diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go index 08bad50d6..c685f3356 100644 --- a/android/allowlists/allowlists.go +++ b/android/allowlists/allowlists.go @@ -157,6 +157,7 @@ var ( "frameworks/av/media/liberror": Bp2BuildDefaultTrueRecursively, "frameworks/av/services/minijail": Bp2BuildDefaultTrueRecursively, "frameworks/base/media/tests/MediaDump": Bp2BuildDefaultTrue, + "frameworks/base/services/tests/servicestests/aidl": Bp2BuildDefaultTrue, "frameworks/base/startop/apps/test": Bp2BuildDefaultTrue, "frameworks/base/tests/appwidgets/AppWidgetHostTest": Bp2BuildDefaultTrueRecursively, "frameworks/native/libs/adbd_auth": Bp2BuildDefaultTrueRecursively, @@ -408,6 +409,7 @@ var ( "linker_config", "java_import", "java_import_host", + "aidl_interface_headers", } Bp2buildModuleDoNotConvertList = []string{ diff --git a/android/filegroup.go b/android/filegroup.go index 7d3923814..e609f6362 100644 --- a/android/filegroup.go +++ b/android/filegroup.go @@ -16,6 +16,7 @@ package android import ( "path/filepath" + "regexp" "strings" "android/soong/bazel" @@ -37,6 +38,34 @@ func IsFilegroup(ctx bazel.OtherModuleContext, m blueprint.Module) bool { return ctx.OtherModuleType(m) == "filegroup" } +var ( + // ignoring case, checks for proto or protos as an independent word in the name, whether at the + // beginning, end, or middle. e.g. "proto.foo", "bar-protos", "baz_proto_srcs" would all match + filegroupLikelyProtoPattern = regexp.MustCompile("(?i)(^|[^a-z])proto(s)?([^a-z]|$)") + filegroupLikelyAidlPattern = regexp.MustCompile("(?i)(^|[^a-z])aidl([^a-z]|$)") + + ProtoSrcLabelPartition = bazel.LabelPartition{ + Extensions: []string{".proto"}, + LabelMapper: isFilegroupWithPattern(filegroupLikelyProtoPattern), + } + AidlSrcLabelPartition = bazel.LabelPartition{ + Extensions: []string{".aidl"}, + LabelMapper: isFilegroupWithPattern(filegroupLikelyAidlPattern), + } +) + +func isFilegroupWithPattern(pattern *regexp.Regexp) bazel.LabelMapper { + return func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) { + m, exists := ctx.ModuleFromName(label.OriginalModuleName) + labelStr := label.Label + if !exists || !IsFilegroup(ctx, m) { + return labelStr, false + } + likelyMatched := pattern.MatchString(label.OriginalModuleName) + return labelStr, likelyMatched + } +} + // https://docs.bazel.build/versions/master/be/general.html#filegroup type bazelFilegroupAttributes struct { Srcs bazel.LabelListAttribute @@ -232,3 +261,16 @@ func (fg *fileGroup) GetAidlLibraryLabel(ctx BazelConversionPathContext) string return fg.GetBazelLabel(ctx, fg) } } + +// Given a name in srcs prop, check to see if the name references a filegroup +// and the filegroup is converted to aidl_library +func IsConvertedToAidlLibrary(ctx BazelConversionPathContext, name string) bool { + if module, ok := ctx.ModuleFromName(name); ok { + if IsFilegroup(ctx, module) { + if fg, ok := module.(Bp2buildAidlLibrary); ok { + return fg.ShouldConvertToAidlLibrary(ctx) + } + } + } + return false +} diff --git a/android/fixture.go b/android/fixture.go index f71893571..f33e7189d 100644 --- a/android/fixture.go +++ b/android/fixture.go @@ -642,6 +642,20 @@ type TestResult struct { NinjaDeps []string } +type TestPathContext struct { + *TestResult +} + +var _ PathContext = &TestPathContext{} + +func (t *TestPathContext) Config() Config { + return t.TestResult.Config +} + +func (t *TestPathContext) AddNinjaFileDeps(deps ...string) { + panic("unimplemented") +} + func createFixture(t *testing.T, buildDir string, preparers []*simpleFixturePreparer) Fixture { config := TestConfig(buildDir, nil, "", nil) ctx := NewTestContext(config) diff --git a/android/paths.go b/android/paths.go index 74d9f13f4..1f6912589 100644 --- a/android/paths.go +++ b/android/paths.go @@ -1978,6 +1978,18 @@ func PathForTesting(paths ...string) Path { return testPath{basePath{path: p, rel: p}} } +func PathForTestingWithRel(path, rel string) Path { + p, err := validateSafePath(path, rel) + if err != nil { + panic(err) + } + r, err := validatePath(rel) + if err != nil { + panic(err) + } + return testPath{basePath{path: p, rel: r}} +} + // PathsForTesting returns a Path constructed from each element in strs. It should only be used from within tests. func PathsForTesting(strs ...string) Paths { p := make(Paths, len(strs)) diff --git a/android/proto.go b/android/proto.go index c3759f8ad..8ad16a6a3 100644 --- a/android/proto.go +++ b/android/proto.go @@ -16,7 +16,6 @@ package android import ( "android/soong/bazel" - "regexp" "strings" "github.com/google/blueprint" @@ -27,14 +26,6 @@ const ( canonicalPathFromRootDefault = true ) -var ( - // ignoring case, checks for proto or protos as an independent word in the name, whether at the - // beginning, end, or middle. e.g. "proto.foo", "bar-protos", "baz_proto_srcs" would all match - filegroupLikelyProtoPattern = regexp.MustCompile("(?i)(^|[^a-z])proto(s)?([^a-z]|$)") - - ProtoSrcLabelPartition = bazel.LabelPartition{Extensions: []string{".proto"}, LabelMapper: isProtoFilegroup} -) - // TODO(ccross): protos are often used to communicate between multiple modules. If the only // way to convert a proto to source is to reference it as a source file, and external modules cannot // reference source files in other modules, then every module that owns a proto file will need to @@ -213,13 +204,3 @@ func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, m *ModuleBase, srcs baz return info, true } - -func isProtoFilegroup(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) { - m, exists := ctx.ModuleFromName(label.OriginalModuleName) - labelStr := label.Label - if !exists || !IsFilegroup(ctx, m) { - return labelStr, false - } - likelyProtos := filegroupLikelyProtoPattern.MatchString(label.OriginalModuleName) - return labelStr, likelyProtos -} diff --git a/bazel/properties.go b/bazel/properties.go index 7fed0e602..13e36b582 100644 --- a/bazel/properties.go +++ b/bazel/properties.go @@ -119,7 +119,7 @@ func (ll *LabelList) uniqueParentDirectories() []string { return dirs } -// Add inserts the label Label at the end of the LabelList. +// Add inserts the label Label at the end of the LabelList.Includes. func (ll *LabelList) Add(label *Label) { if label == nil { return @@ -127,6 +127,14 @@ func (ll *LabelList) Add(label *Label) { ll.Includes = append(ll.Includes, *label) } +// AddExclude inserts the label Label at the end of the LabelList.Excludes. +func (ll *LabelList) AddExclude(label *Label) { + if label == nil { + return + } + ll.Excludes = append(ll.Excludes, *label) +} + // Append appends the fields of other labelList to the corresponding fields of ll. func (ll *LabelList) Append(other LabelList) { if len(ll.Includes) > 0 || len(other.Includes) > 0 { @@ -137,6 +145,30 @@ func (ll *LabelList) Append(other LabelList) { } } +// Partition splits a LabelList into two LabelLists depending on the return value +// of the predicate. +// This function preserves the Includes and Excludes, but it does not provide +// that information to the partition function. +func (ll *LabelList) Partition(predicate func(label Label) bool) (LabelList, LabelList) { + predicated := LabelList{} + unpredicated := LabelList{} + for _, include := range ll.Includes { + if predicate(include) { + predicated.Add(&include) + } else { + unpredicated.Add(&include) + } + } + for _, exclude := range ll.Excludes { + if predicate(exclude) { + predicated.AddExclude(&exclude) + } else { + unpredicated.AddExclude(&exclude) + } + } + return predicated, unpredicated +} + // UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns // the slice in a sorted order. func UniqueSortedBazelLabels(originalLabels []Label) []Label { @@ -822,6 +854,29 @@ func (lla *LabelListAttribute) ResolveExcludes() { } } +// Partition splits a LabelListAttribute into two LabelListAttributes depending +// on the return value of the predicate. +// This function preserves the Includes and Excludes, but it does not provide +// that information to the partition function. +func (lla LabelListAttribute) Partition(predicate func(label Label) bool) (LabelListAttribute, LabelListAttribute) { + predicated := LabelListAttribute{} + unpredicated := LabelListAttribute{} + + valuePartitionTrue, valuePartitionFalse := lla.Value.Partition(predicate) + predicated.SetValue(valuePartitionTrue) + unpredicated.SetValue(valuePartitionFalse) + + for axis, selectValueLabelLists := range lla.ConfigurableValues { + for config, labelList := range selectValueLabelLists { + configPredicated, configUnpredicated := labelList.Partition(predicate) + predicated.SetSelectValue(axis, config, configPredicated) + unpredicated.SetSelectValue(axis, config, configUnpredicated) + } + } + + return predicated, unpredicated +} + // OtherModuleContext is a limited context that has methods with information about other modules. type OtherModuleContext interface { ModuleFromName(name string) (blueprint.Module, bool) diff --git a/bazel/properties_test.go b/bazel/properties_test.go index 7b76b747b..dc4d5b0f5 100644 --- a/bazel/properties_test.go +++ b/bazel/properties_test.go @@ -310,6 +310,134 @@ func TestResolveExcludes(t *testing.T) { } } +func TestLabelListAttributePartition(t *testing.T) { + testCases := []struct { + name string + input LabelListAttribute + predicated LabelListAttribute + unpredicated LabelListAttribute + predicate func(label Label) bool + }{ + { + name: "move all to predicated partition", + input: MakeLabelListAttribute(makeLabelList( + []string{"keep1", "throw1", "keep2", "throw2"}, + []string{"keep1", "throw1", "keep2", "throw2"}, + )), + predicated: MakeLabelListAttribute(makeLabelList( + []string{"keep1", "throw1", "keep2", "throw2"}, + []string{"keep1", "throw1", "keep2", "throw2"}, + )), + unpredicated: LabelListAttribute{}, + predicate: func(label Label) bool { + return true + }, + }, + { + name: "move all to unpredicated partition", + input: MakeLabelListAttribute(makeLabelList( + []string{"keep1", "throw1", "keep2", "throw2"}, + []string{"keep1", "throw1", "keep2", "throw2"}, + )), + predicated: LabelListAttribute{}, + unpredicated: MakeLabelListAttribute(makeLabelList( + []string{"keep1", "throw1", "keep2", "throw2"}, + []string{"keep1", "throw1", "keep2", "throw2"}, + )), + predicate: func(label Label) bool { + return false + }, + }, + { + name: "partition includes and excludes", + input: MakeLabelListAttribute(makeLabelList( + []string{"keep1", "throw1", "keep2", "throw2"}, + []string{"keep1", "throw1", "keep2", "throw2"}, + )), + predicated: MakeLabelListAttribute(makeLabelList( + []string{"keep1", "keep2"}, + []string{"keep1", "keep2"}, + )), + unpredicated: MakeLabelListAttribute(makeLabelList( + []string{"throw1", "throw2"}, + []string{"throw1", "throw2"}, + )), + predicate: func(label Label) bool { + return strings.HasPrefix(label.Label, "keep") + }, + }, + { + name: "partition excludes only", + input: MakeLabelListAttribute(makeLabelList( + []string{}, + []string{"keep1", "throw1", "keep2", "throw2"}, + )), + predicated: MakeLabelListAttribute(makeLabelList( + []string{}, + []string{"keep1", "keep2"}, + )), + unpredicated: MakeLabelListAttribute(makeLabelList( + []string{}, + []string{"throw1", "throw2"}, + )), + predicate: func(label Label) bool { + return strings.HasPrefix(label.Label, "keep") + }, + }, + { + name: "partition includes only", + input: MakeLabelListAttribute(makeLabelList( + []string{"keep1", "throw1", "keep2", "throw2"}, + []string{}, + )), + predicated: MakeLabelListAttribute(makeLabelList( + []string{"keep1", "keep2"}, + []string{}, + )), + unpredicated: MakeLabelListAttribute(makeLabelList( + []string{"throw1", "throw2"}, + []string{}, + )), + predicate: func(label Label) bool { + return strings.HasPrefix(label.Label, "keep") + }, + }, + { + name: "empty partition", + input: MakeLabelListAttribute(makeLabelList([]string{}, []string{})), + predicated: LabelListAttribute{}, + unpredicated: LabelListAttribute{}, + predicate: func(label Label) bool { + return true + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + predicated, unpredicated := tc.input.Partition(tc.predicate) + if !predicated.Value.Equals(tc.predicated.Value) { + t.Errorf("expected predicated labels to be %v; got %v", tc.predicated, predicated) + } + for axis, configs := range predicated.ConfigurableValues { + tcConfigs, ok := tc.predicated.ConfigurableValues[axis] + if !ok || !reflect.DeepEqual(configs, tcConfigs) { + t.Errorf("expected predicated labels to be %v; got %v", tc.predicated, predicated) + } + } + if !unpredicated.Value.Equals(tc.unpredicated.Value) { + t.Errorf("expected unpredicated labels to be %v; got %v", tc.unpredicated, unpredicated) + } + for axis, configs := range unpredicated.ConfigurableValues { + tcConfigs, ok := tc.unpredicated.ConfigurableValues[axis] + if !ok || !reflect.DeepEqual(configs, tcConfigs) { + t.Errorf("expected unpredicated labels to be %v; got %v", tc.unpredicated, unpredicated) + } + } + }) + } +} + // labelAddSuffixForTypeMapper returns a LabelMapper that adds suffix to label name for modules of // typ func labelAddSuffixForTypeMapper(suffix, typ string) LabelMapper { diff --git a/bp2build/Android.bp b/bp2build/Android.bp index cb25627b2..d327157d5 100644 --- a/bp2build/Android.bp +++ b/bp2build/Android.bp @@ -18,6 +18,7 @@ bootstrap_go_package { "testing.go", ], deps: [ + "aidl-soong-rules", "soong-android", "soong-android-allowlists", "soong-android-soongconfig", @@ -36,6 +37,7 @@ bootstrap_go_package { ], testSrcs: [ "aar_conversion_test.go", + "aidl_interface_conversion_test.go", "android_app_certificate_conversion_test.go", "android_app_conversion_test.go", "apex_conversion_test.go", diff --git a/bp2build/aidl_interface_conversion_test.go b/bp2build/aidl_interface_conversion_test.go new file mode 100644 index 000000000..38d902d3b --- /dev/null +++ b/bp2build/aidl_interface_conversion_test.go @@ -0,0 +1,249 @@ +package bp2build + +import ( + "android/soong/aidl" + "android/soong/android" + "testing" +) + +func runAidlInterfaceTestCase(t *testing.T, tc Bp2buildTestCase) { + t.Helper() + RunBp2BuildTestCase( + t, + func(ctx android.RegistrationContext) { + ctx.RegisterModuleType("aidl_interface", aidl.AidlInterfaceFactory) + ctx.RegisterModuleType("aidl_interface_headers", aidl.AidlInterfaceHeadersFactory) + }, + tc, + ) +} + +func TestAidlInterfaceHeaders(t *testing.T) { + runAidlInterfaceTestCase(t, Bp2buildTestCase{ + Description: `aidl_interface_headers`, + Blueprint: ` +aidl_interface_headers { + name: "aidl-interface-headers", + include_dir: "src", + srcs: [ + "src/A.aidl", + ], +} +`, + ExpectedBazelTargets: []string{ + MakeBazelTargetNoRestrictions("aidl_library", "aidl-interface-headers", AttrNameToString{ + "strip_import_prefix": `"src"`, + "hdrs": `["src/A.aidl"]`, + }), + }, + }) +} + +func TestAidlInterface(t *testing.T) { + runAidlInterfaceTestCase(t, Bp2buildTestCase{ + Description: `aidl_interface with single "latest" aidl_interface import`, + Blueprint: ` +aidl_interface_headers { + name: "aidl-interface-headers", +} +aidl_interface { + name: "aidl-interface-import", + versions: [ + "1", + "2", + ], +} +aidl_interface { + name: "aidl-interface1", + flags: ["--flag1"], + imports: [ + "aidl-interface-import-V1", + ], + headers: [ + "aidl-interface-headers", + ], + versions: [ + "1", + "2", + "3", + ], +}`, + ExpectedBazelTargets: []string{ + MakeBazelTargetNoRestrictions("aidl_library", "aidl-interface-headers", AttrNameToString{}), + MakeBazelTargetNoRestrictions("aidl_interface", "aidl-interface-import", AttrNameToString{ + "backends": `[ + "cpp", + "java", + "ndk", + ]`, + "versions": `[ + "1", + "2", + ]`, + }), + MakeBazelTargetNoRestrictions("aidl_interface", "aidl-interface1", AttrNameToString{ + "backends": `[ + "cpp", + "java", + "ndk", + ]`, + "deps": `[ + ":aidl-interface-import-V1", + ":aidl-interface-headers", + ]`, + "flags": `["--flag1"]`, + "versions": `[ + "1", + "2", + "3", + ]`, + }), + }, + }) +} + +func TestAidlInterfaceWithNoProperties(t *testing.T) { + runAidlInterfaceTestCase(t, Bp2buildTestCase{ + Description: `aidl_interface no properties set`, + Blueprint: ` +aidl_interface { + name: "aidl-interface1", +} +`, + ExpectedBazelTargets: []string{ + MakeBazelTargetNoRestrictions("aidl_interface", "aidl-interface1", AttrNameToString{ + "backends": `[ + "cpp", + "java", + "ndk", + ]`, + }), + }, + }) +} + +func TestAidlInterfaceWithDisabledBackends(t *testing.T) { + runAidlInterfaceTestCase(t, Bp2buildTestCase{ + Description: `aidl_interface with some backends disabled`, + Blueprint: ` +aidl_interface { + name: "aidl-interface1", + backend: { + ndk: { + enabled: false, + }, + cpp: { + enabled: false, + }, + }, +} +`, + ExpectedBazelTargets: []string{ + MakeBazelTargetNoRestrictions("aidl_interface", "aidl-interface1", AttrNameToString{ + "backends": `["java"]`, + }), + }, + }) +} + +func TestAidlInterfaceWithLatestImport(t *testing.T) { + runAidlInterfaceTestCase(t, Bp2buildTestCase{ + Description: `aidl_interface with single "latest" aidl_interface import`, + Blueprint: ` +aidl_interface { + name: "aidl-interface-import", + versions: [ + "1", + "2", + ], +} +aidl_interface { + name: "aidl-interface1", + imports: [ + "aidl-interface-import", + ], + versions: [ + "1", + "2", + "3", + ], +}`, + ExpectedBazelTargets: []string{ + MakeBazelTargetNoRestrictions("aidl_interface", "aidl-interface-import", AttrNameToString{ + "backends": `[ + "cpp", + "java", + "ndk", + ]`, + "versions": `[ + "1", + "2", + ]`, + }), + MakeBazelTargetNoRestrictions("aidl_interface", "aidl-interface1", AttrNameToString{ + "backends": `[ + "cpp", + "java", + "ndk", + ]`, + "deps": `[":aidl-interface-import-latest"]`, + "versions": `[ + "1", + "2", + "3", + ]`, + }), + }, + }) +} + +func TestAidlInterfaceWithVersionedImport(t *testing.T) { + runAidlInterfaceTestCase(t, Bp2buildTestCase{ + Description: `aidl_interface with single versioned aidl_interface import`, + Blueprint: ` +aidl_interface { + name: "aidl-interface-import", + versions: [ + "1", + "2", + ], +} +aidl_interface { + name: "aidl-interface1", + imports: [ + "aidl-interface-import-V2", + ], + versions: [ + "1", + "2", + "3", + ], +}`, + ExpectedBazelTargets: []string{ + MakeBazelTargetNoRestrictions("aidl_interface", "aidl-interface-import", AttrNameToString{ + "backends": `[ + "cpp", + "java", + "ndk", + ]`, + "versions": `[ + "1", + "2", + ]`, + }), + MakeBazelTargetNoRestrictions("aidl_interface", "aidl-interface1", AttrNameToString{ + "backends": `[ + "cpp", + "java", + "ndk", + ]`, + "deps": `[":aidl-interface-import-V2"]`, + "versions": `[ + "1", + "2", + "3", + ]`, + }), + }, + }) +} diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go index 7fa19d950..f5a59382c 100644 --- a/bp2build/java_library_conversion_test.go +++ b/bp2build/java_library_conversion_test.go @@ -372,3 +372,132 @@ func TestJavaLibraryResourcesFailsWithMultipleDirs(t *testing.T) { ExpectedBazelTargets: []string{}, }) } + +func TestJavaLibraryAidl(t *testing.T) { + runJavaLibraryTestCase(t, Bp2buildTestCase{ + Description: "Java library - aidl creates separate dependency", + ModuleTypeUnderTest: "java_library", + ModuleTypeUnderTestFactory: java.LibraryFactory, + Blueprint: `java_library { + name: "example_lib", + srcs: [ + "a.java", + "b.java", + "a.aidl", + "b.aidl", + ], + bazel_module: { bp2build_available: true }, +}`, + ExpectedBazelTargets: []string{ + makeBazelTarget("aidl_library", "example_lib_aidl_library", AttrNameToString{ + "srcs": `[ + "a.aidl", + "b.aidl", + ]`, + }), + makeBazelTarget("java_aidl_library", "example_lib_java_aidl_library", AttrNameToString{ + "deps": `[":example_lib_aidl_library"]`, + }), + makeBazelTarget("java_library", "example_lib", AttrNameToString{ + "deps": `[":example_lib_java_aidl_library"]`, + "exports": `[":example_lib_java_aidl_library"]`, + "srcs": `[ + "a.java", + "b.java", + ]`, + }), + }}) +} + +func TestJavaLibraryAidlSrcsNoFileGroup(t *testing.T) { + runJavaLibraryTestCaseWithRegistrationCtxFunc(t, Bp2buildTestCase{ + Description: "Java library - aidl filegroup is parsed", + ModuleTypeUnderTest: "java_library", + ModuleTypeUnderTestFactory: java.LibraryFactory, + Blueprint: ` +java_library { + name: "example_lib", + srcs: [ + "a.java", + "b.aidl", + ], + bazel_module: { bp2build_available: true }, +}`, + ExpectedBazelTargets: []string{ + makeBazelTarget("aidl_library", "example_lib_aidl_library", AttrNameToString{ + "srcs": `["b.aidl"]`, + }), + makeBazelTarget("java_aidl_library", "example_lib_java_aidl_library", AttrNameToString{ + "deps": `[":example_lib_aidl_library"]`, + }), + makeBazelTarget("java_library", "example_lib", AttrNameToString{ + "deps": `[":example_lib_java_aidl_library"]`, + "exports": `[":example_lib_java_aidl_library"]`, + "srcs": `["a.java"]`, + }), + }, + }, func(ctx android.RegistrationContext) { + ctx.RegisterModuleType("filegroup", android.FileGroupFactory) + }) +} + +func TestJavaLibraryAidlFilegroup(t *testing.T) { + runJavaLibraryTestCaseWithRegistrationCtxFunc(t, Bp2buildTestCase{ + Description: "Java library - aidl filegroup is parsed", + ModuleTypeUnderTest: "java_library", + ModuleTypeUnderTestFactory: java.LibraryFactory, + Blueprint: ` +filegroup { + name: "random_other_files", + srcs: [ + "a.java", + "b.java", + ], +} +filegroup { + name: "aidl_files", + srcs: [ + "a.aidl", + "b.aidl", + ], +} +java_library { + name: "example_lib", + srcs: [ + "a.java", + "b.java", + ":aidl_files", + ":random_other_files", + ], + bazel_module: { bp2build_available: true }, +}`, + ExpectedBazelTargets: []string{ + MakeBazelTargetNoRestrictions("aidl_library", "aidl_files", AttrNameToString{ + "srcs": `[ + "a.aidl", + "b.aidl", + ]`, + }), + makeBazelTarget("java_aidl_library", "example_lib_java_aidl_library", AttrNameToString{ + "deps": `[":aidl_files"]`, + }), + makeBazelTarget("java_library", "example_lib", AttrNameToString{ + "deps": `[":example_lib_java_aidl_library"]`, + "exports": `[":example_lib_java_aidl_library"]`, + "srcs": `[ + "a.java", + "b.java", + ":random_other_files", + ]`, + }), + MakeBazelTargetNoRestrictions("filegroup", "random_other_files", AttrNameToString{ + "srcs": `[ + "a.java", + "b.java", + ]`, + }), + }, + }, func(ctx android.RegistrationContext) { + ctx.RegisterModuleType("filegroup", android.FileGroupFactory) + }) +} diff --git a/go.mod b/go.mod index 8c1a9f011..2e28ab677 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,17 @@ module android/soong -require google.golang.org/protobuf v0.0.0 - -require github.com/google/blueprint v0.0.0 +require ( + google.golang.org/protobuf v0.0.0 + github.com/google/blueprint v0.0.0 + android/soong/aidl v0.0.0 +) replace google.golang.org/protobuf v0.0.0 => ../../external/golang-protobuf replace github.com/google/blueprint v0.0.0 => ../blueprint +replace android/soong/aidl v0.0.0 => ../../system/tools/aidl/build + // Indirect deps from golang-protobuf exclude github.com/golang/protobuf v1.5.0 diff --git a/java/base.go b/java/base.go index 2da7cbd41..cf3b3d510 100644 --- a/java/base.go +++ b/java/base.go @@ -820,7 +820,7 @@ func (j *Module) individualAidlFlags(ctx android.ModuleContext, aidlFile android } func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.OptionalPath, - aidlIncludeDirs android.Paths) (string, android.Paths) { + aidlIncludeDirs android.Paths, aidlSrcs android.Paths) (string, android.Paths) { aidlIncludes := android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Local_include_dirs) aidlIncludes = append(aidlIncludes, @@ -830,6 +830,7 @@ func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.Opt var flags []string var deps android.Paths + var includeDirs android.Paths flags = append(flags, j.deviceProperties.Aidl.Flags...) @@ -837,21 +838,24 @@ func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.Opt flags = append(flags, "-p"+aidlPreprocess.String()) deps = append(deps, aidlPreprocess.Path()) } else if len(aidlIncludeDirs) > 0 { - flags = append(flags, android.JoinWithPrefix(aidlIncludeDirs.Strings(), "-I")) + includeDirs = append(includeDirs, aidlIncludeDirs...) } if len(j.exportAidlIncludeDirs) > 0 { - flags = append(flags, android.JoinWithPrefix(j.exportAidlIncludeDirs.Strings(), "-I")) + includeDirs = append(includeDirs, j.exportAidlIncludeDirs...) } if len(aidlIncludes) > 0 { - flags = append(flags, android.JoinWithPrefix(aidlIncludes.Strings(), "-I")) + includeDirs = append(includeDirs, aidlIncludes...) } - flags = append(flags, "-I"+android.PathForModuleSrc(ctx).String()) + includeDirs = append(includeDirs, android.PathForModuleSrc(ctx)) if src := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "src"); src.Valid() { - flags = append(flags, "-I"+src.String()) + includeDirs = append(includeDirs, src.Path()) } + flags = append(flags, android.JoinWithPrefix(includeDirs.Strings(), "-I")) + // add flags for dirs containing AIDL srcs that haven't been specified yet + flags = append(flags, genAidlIncludeFlags(ctx, aidlSrcs, includeDirs)) if Bool(j.deviceProperties.Aidl.Generate_traces) { flags = append(flags, "-t") @@ -935,9 +939,6 @@ func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaB // systemModules flags.systemModules = deps.systemModules - // aidl flags. - flags.aidlFlags, flags.aidlDeps = j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs) - return flags } @@ -1046,6 +1047,9 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { ctx.PropertyErrorf("common_srcs", "common_srcs must be .kt files") } + aidlSrcs := srcFiles.FilterByExt(".aidl") + flags.aidlFlags, flags.aidlDeps = j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs, aidlSrcs) + nonGeneratedSrcJars := srcFiles.FilterByExt(".srcjar") srcFiles = j.genSources(ctx, srcFiles, flags) diff --git a/java/droiddoc.go b/java/droiddoc.go index 901419cba..9b1f43b4c 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -314,7 +314,7 @@ func (j *Javadoc) genSources(ctx android.ModuleContext, srcFiles android.Paths, outSrcFiles := make(android.Paths, 0, len(srcFiles)) var aidlSrcs android.Paths - aidlIncludeFlags := genAidlIncludeFlags(srcFiles) + aidlIncludeFlags := genAidlIncludeFlags(ctx, srcFiles, android.Paths{}) for _, srcFile := range srcFiles { switch srcFile.Ext() { diff --git a/java/gen.go b/java/gen.go index 1572bf00a..638da255a 100644 --- a/java/gen.go +++ b/java/gen.go @@ -15,6 +15,7 @@ package java import ( + "path/filepath" "strconv" "strings" @@ -116,12 +117,31 @@ func genLogtags(ctx android.ModuleContext, logtagsFile android.Path) android.Pat return javaFile } -func genAidlIncludeFlags(srcFiles android.Paths) string { +// genAidlIncludeFlags returns additional include flags based on the relative path +// of each .aidl file passed in srcFiles. excludeDirs is a list of paths relative to +// the Android checkout root that should not be included in the returned flags. +func genAidlIncludeFlags(ctx android.PathContext, srcFiles android.Paths, excludeDirs android.Paths) string { var baseDirs []string + excludeDirsStrings := excludeDirs.Strings() for _, srcFile := range srcFiles { if srcFile.Ext() == ".aidl" { baseDir := strings.TrimSuffix(srcFile.String(), srcFile.Rel()) - if baseDir != "" && !android.InList(baseDir, baseDirs) { + baseDir = filepath.Clean(baseDir) + baseDirSeen := android.InList(baseDir, baseDirs) || android.InList(baseDir, excludeDirsStrings) + + // For go/bp2build mixed builds, a file may be listed under a + // directory in the Bazel output tree that is symlinked to a + // directory under the android source tree. We should only + // include one copy of this directory so that the AIDL tool + // doesn't find multiple definitions of the same AIDL class. + // This code comes into effect when filegroups are used in mixed builds. + bazelPathPrefix := android.PathForBazelOut(ctx, "").String() + bazelBaseDir, err := filepath.Rel(bazelPathPrefix, baseDir) + bazelBaseDirSeen := err == nil && + android.InList(bazelBaseDir, baseDirs) || + android.InList(bazelBaseDir, excludeDirsStrings) + + if baseDir != "" && !baseDirSeen && !bazelBaseDirSeen { baseDirs = append(baseDirs, baseDir) } } @@ -136,8 +156,6 @@ func (j *Module) genSources(ctx android.ModuleContext, srcFiles android.Paths, var protoSrcs android.Paths var aidlSrcs android.Paths - aidlIncludeFlags := genAidlIncludeFlags(srcFiles) - for _, srcFile := range srcFiles { switch srcFile.Ext() { case ".aidl": @@ -168,7 +186,7 @@ func (j *Module) genSources(ctx android.ModuleContext, srcFiles android.Paths, individualFlags[aidlSrc.String()] = flags } } - srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, individualFlags, flags.aidlDeps) + srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags, individualFlags, flags.aidlDeps) outSrcFiles = append(outSrcFiles, srcJarFiles...) } diff --git a/java/java.go b/java/java.go index 210b883e1..0251b5754 100644 --- a/java/java.go +++ b/java/java.go @@ -2259,16 +2259,27 @@ type javaDependencyLabels struct { StaticDeps bazel.LabelListAttribute } -// convertLibraryAttrsBp2Build converts a few shared attributes from java_* modules -// and also separates dependencies into dynamic dependencies and static dependencies. -// Each corresponding Bazel target type, can have a different method for handling -// dynamic vs. static dependencies, and so these are returned to the calling function. type eventLogTagsAttributes struct { Srcs bazel.LabelListAttribute } +type aidlLibraryAttributes struct { + Srcs bazel.LabelListAttribute +} + +type javaAidlLibraryAttributes struct { + Deps bazel.LabelListAttribute +} + +// convertLibraryAttrsBp2Build converts a few shared attributes from java_* modules +// and also separates dependencies into dynamic dependencies and static dependencies. +// Each corresponding Bazel target type, can have a different method for handling +// dynamic vs. static dependencies, and so these are returned to the calling function. func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) (*javaCommonAttributes, *javaDependencyLabels) { var srcs bazel.LabelListAttribute + var deps bazel.LabelList + var staticDeps bazel.LabelList + archVariantProps := m.GetArchVariantProperties(ctx, &CommonProperties{}) for axis, configToProps := range archVariantProps { for config, _props := range configToProps { @@ -2282,18 +2293,18 @@ func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) javaSrcPartition := "java" protoSrcPartition := "proto" logtagSrcPartition := "logtag" + aidlSrcPartition := "aidl" srcPartitions := bazel.PartitionLabelListAttribute(ctx, &srcs, bazel.LabelPartitions{ javaSrcPartition: bazel.LabelPartition{Extensions: []string{".java"}, Keep_remainder: true}, logtagSrcPartition: bazel.LabelPartition{Extensions: []string{".logtags", ".logtag"}}, protoSrcPartition: android.ProtoSrcLabelPartition, + aidlSrcPartition: android.AidlSrcLabelPartition, }) javaSrcs := srcPartitions[javaSrcPartition] - var logtagsSrcs bazel.LabelList if !srcPartitions[logtagSrcPartition].IsEmpty() { logtagsLibName := m.Name() + "_logtags" - logtagsSrcs = bazel.MakeLabelList([]bazel.Label{{Label: ":" + logtagsLibName}}) ctx.CreateBazelTargetModule( bazel.BazelTargetModuleProperties{ Rule_class: "event_log_tags", @@ -2304,8 +2315,45 @@ func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) Srcs: srcPartitions[logtagSrcPartition], }, ) + + logtagsSrcs := bazel.MakeLabelList([]bazel.Label{{Label: ":" + logtagsLibName}}) + javaSrcs.Append(bazel.MakeLabelListAttribute(logtagsSrcs)) + } + + if !srcPartitions[aidlSrcPartition].IsEmpty() { + aidlLibs, aidlSrcs := srcPartitions[aidlSrcPartition].Partition(func(src bazel.Label) bool { + return android.IsConvertedToAidlLibrary(ctx, src.OriginalModuleName) + }) + + if !aidlSrcs.IsEmpty() { + aidlLibName := m.Name() + "_aidl_library" + ctx.CreateBazelTargetModule( + bazel.BazelTargetModuleProperties{ + Rule_class: "aidl_library", + Bzl_load_location: "//build/bazel/rules/aidl:library.bzl", + }, + android.CommonAttributes{Name: aidlLibName}, + &aidlLibraryAttributes{ + Srcs: aidlSrcs, + }, + ) + aidlLibs.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + aidlLibName}}) + } + + javaAidlLibName := m.Name() + "_java_aidl_library" + ctx.CreateBazelTargetModule( + bazel.BazelTargetModuleProperties{ + Rule_class: "java_aidl_library", + Bzl_load_location: "//build/bazel/rules/java:aidl_library.bzl", + }, + android.CommonAttributes{Name: javaAidlLibName}, + &javaAidlLibraryAttributes{ + Deps: aidlLibs, + }, + ) + + staticDeps.Add(&bazel.Label{Label: ":" + javaAidlLibName}) } - javaSrcs.Append(bazel.MakeLabelListAttribute(logtagsSrcs)) var javacopts []string if m.properties.Javacflags != nil { @@ -2331,14 +2379,10 @@ func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) Javacopts: bazel.MakeStringListAttribute(javacopts), } - depLabels := &javaDependencyLabels{} - - var deps bazel.LabelList if m.properties.Libs != nil { deps.Append(android.BazelLabelForModuleDeps(ctx, android.LastUniqueStrings(android.CopyOf(m.properties.Libs)))) } - var staticDeps bazel.LabelList if m.properties.Static_libs != nil { staticDeps.Append(android.BazelLabelForModuleDeps(ctx, android.LastUniqueStrings(android.CopyOf(m.properties.Static_libs)))) } @@ -2352,6 +2396,7 @@ func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) // and so this should be a static dependency. staticDeps.Add(protoDepLabel) + depLabels := &javaDependencyLabels{} depLabels.Deps = bazel.MakeLabelListAttribute(deps) depLabels.StaticDeps = bazel.MakeLabelListAttribute(staticDeps) @@ -2376,7 +2421,7 @@ func javaLibraryBp2Build(ctx android.TopDownMutatorContext, m *Library) { // TODO(b/220869005) remove forced dependency on current public android.jar deps.Add(bazel.MakeLabelAttribute("//prebuilts/sdk:public_current_android_sdk_java_import")) } - } else if !depLabels.Deps.IsEmpty() { + } else if !deps.IsEmpty() { ctx.ModuleErrorf("Module has direct dependencies but no sources. Bazel will not allow this.") } diff --git a/java/java_test.go b/java/java_test.go index bfd97eb0d..7f0cea718 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -1747,3 +1747,37 @@ func TestImportMixedBuild(t *testing.T) { android.AssertDeepEquals(t, "Implementation/Resources JARs are produced", expectedOutputFiles, android.NormalizePathsForTesting(javaInfo.ImplementationAndResourcesJars)) android.AssertDeepEquals(t, "Implementation JARs are produced", expectedOutputFiles, android.NormalizePathsForTesting(javaInfo.ImplementationJars)) } + +func TestGenAidlIncludeFlagsForMixedBuilds(t *testing.T) { + bazelOutputBaseDir := filepath.Join("out", "bazel") + result := android.GroupFixturePreparers( + PrepareForIntegrationTestWithJava, + android.FixtureModifyConfig(func(config android.Config) { + config.BazelContext = android.MockBazelContext{ + OutputBaseDir: bazelOutputBaseDir, + } + }), + ).RunTest(t) + + ctx := &android.TestPathContext{TestResult: result} + + srcDirectory := filepath.Join("frameworks", "base") + srcDirectoryAlreadyIncluded := filepath.Join("frameworks", "base", "core", "java") + bazelSrcDirectory := android.PathForBazelOut(ctx, srcDirectory) + bazelSrcDirectoryAlreadyIncluded := android.PathForBazelOut(ctx, srcDirectoryAlreadyIncluded) + srcs := android.Paths{ + android.PathForTestingWithRel(bazelSrcDirectory.String(), "bazelAidl.aidl"), + android.PathForTestingWithRel(bazelSrcDirectory.String(), "bazelAidl2.aidl"), + android.PathForTestingWithRel(bazelSrcDirectoryAlreadyIncluded.String(), "bazelAidlExclude.aidl"), + android.PathForTestingWithRel(bazelSrcDirectoryAlreadyIncluded.String(), "bazelAidl2Exclude.aidl"), + } + dirsAlreadyIncluded := android.Paths{ + android.PathForTesting(srcDirectoryAlreadyIncluded), + } + + expectedFlags := " -Iout/bazel/execroot/__main__/frameworks/base" + flags := genAidlIncludeFlags(ctx, srcs, dirsAlreadyIncluded) + if flags != expectedFlags { + t.Errorf("expected flags to be %q; was %q", expectedFlags, flags) + } +} diff --git a/tests/lib.sh b/tests/lib.sh index 6210e779e..643b46ac9 100644 --- a/tests/lib.sh +++ b/tests/lib.sh @@ -93,6 +93,7 @@ function create_mock_soong { symlink_directory external/go-cmp symlink_directory external/golang-protobuf symlink_directory external/starlark-go + symlink_directory system/tools/aidl touch "$MOCK_TOP/Android.bp" }