diff --git a/bazel/properties.go b/bazel/properties.go index 7093d6cdf..0dd47da73 100644 --- a/bazel/properties.go +++ b/bazel/properties.go @@ -19,6 +19,7 @@ import ( "path/filepath" "regexp" "sort" + "strings" ) // BazelTargetModuleProperties contain properties and metadata used for @@ -33,6 +34,10 @@ type BazelTargetModuleProperties struct { const BazelTargetModuleNamePrefix = "__bp2build__" +func StripNamePrefix(moduleName string) string { + return strings.TrimPrefix(moduleName, BazelTargetModuleNamePrefix) +} + var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)") // Label is used to represent a Bazel compatible Label. Also stores the original diff --git a/bp2build/Android.bp b/bp2build/Android.bp index 3abbc4c88..0e6030e5b 100644 --- a/bp2build/Android.bp +++ b/bp2build/Android.bp @@ -11,6 +11,7 @@ bootstrap_go_package { "build_conversion.go", "bzl_conversion.go", "configurability.go", + "compatibility.go", "constants.go", "conversion.go", "metrics.go", diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go index ee36982c7..06a7306c0 100644 --- a/bp2build/bp2build.go +++ b/bp2build/bp2build.go @@ -29,12 +29,12 @@ func Codegen(ctx *CodegenContext) CodegenMetrics { bp2buildDir := android.PathForOutput(ctx, "bp2build") android.RemoveAllOutputDir(bp2buildDir) - buildToTargets, metrics := GenerateBazelTargets(ctx, true) + buildToTargets, metrics, compatLayer := GenerateBazelTargets(ctx, true) bp2buildFiles := CreateBazelFiles(nil, buildToTargets, ctx.mode) writeFiles(ctx, bp2buildDir, bp2buildFiles) soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName) - writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles()) + writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(compatLayer)) return metrics } diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go index a1e04248e..96a8b0973 100644 --- a/bp2build/build_conversion.go +++ b/bp2build/build_conversion.go @@ -32,6 +32,7 @@ type BazelAttributes struct { type BazelTarget struct { name string + packageName string content string ruleClass string bzlLoadLocation string @@ -44,6 +45,16 @@ func (t BazelTarget) IsLoadedFromStarlark() bool { return t.bzlLoadLocation != "" } +// Label is the fully qualified Bazel label constructed from the BazelTarget's +// package name and target name. +func (t BazelTarget) Label() string { + if t.packageName == "." { + return "//:" + t.name + } else { + return "//" + t.packageName + ":" + t.name + } +} + // BazelTargets is a typedef for a slice of BazelTarget objects. type BazelTargets []BazelTarget @@ -213,7 +224,7 @@ func propsToAttributes(props map[string]string) string { return attributes } -func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (map[string]BazelTargets, CodegenMetrics) { +func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (map[string]BazelTargets, CodegenMetrics, CodegenCompatLayer) { buildFileToTargets := make(map[string]BazelTargets) buildFileToAppend := make(map[string]bool) @@ -222,6 +233,10 @@ func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (map[str RuleClassCount: make(map[string]int), } + compatLayer := CodegenCompatLayer{ + NameToLabelMap: make(map[string]string), + } + dirs := make(map[string]bool) bpCtx := ctx.Context() @@ -236,6 +251,7 @@ func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (map[str if b, ok := m.(android.Bazelable); ok && b.HasHandcraftedLabel() { metrics.handCraftedTargetCount += 1 metrics.TotalModuleCount += 1 + compatLayer.AddNameToLabelEntry(m.Name(), b.HandcraftedLabel()) pathToBuildFile := getBazelPackagePath(b) // We are using the entire contents of handcrafted build file, so if multiple targets within // a package have handcrafted targets, we only want to include the contents one time. @@ -253,6 +269,7 @@ func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (map[str } else if btm, ok := m.(android.BazelTargetModule); ok { t = generateBazelTarget(bpCtx, m, btm) metrics.RuleClassCount[t.ruleClass] += 1 + compatLayer.AddNameToLabelEntry(m.Name(), t.Label()) } else { metrics.TotalModuleCount += 1 return @@ -283,7 +300,7 @@ func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (map[str } } - return buildFileToTargets, metrics + return buildFileToTargets, metrics, compatLayer } func getBazelPackagePath(b android.Bazelable) string { @@ -324,6 +341,7 @@ func generateBazelTarget(ctx bpToBuildContext, m blueprint.Module, btm android.B targetName := targetNameForBp2Build(ctx, m) return BazelTarget{ name: targetName, + packageName: ctx.ModuleDir(m), ruleClass: ruleClass, bzlLoadLocation: bzlLoadLocation, content: fmt.Sprintf( diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go index 33609af03..0e52f2aa9 100644 --- a/bp2build/build_conversion_test.go +++ b/bp2build/build_conversion_test.go @@ -1398,14 +1398,14 @@ func TestCombineBuildFilesBp2buildTargets(t *testing.T) { moduleTypeUnderTestFactory: android.FileGroupFactory, moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build, bp: `filegroup { - name: "fg_foo", - bazel_module: { label: "//other:fg_foo" }, -} + name: "fg_foo", + bazel_module: { label: "//other:fg_foo" }, + } -filegroup { - name: "foo", - bazel_module: { label: "//other:foo" }, -}`, + filegroup { + name: "foo", + bazel_module: { label: "//other:foo" }, + }`, expectedBazelTargets: []string{ `// BUILD file`, }, @@ -1414,25 +1414,31 @@ filegroup { }, }, { - description: "filegroup bazel_module.label and bp2build", + description: "filegroup bazel_module.label and bp2build in subdir", moduleTypeUnderTest: "filegroup", moduleTypeUnderTestFactory: android.FileGroupFactory, moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build, - bp: `filegroup { - name: "fg_foo", - bazel_module: { - label: "//other:fg_foo", - bp2build_available: true, - }, -}`, + dir: "other", + bp: ``, + fs: map[string]string{ + "other/Android.bp": `filegroup { + name: "fg_foo", + bazel_module: { + bp2build_available: true, + }, + } + filegroup { + name: "fg_bar", + bazel_module: { + label: "//other:fg_bar" + }, + }`, + "other/BUILD.bazel": `// definition for fg_bar`, + }, expectedBazelTargets: []string{ `filegroup( name = "fg_foo", -)`, - `// BUILD file`, - }, - fs: map[string]string{ - "other/BUILD.bazel": `// BUILD file`, +)`, `// definition for fg_bar`, }, }, { @@ -1441,18 +1447,18 @@ filegroup { moduleTypeUnderTestFactory: android.FileGroupFactory, moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build, bp: `filegroup { - name: "fg_foo", - bazel_module: { - label: "//other:fg_foo", - }, -} + name: "fg_foo", + bazel_module: { + label: "//other:fg_foo", + }, + } -filegroup { - name: "fg_bar", - bazel_module: { - bp2build_available: true, - }, -}`, + filegroup { + name: "fg_bar", + bazel_module: { + bp2build_available: true, + }, + }`, expectedBazelTargets: []string{ `filegroup( name = "fg_bar", diff --git a/bp2build/compatibility.go b/bp2build/compatibility.go new file mode 100644 index 000000000..5baa5249b --- /dev/null +++ b/bp2build/compatibility.go @@ -0,0 +1,31 @@ +package bp2build + +import ( + "android/soong/bazel" + "fmt" +) + +// Data from the code generation process that is used to improve compatibility +// between build systems. +type CodegenCompatLayer struct { + // A map from the original module name to the generated/handcrafted Bazel + // label for legacy build systems to be able to build a fully-qualified + // Bazel target from an unique module name. + NameToLabelMap map[string]string +} + +// Log an entry of module name -> Bazel target label. +func (compatLayer CodegenCompatLayer) AddNameToLabelEntry(name, label string) { + // The module name may be prefixed with bazel.BazelTargetModuleNamePrefix if + // generated from bp2build. + name = bazel.StripNamePrefix(name) + if existingLabel, ok := compatLayer.NameToLabelMap[name]; ok { + panic(fmt.Errorf( + "Module '%s' maps to more than one Bazel target label: %s, %s. "+ + "This shouldn't happen. It probably indicates a bug with the bp2build internals.", + name, + existingLabel, + label)) + } + compatLayer.NameToLabelMap[name] = label +} diff --git a/bp2build/conversion.go b/bp2build/conversion.go index bced4c19f..75bc2b415 100644 --- a/bp2build/conversion.go +++ b/bp2build/conversion.go @@ -16,15 +16,31 @@ type BazelFile struct { Contents string } -func CreateSoongInjectionFiles() []BazelFile { +func CreateSoongInjectionFiles(compatLayer CodegenCompatLayer) []BazelFile { var files []BazelFile - files = append(files, newFile("cc_toolchain", "BUILD", "")) // Creates a //cc_toolchain package. + files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package. files = append(files, newFile("cc_toolchain", "constants.bzl", config.BazelCcToolchainVars())) + files = append(files, newFile("module_name_to_label", GeneratedBuildFileName, nameToLabelAliases(compatLayer.NameToLabelMap))) + return files } +func nameToLabelAliases(nameToLabelMap map[string]string) string { + ret := make([]string, len(nameToLabelMap)) + + for k, v := range nameToLabelMap { + // v is the fully qualified label rooted at '//' + ret = append(ret, fmt.Sprintf( + `alias( + name = "%s", + actual = "@%s", +)`, k, v)) + } + return strings.Join(ret, "\n\n") +} + func CreateBazelFiles( ruleShims map[string]RuleShim, buildToTargets map[string]BazelTargets, diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go index 0931ff7fc..56ea589bb 100644 --- a/bp2build/conversion_test.go +++ b/bp2build/conversion_test.go @@ -80,17 +80,21 @@ func TestCreateBazelFiles_QueryView_AddsTopLevelFiles(t *testing.T) { } func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) { - files := CreateSoongInjectionFiles() + files := CreateSoongInjectionFiles(CodegenCompatLayer{}) expectedFilePaths := []bazelFilepath{ { dir: "cc_toolchain", - basename: "BUILD", + basename: GeneratedBuildFileName, }, { dir: "cc_toolchain", basename: "constants.bzl", }, + { + dir: "module_name_to_label", + basename: GeneratedBuildFileName, + }, } if len(files) != len(expectedFilePaths) { @@ -104,7 +108,7 @@ func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) { t.Errorf("Did not find expected file %s/%s", actualFile.Dir, actualFile.Basename) } - if expectedFile.basename != "BUILD" && actualFile.Contents == "" { + if expectedFile.basename != GeneratedBuildFileName && actualFile.Contents == "" { t.Errorf("Contents of %s unexpected empty.", actualFile) } } diff --git a/bp2build/testing.go b/bp2build/testing.go index 861f7d25f..f3cd7f07f 100644 --- a/bp2build/testing.go +++ b/bp2build/testing.go @@ -213,6 +213,6 @@ func customBp2BuildMutatorFromStarlark(ctx android.TopDownMutatorContext) { // Helper method for tests to easily access the targets in a dir. func generateBazelTargetsForDir(codegenCtx *CodegenContext, dir string) BazelTargets { // TODO: Set generateFilegroups to true and/or remove the generateFilegroups argument completely - buildFileToTargets, _ := GenerateBazelTargets(codegenCtx, false) + buildFileToTargets, _, _ := GenerateBazelTargets(codegenCtx, false) return buildFileToTargets[dir] } diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go index e2ce77202..a8602deb7 100644 --- a/cmd/soong_build/queryview.go +++ b/cmd/soong_build/queryview.go @@ -25,9 +25,10 @@ import ( func createBazelQueryView(ctx *bp2build.CodegenContext, bazelQueryViewDir string) error { ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories()) - // Ignore metrics reporting for queryview, since queryview is already a full-repo - // conversion and can use data from bazel query directly. - buildToTargets, _ := bp2build.GenerateBazelTargets(ctx, true) + // Ignore metrics reporting and compat layers for queryview, since queryview + // is already a full-repo conversion and can use data from bazel query + // directly. + buildToTargets, _, _ := bp2build.GenerateBazelTargets(ctx, true) filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets, bp2build.QueryView) for _, f := range filesToWrite {