bp2build: cc_object converter.

This CL introduces a basic bp2build converter for cc_object modules.
cc_objects maps cleanly to cc_library targets, but with -fnoaddrsig.

This CL also demonstrates generating include deps within a macro to
allow the cc_object compilation to depend on a relative-include header
within an include dir.

e.g. if "foo.cc" includes "android/log.h" and the latter is located at
"include/android/log.h", the autogenerated header deps would export
"android/log.h" correctly to the foo.cc upstream target.

Test: GENERATE_BAZEL_FILES=true m nothing && bp2build-sync write && bazel build //bionic/libc:crtbegin_so1

Change-Id: Ifd9e097051ec184ab0a1929d07918f0ff4f24d98
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index ddde1b7..521bb06 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -22,7 +22,8 @@
     testSrcs: [
         "build_conversion_test.go",
         "bzl_conversion_test.go",
-        "cc_conversion_test.go",
+        "cc_library_headers_conversion_test.go",
+        "cc_object_conversion_test.go",
         "conversion_test.go",
         "sh_conversion_test.go",
         "testing.go",
diff --git a/bp2build/cc_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
similarity index 100%
rename from bp2build/cc_conversion_test.go
rename to bp2build/cc_library_headers_conversion_test.go
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
new file mode 100644
index 0000000..e4ffe16
--- /dev/null
+++ b/bp2build/cc_object_conversion_test.go
@@ -0,0 +1,186 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+	"android/soong/android"
+	"android/soong/cc"
+	"fmt"
+	"strings"
+	"testing"
+)
+
+func TestCcObjectBp2Build(t *testing.T) {
+	testCases := []struct {
+		description                        string
+		moduleTypeUnderTest                string
+		moduleTypeUnderTestFactory         android.ModuleFactory
+		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+		blueprint                          string
+		expectedBazelTargets               []string
+		filesystem                         map[string]string
+	}{
+		{
+			description:                        "simple cc_object generates cc_object with include header dep",
+			moduleTypeUnderTest:                "cc_object",
+			moduleTypeUnderTestFactory:         cc.ObjectFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+			filesystem: map[string]string{
+				"a/b/foo.h": "",
+				"a/b/bar.h": "",
+				"a/b/c.c":   "",
+			},
+			blueprint: `cc_object {
+    name: "foo",
+    local_include_dirs: ["include"],
+    cflags: [
+        "-Wno-gcc-compat",
+        "-Wall",
+        "-Werror",
+    ],
+    srcs: [
+        "a/b/*.h",
+        "a/b/c.c"
+    ],
+
+    bazel_module: { bp2build_available: true },
+}
+`,
+			expectedBazelTargets: []string{`cc_object(
+    name = "foo",
+    copts = [
+        "-fno-addrsig",
+        "-Wno-gcc-compat",
+        "-Wall",
+        "-Werror",
+    ],
+    local_include_dirs = [
+        "include",
+    ],
+    srcs = [
+        "a/b/bar.h",
+        "a/b/foo.h",
+        "a/b/c.c",
+    ],
+)`,
+			},
+		},
+		{
+			description:                        "simple cc_object with defaults",
+			moduleTypeUnderTest:                "cc_object",
+			moduleTypeUnderTestFactory:         cc.ObjectFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+			blueprint: `cc_object {
+    name: "foo",
+    local_include_dirs: ["include"],
+    srcs: [
+        "a/b/*.h",
+        "a/b/c.c"
+    ],
+
+    defaults: ["foo_defaults"],
+    bazel_module: { bp2build_available: true },
+}
+
+cc_defaults {
+    name: "foo_defaults",
+    defaults: ["foo_bar_defaults"],
+	// TODO(b/178130668): handle configurable attributes that depend on the platform
+    arch: {
+        x86: {
+            cflags: ["-fPIC"],
+        },
+        x86_64: {
+            cflags: ["-fPIC"],
+        },
+    },
+}
+
+cc_defaults {
+    name: "foo_bar_defaults",
+    cflags: [
+        "-Wno-gcc-compat",
+        "-Wall",
+        "-Werror",
+    ],
+}
+`,
+			expectedBazelTargets: []string{`cc_object(
+    name = "foo",
+    copts = [
+        "-Wno-gcc-compat",
+        "-Wall",
+        "-Werror",
+        "-fno-addrsig",
+    ],
+    local_include_dirs = [
+        "include",
+    ],
+    srcs = [
+        "a/b/c.c",
+    ],
+)`,
+			},
+		},
+	}
+
+	dir := "."
+	for _, testCase := range testCases {
+		filesystem := make(map[string][]byte)
+		toParse := []string{
+			"Android.bp",
+		}
+		for f, content := range testCase.filesystem {
+			if strings.HasSuffix(f, "Android.bp") {
+				toParse = append(toParse, f)
+			}
+			filesystem[f] = []byte(content)
+		}
+		config := android.TestConfig(buildDir, nil, testCase.blueprint, filesystem)
+		ctx := android.NewTestContext(config)
+		// Always register cc_defaults module factory
+		ctx.RegisterModuleType("cc_defaults", func() android.Module { return cc.DefaultsFactory() })
+
+		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+		ctx.RegisterForBazelConversion()
+
+		_, errs := ctx.ParseFileList(dir, toParse)
+		if Errored(t, testCase.description, errs) {
+			continue
+		}
+		_, errs = ctx.ResolveDependencies(config)
+		if Errored(t, testCase.description, errs) {
+			continue
+		}
+
+		bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[dir]
+		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+			fmt.Println(bazelTargets)
+			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
+		} else {
+			for i, target := range bazelTargets {
+				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+					t.Errorf(
+						"%s: Expected generated Bazel target to be '%s', got '%s'",
+						testCase.description,
+						w,
+						g,
+					)
+				}
+			}
+		}
+	}
+}
diff --git a/cc/cc.go b/cc/cc.go
index 7f59158..6c1469f 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -28,6 +28,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel"
 	"android/soong/cc/config"
 	"android/soong/genrule"
 )
