diff --git a/bp2build/Android.bp b/bp2build/Android.bp index 6570b163c..ddde1b7fc 100644 --- a/bp2build/Android.bp +++ b/bp2build/Android.bp @@ -15,12 +15,14 @@ bootstrap_go_package { deps: [ "soong-android", "soong-bazel", + "soong-cc", "soong-genrule", "soong-sh", ], testSrcs: [ "build_conversion_test.go", "bzl_conversion_test.go", + "cc_conversion_test.go", "conversion_test.go", "sh_conversion_test.go", "testing.go", diff --git a/bp2build/cc_conversion_test.go b/bp2build/cc_conversion_test.go new file mode 100644 index 000000000..3cd37628f --- /dev/null +++ b/bp2build/cc_conversion_test.go @@ -0,0 +1,221 @@ +// 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" + "strings" + "testing" +) + +const ( + // See cc/testing.go for more context + soongCcLibraryPreamble = ` +cc_defaults { + name: "linux_bionic_supported", +} + +toolchain_library { + name: "libclang_rt.builtins-x86_64-android", + defaults: ["linux_bionic_supported"], + vendor_available: true, + vendor_ramdisk_available: true, + product_available: true, + recovery_available: true, + native_bridge_supported: true, + src: "", +} + +toolchain_library { + name: "libatomic", + defaults: ["linux_bionic_supported"], + vendor_available: true, + vendor_ramdisk_available: true, + product_available: true, + recovery_available: true, + native_bridge_supported: true, + src: "", +}` +) + +func TestCcLibraryHeadersLoadStatement(t *testing.T) { + testCases := []struct { + bazelTargets BazelTargets + expectedLoadStatements string + }{ + { + bazelTargets: BazelTargets{ + BazelTarget{ + name: "cc_library_headers_target", + ruleClass: "cc_library_headers", + // Note: no bzlLoadLocation for native rules + }, + }, + expectedLoadStatements: ``, + }, + } + + for _, testCase := range testCases { + actual := testCase.bazelTargets.LoadStatements() + expected := testCase.expectedLoadStatements + if actual != expected { + t.Fatalf("Expected load statements to be %s, got %s", expected, actual) + } + } + +} + +func TestCcLibraryHeadersBp2Build(t *testing.T) { + testCases := []struct { + description string + moduleTypeUnderTest string + moduleTypeUnderTestFactory android.ModuleFactory + moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext) + preArchMutators []android.RegisterMutatorFunc + depsMutators []android.RegisterMutatorFunc + bp string + expectedBazelTargets []string + filesystem map[string]string + dir string + }{ + { + description: "cc_library_headers test", + moduleTypeUnderTest: "cc_library_headers", + moduleTypeUnderTestFactory: cc.LibraryHeaderFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build, + filesystem: map[string]string{ + "lib-1/lib1a.h": "", + "lib-1/lib1b.h": "", + "lib-2/lib2a.h": "", + "lib-2/lib2b.h": "", + "dir-1/dir1a.h": "", + "dir-1/dir1b.h": "", + "dir-2/dir2a.h": "", + "dir-2/dir2b.h": "", + }, + bp: soongCcLibraryPreamble + ` +cc_library_headers { + name: "lib-1", + export_include_dirs: ["lib-1"], + bazel_module: { bp2build_available: true }, +} + +cc_library_headers { + name: "lib-2", + export_include_dirs: ["lib-2"], + bazel_module: { bp2build_available: true }, +} + +cc_library_headers { + name: "foo_headers", + export_include_dirs: ["dir-1", "dir-2"], + header_libs: ["lib-1", "lib-2"], + export_header_lib_headers: ["lib-1", "lib-2"], + bazel_module: { bp2build_available: true }, +}`, + expectedBazelTargets: []string{`cc_library_headers( + name = "foo_headers", + deps = [ + ":lib-1", + ":lib-2", + ], + hdrs = [ + "dir-1/dir1a.h", + "dir-1/dir1b.h", + "dir-2/dir2a.h", + "dir-2/dir2b.h", + ], + includes = [ + "dir-1", + "dir-2", + ], +)`, `cc_library_headers( + name = "lib-1", + hdrs = [ + "lib-1/lib1a.h", + "lib-1/lib1b.h", + ], + includes = [ + "lib-1", + ], +)`, `cc_library_headers( + name = "lib-2", + hdrs = [ + "lib-2/lib2a.h", + "lib-2/lib2b.h", + ], + includes = [ + "lib-2", + ], +)`}, + }, + } + + 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.bp, filesystem) + ctx := android.NewTestContext(config) + + cc.RegisterCCBuildComponents(ctx) + ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory) + + ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory) + for _, m := range testCase.depsMutators { + ctx.DepsBp2BuildMutators(m) + } + 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 + } + + checkDir := dir + if testCase.dir != "" { + checkDir = testCase.dir + } + bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[checkDir] + if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount { + 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/library.go b/cc/library.go index 65533bc2b..bdcb8ae04 100644 --- a/cc/library.go +++ b/cc/library.go @@ -27,6 +27,7 @@ import ( "github.com/google/blueprint/pathtools" "android/soong/android" + "android/soong/bazel" "android/soong/cc/config" ) @@ -120,6 +121,9 @@ type LibraryProperties struct { // 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 8b3dbeb8b..448e1444d 100644 --- a/cc/library_headers.go +++ b/cc/library_headers.go @@ -14,13 +14,18 @@ package cc -import "android/soong/android" +import ( + "android/soong/android" + "android/soong/bazel" +) func init() { RegisterLibraryHeadersBuildComponents(android.InitRegistrationContext) // Register sdk member types. android.RegisterSdkMemberType(headersLibrarySdkMemberType) + + android.RegisterBp2BuildMutator("cc_library_headers", CcLibraryHeadersBp2Build) } var headersLibrarySdkMemberType = &librarySdkMemberType{ @@ -55,3 +60,86 @@ func prebuiltLibraryHeaderFactory() android.Module { library.HeaderOnly() return module.Init() } + +type bazelCcLibraryHeadersAttributes struct { + Hdrs bazel.LabelList + Includes bazel.LabelList + Deps bazel.LabelList +} + +type bazelCcLibraryHeaders struct { + android.BazelTargetModuleBase + bazelCcLibraryHeadersAttributes +} + +func BazelCcLibraryHeadersFactory() android.Module { + module := &bazelCcLibraryHeaders{} + module.AddProperties(&module.bazelCcLibraryHeadersAttributes) + android.InitBazelTargetModule(module) + return module +} + +func CcLibraryHeadersBp2Build(ctx android.TopDownMutatorContext) { + module, ok := ctx.Module().(*Module) + if !ok { + // Not a cc module + return + } + + lib, ok := module.linker.(*libraryDecorator) + if !ok { + // Not a cc_library module + return + } + if !lib.header() { + // Not a cc_library_headers module + 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 + includeDirs = append(includeDirs, lib.flagExporter.Properties.Export_include_dirs...) + includeDirLabels := android.BazelLabelForModuleSrc(ctx, includeDirs) + + var includeDirGlobs []string + for _, includeDir := range includeDirs { + includeDirGlobs = append(includeDirGlobs, includeDir+"/**/*.h") + } + + headerLabels := android.BazelLabelForModuleSrc(ctx, includeDirGlobs) + + // list of modules that should only provide headers for this module. + var headerLibs []string + for _, linkerProps := range lib.linkerProps() { + if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok { + headerLibs = baseLinkerProps.Export_header_lib_headers + break + } + } + headerLibLabels := android.BazelLabelForModuleDeps(ctx, headerLibs) + + attrs := &bazelCcLibraryHeadersAttributes{ + Includes: includeDirLabels, + Hdrs: headerLabels, + Deps: headerLibLabels, + } + + props := bazel.NewBazelTargetModuleProperties( + module.Name(), + "cc_library_headers", + "//build/bazel/rules:cc_library_headers.bzl", + ) + + ctx.CreateBazelTargetModule(BazelCcLibraryHeadersFactory, props, attrs) +} + +func (m *bazelCcLibraryHeaders) Name() string { + return m.BaseModuleName() +} + +func (m *bazelCcLibraryHeaders) GenerateAndroidBuildActions(ctx android.ModuleContext) {}