@@ -364,6 +365,8 @@
 	// can depend on libraries that are not exported by the APEXes and use private symbols
 	// from the exported libraries.
 	Test_for []string
+
+	bazel.Properties
 }
 
 type VendorProperties struct {
diff --git a/cc/library.go b/cc/library.go
index bdcb8ae..65533bc 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -27,7 +27,6 @@
 	"github.com/google/blueprint/pathtools"
 
 	"android/soong/android"
-	"android/soong/bazel"
 	"android/soong/cc/config"
 )
 
@@ -121,9 +120,6 @@
 	// If this is an LLNDK library, properties to describe the LLNDK stubs.  Will be copied from
 	// the module pointed to by llndk_stubs if it is set.
 	Llndk llndkLibraryProperties
-
-	// Properties for Bazel migration purposes.
-	bazel.Properties
 }
 
 // StaticProperties is a properties stanza to affect only attributes of the "static" variants of a
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 448e144..e5a5557 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -86,6 +86,10 @@
 		return
 	}
 
+	if !module.Properties.Bazel_module.Bp2build_available {
+		return
+	}
+
 	lib, ok := module.linker.(*libraryDecorator)
 	if !ok {
 		// Not a cc_library module
@@ -96,10 +100,6 @@
 		return
 	}
 
-	if !lib.Properties.Bazel_module.Bp2build_available {
-		return
-	}
-
 	// list of directories that will be added to the include path (using -I) for this
 	// module and any module that links against this module.
 	includeDirs := lib.flagExporter.Properties.Export_system_include_dirs
diff --git a/cc/object.go b/cc/object.go
index 3ce7676..d92e110 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -18,6 +18,7 @@
 	"fmt"
 
 	"android/soong/android"
+	"android/soong/bazel"
 )
 
 //
@@ -27,6 +28,8 @@
 func init() {
 	android.RegisterModuleType("cc_object", ObjectFactory)
 	android.RegisterSdkMemberType(ccObjectSdkMemberType)
+
+	android.RegisterBp2BuildMutator("cc_object", ObjectBp2Build)
 }
 
 var ccObjectSdkMemberType = &librarySdkMemberType{
@@ -82,9 +85,80 @@
 	module.compiler.appendCflags([]string{"-fno-addrsig"})
 
 	module.sdkMemberTypes = []android.SdkMemberType{ccObjectSdkMemberType}
+
 	return module.Init()
 }
 
+// For bp2build conversion.
+type bazelObjectAttributes struct {
+	Srcs               bazel.LabelList
+	Copts              []string
+	Local_include_dirs []string
+}
+
+type bazelObject struct {
+	android.BazelTargetModuleBase
+	bazelObjectAttributes
+}
+
+func (m *bazelObject) Name() string {
+	return m.BaseModuleName()
+}
+
+func (m *bazelObject) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+
+func BazelObjectFactory() android.Module {
+	module := &bazelObject{}
+	module.AddProperties(&module.bazelObjectAttributes)
+	android.InitBazelTargetModule(module)
+	return module
+}
+
+// ObjectBp2Build is the bp2build converter from cc_object modules to the
+// Bazel equivalent target, plus any necessary include deps for the cc_object.
+func ObjectBp2Build(ctx android.TopDownMutatorContext) {
+	m, ok := ctx.Module().(*Module)
+	if !ok || !m.Properties.Bazel_module.Bp2build_available {
+		return
+	}
+
+	// a Module can be something other than a cc_object.
+	if ctx.ModuleType() != "cc_object" {
+		return
+	}
+
+	if m.compiler == nil {
+		// a cc_object must have access to the compiler decorator for its props.
+		ctx.ModuleErrorf("compiler must not be nil for a cc_object module")
+	}
+
+	var copts []string
+	var srcs []string
+	var localIncludeDirs []string
+	for _, props := range m.compiler.compilerProps() {
+		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
+			copts = baseCompilerProps.Cflags
+			srcs = baseCompilerProps.Srcs
+			localIncludeDirs = baseCompilerProps.Local_include_dirs
+			break
+		}
+	}
+
+	attrs := &bazelObjectAttributes{
+		Srcs:               android.BazelLabelForModuleSrc(ctx, srcs),
+		Copts:              copts,
+		Local_include_dirs: localIncludeDirs,
+	}
+
+	props := bazel.NewBazelTargetModuleProperties(
+		m.Name(),
+		"cc_object",
+		"//build/bazel/rules:cc_object.bzl",
+	)
+
+	ctx.CreateBazelTargetModule(BazelObjectFactory, props, attrs)
+}
+
 func (object *objectLinker) appendLdflags(flags []string) {
 	panic(fmt.Errorf("appendLdflags on objectLinker not supported"))
 }