diff options
54 files changed, 1719 insertions, 503 deletions
diff --git a/android/apex.go b/android/apex.go index b01b700b8..4b744364a 100644 --- a/android/apex.go +++ b/android/apex.go @@ -36,11 +36,16 @@ var ( // Accessible via `ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)` type ApexInfo struct { // Name of the apex variation that this module (i.e. the apex variant of the module) is - // mutated into, or "" for a platform (i.e. non-APEX) variant. Note that a module can be - // included in multiple APEXes, in which case, the module is mutated into one or more - // variants, each of which is for an APEX. The variants then can later be deduped if they - // don't need to be compiled differently. This is an optimization done in - // mergeApexVariations. + // mutated into, or "" for a platform (i.e. non-APEX) variant. Note that this name and the + // Soong module name of the APEX can be different. That happens when there is + // `override_apex` that overrides `apex`. In that case, both Soong modules have the same + // apex variation name which usually is `com.android.foo`. This name is also the `name` + // in the path `/apex/<name>` where this apex is activated on at runtime. + // + // Also note that a module can be included in multiple APEXes, in which case, the module is + // mutated into one or more variants, each of which is for an APEX. The variants then can + // later be deduped if they don't need to be compiled differently. This is an optimization + // done in mergeApexVariations. ApexVariationName string // ApiLevel that this module has to support at minimum. @@ -52,11 +57,19 @@ type ApexInfo struct { // The list of SDK modules that the containing apexBundle depends on. RequiredSdks SdkRefs - // List of apexBundles that this apex variant of the module is associated with. Initially, - // the size of this list is one because one apex variant is associated with one apexBundle. - // When multiple apex variants are merged in mergeApexVariations, ApexInfo struct of the - // merged variant holds the list of apexBundles that are merged together. - InApexes []string + // List of Apex variant names that this module is associated with. This initially is the + // same as the `ApexVariationName` field. Then when multiple apex variants are merged in + // mergeApexVariations, ApexInfo struct of the merged variant holds the list of apexBundles + // that are merged together. + InApexVariants []string + + // List of APEX Soong module names that this module is part of. Note that the list includes + // different variations of the same APEX. For example, if module `foo` is included in the + // apex `com.android.foo`, and also if there is an override_apex module + // `com.mycompany.android.foo` overriding `com.android.foo`, then this list contains both + // `com.android.foo` and `com.mycompany.android.foo`. If the APEX Soong module is a + // prebuilt, the name here doesn't have the `prebuilt_` prefix. + InApexModules []string // Pointers to the ApexContents struct each of which is for apexBundle modules that this // module is part of. The ApexContents gives information about which modules the apexBundle @@ -93,23 +106,33 @@ func (i ApexInfo) IsForPlatform() bool { return i.ApexVariationName == "" } -// InApex tells whether this apex variant of the module is part of the given apexBundle or not. -func (i ApexInfo) InApex(apex string) bool { - for _, a := range i.InApexes { - if a == apex { +// InApexVariant tells whether this apex variant of the module is part of the given apexVariant or +// not. +func (i ApexInfo) InApexVariant(apexVariant string) bool { + for _, a := range i.InApexVariants { + if a == apexVariant { return true } } return false } -// InApexByBaseName tells whether this apex variant of the module is part of the given APEX or not, -// where the APEX is specified by its canonical base name, i.e. typically beginning with +// InApexByBaseName tells whether this apex variant of the module is part of the given apexVariant +// or not, where the APEX is specified by its canonical base name, i.e. typically beginning with // "com.android.". In particular this function doesn't differentiate between source and prebuilt // APEXes, where the latter may have "prebuilt_" prefixes. -func (i ApexInfo) InApexByBaseName(apex string) bool { - for _, a := range i.InApexes { - if RemoveOptionalPrebuiltPrefix(a) == apex { +func (i ApexInfo) InApexVariantByBaseName(apexVariant string) bool { + for _, a := range i.InApexVariants { + if RemoveOptionalPrebuiltPrefix(a) == apexVariant { + return true + } + } + return false +} + +func (i ApexInfo) InApexModule(apexModuleName string) bool { + for _, a := range i.InApexModules { + if a == apexModuleName { return true } } @@ -345,8 +368,21 @@ func (m *ApexModuleBase) ApexAvailable() []string { func (m *ApexModuleBase) BuildForApex(apex ApexInfo) { m.apexInfosLock.Lock() defer m.apexInfosLock.Unlock() - for _, v := range m.apexInfos { + for i, v := range m.apexInfos { if v.ApexVariationName == apex.ApexVariationName { + if len(apex.InApexModules) != 1 { + panic(fmt.Errorf("Newly created apexInfo must be for a single APEX")) + } + // Even when the ApexVariantNames are the same, the given ApexInfo might + // actually be for different APEX. This can happen when an APEX is + // overridden via override_apex. For example, there can be two apexes + // `com.android.foo` (from the `apex` module type) and + // `com.mycompany.android.foo` (from the `override_apex` module type), both + // of which has the same ApexVariantName `com.android.foo`. Add the apex + // name to the list so that it's not lost. + if !InList(apex.InApexModules[0], v.InApexModules) { + m.apexInfos[i].InApexModules = append(m.apexInfos[i].InApexModules, apex.InApexModules[0]) + } return } } @@ -496,21 +532,23 @@ func mergeApexVariations(ctx PathContext, apexInfos []ApexInfo) (merged []ApexIn // Merge the ApexInfo together. If a compatible ApexInfo exists then merge the information from // this one into it, otherwise create a new merged ApexInfo from this one and save it away so // other ApexInfo instances can be merged into it. - apexName := apexInfo.ApexVariationName + variantName := apexInfo.ApexVariationName mergedName := apexInfo.mergedName(ctx) if index, exists := seen[mergedName]; exists { // Variants having the same mergedName are deduped - merged[index].InApexes = append(merged[index].InApexes, apexName) + merged[index].InApexVariants = append(merged[index].InApexVariants, variantName) + merged[index].InApexModules = append(merged[index].InApexModules, apexInfo.InApexModules...) merged[index].ApexContents = append(merged[index].ApexContents, apexInfo.ApexContents...) merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable } else { seen[mergedName] = len(merged) apexInfo.ApexVariationName = mergedName - apexInfo.InApexes = CopyOf(apexInfo.InApexes) + apexInfo.InApexVariants = CopyOf(apexInfo.InApexVariants) + apexInfo.InApexModules = CopyOf(apexInfo.InApexModules) apexInfo.ApexContents = append([]*ApexContents(nil), apexInfo.ApexContents...) merged = append(merged, apexInfo) } - aliases = append(aliases, [2]string{apexName, mergedName}) + aliases = append(aliases, [2]string{variantName, mergedName}) } return merged, aliases } @@ -583,15 +621,15 @@ func CreateApexVariations(mctx BottomUpMutatorContext, module ApexModule) []Modu // in the same APEX have unique APEX variations so that the module can link against the right // variant. func UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext, am ApexModule) { - // anyInSameApex returns true if the two ApexInfo lists contain any values in an InApexes - // list in common. It is used instead of DepIsInSameApex because it needs to determine if - // the dep is in the same APEX due to being directly included, not only if it is included - // _because_ it is a dependency. + // anyInSameApex returns true if the two ApexInfo lists contain any values in an + // InApexVariants list in common. It is used instead of DepIsInSameApex because it needs to + // determine if the dep is in the same APEX due to being directly included, not only if it + // is included _because_ it is a dependency. anyInSameApex := func(a, b []ApexInfo) bool { collectApexes := func(infos []ApexInfo) []string { var ret []string for _, info := range infos { - ret = append(ret, info.InApexes...) + ret = append(ret, info.InApexVariants...) } return ret } diff --git a/android/apex_test.go b/android/apex_test.go index 109b1c8b2..e1123692d 100644 --- a/android/apex_test.go +++ b/android/apex_test.go @@ -33,10 +33,10 @@ func Test_mergeApexVariations(t *testing.T) { { name: "single", in: []ApexInfo{ - {"foo", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex}, + {"apex10000", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, }, wantAliases: [][2]string{ {"foo", "apex10000"}, @@ -45,11 +45,11 @@ func Test_mergeApexVariations(t *testing.T) { { name: "merge", in: []ApexInfo{ - {"foo", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, nil, false}}, + {"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, false}}, wantAliases: [][2]string{ {"bar", "apex10000_baz_1"}, {"foo", "apex10000_baz_1"}, @@ -58,12 +58,12 @@ func Test_mergeApexVariations(t *testing.T) { { name: "don't merge version", in: []ApexInfo{ - {"foo", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex30", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, nil, NotForPrebuiltApex}, - {"apex10000", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex}, + {"apex30", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"apex10000", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, }, wantAliases: [][2]string{ {"bar", "apex30"}, @@ -73,11 +73,11 @@ func Test_mergeApexVariations(t *testing.T) { { name: "merge updatable", in: []ApexInfo{ - {"foo", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, true, nil, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", FutureApiLevel, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, + {"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, }, wantAliases: [][2]string{ {"bar", "apex10000"}, @@ -87,12 +87,12 @@ func Test_mergeApexVariations(t *testing.T) { { name: "don't merge sdks", in: []ApexInfo{ - {"foo", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000_baz_2", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex}, - {"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"apex10000_baz_2", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, }, wantAliases: [][2]string{ {"bar", "apex10000_baz_2"}, @@ -102,15 +102,15 @@ func Test_mergeApexVariations(t *testing.T) { { name: "don't merge when for prebuilt_apex", in: []ApexInfo{ - {"foo", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, true, nil, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", FutureApiLevel, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, // This one should not be merged in with the others because it is for // a prebuilt_apex. - {"baz", FutureApiLevel, true, nil, []string{"baz"}, nil, ForPrebuiltApex}, + {"baz", FutureApiLevel, true, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, - {"baz", FutureApiLevel, true, nil, []string{"baz"}, nil, ForPrebuiltApex}, + {"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, + {"baz", FutureApiLevel, true, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex}, }, wantAliases: [][2]string{ {"bar", "apex10000"}, diff --git a/android/bazel.go b/android/bazel.go index ba29f6d0f..2d4755fb5 100644 --- a/android/bazel.go +++ b/android/bazel.go @@ -160,14 +160,6 @@ var ( // Per-module denylist to always opt modules out of both bp2build and mixed builds. bp2buildModuleDoNotConvertList = []string{ // Things that transitively depend on unconverted libc_* modules. - "libc_nopthread", // http://b/186821550, cc_library_static, depends on //bionic/libc:libc_bionic_ndk (http://b/186822256) - // also depends on //bionic/libc:libc_tzcode (http://b/186822591) - // also depends on //bionic/libc:libstdc++ (http://b/186822597) - "libc_common", // http://b/186821517, cc_library_static, depends on //bionic/libc:libc_nopthread (http://b/186821550) - "libc_common_static", // http://b/186824119, cc_library_static, depends on //bionic/libc:libc_common (http://b/186821517) - "libc_common_shared", // http://b/186824118, cc_library_static, depends on //bionic/libc:libc_common (http://b/186821517) - "libc_nomalloc", // http://b/186825031, cc_library_static, depends on //bionic/libc:libc_common (http://b/186821517) - "libbionic_spawn_benchmark", // http://b/186824595, cc_library_static, depends on //external/google-benchmark (http://b/186822740) // also depends on //system/logging/liblog:liblog (http://b/186822772) @@ -186,12 +178,10 @@ var ( "liblinker_malloc", // http://b/186826466, cc_library_static, depends on //external/zlib:libz (http://b/186823782) // also depends on //system/libziparchive:libziparchive (http://b/186823656) // also depends on //system/logging/liblog:liblog (http://b/186822772) - "libc_jemalloc_wrapper", // http://b/187012490, cc_library_static, depends on //external/jemalloc_new:libjemalloc5 (http://b/186828626) - "libc_ndk", // http://b/187013218, cc_library_static, depends on //bionic/libm:libm (http://b/183064661) - "libc", // http://b/183064430, cc_library, depends on //external/jemalloc_new:libjemalloc5 (http://b/186828626) - "libc_bionic_ndk", // http://b/186822256, cc_library_static, fatal error: 'generated_android_ids.h' file not found - "libc_malloc_hooks", // http://b/187016307, cc_library, ld.lld: error: undefined symbol: __malloc_hook - "libm", // http://b/183064661, cc_library, math.h:25:16: error: unexpected token in argument list + "libc_ndk", // http://b/187013218, cc_library_static, depends on //bionic/libm:libm (http://b/183064661) + "libc", // http://b/183064430, cc_library, depends on //external/jemalloc_new:libjemalloc5 (http://b/186828626) + "libc_malloc_hooks", // http://b/187016307, cc_library, ld.lld: error: undefined symbol: __malloc_hook + "libm", // http://b/183064661, cc_library, math.h:25:16: error: unexpected token in argument list // http://b/186823769: Needs C++ STL support, includes from unconverted standard libraries in //external/libcxx // c++_static @@ -211,7 +201,6 @@ var ( "note_memtag_heap_async", // http://b/185127353: cc_library_static, error: feature.h not found "note_memtag_heap_sync", // http://b/185127353: cc_library_static, error: feature.h not found - "libjemalloc5", // cc_library, ld.lld: error: undefined symbol: memset "gwp_asan_crash_handler", // cc_library, ld.lld: error: undefined symbol: memset // Tests. Handle later. @@ -224,19 +213,26 @@ var ( // Per-module denylist of cc_library modules to only generate the static // variant if their shared variant isn't ready or buildable by Bazel. bp2buildCcLibraryStaticOnlyList = []string{ - "libstdc++", // http://b/186822597, cc_library, ld.lld: error: undefined symbol: __errno + "libstdc++", // http://b/186822597, cc_library, ld.lld: error: undefined symbol: __errno + "libjemalloc5", // http://b/188503688, cc_library, `target: { android: { enabled: false } }` for android targets. } // Per-module denylist to opt modules out of mixed builds. Such modules will // still be generated via bp2build. mixedBuildsDisabledList = []string{ + "libc_bionic_ndk", // cparsons@ cc_library_static, depends on //bionic/libc:libsystemproperties + "libc_common", // cparsons@ cc_library_static, depends on //bionic/libc:libc_nopthread + "libc_common_static", // cparsons@ cc_library_static, depends on //bionic/libc:libc_common + "libc_common_shared", // cparsons@ cc_library_static, depends on //bionic/libc:libc_common "libc_netbsd", // lberki@, cc_library_static, version script assignment of 'LIBC_PRIVATE' to symbol 'SHA1Final' failed: symbol not defined + "libc_nopthread", // cparsons@ cc_library_static, depends on //bionic/libc:libc_bionic_ndk "libc_openbsd", // ruperts@, cc_library_static, OK for bp2build but error: duplicate symbol: strcpy for mixed builds "libsystemproperties", // cparsons@, cc_library_static, wrong include paths "libpropertyinfoparser", // cparsons@, cc_library_static, wrong include paths "libarm-optimized-routines-string", // jingwen@, cc_library_static, OK for bp2build but b/186615213 (asflags not handled in bp2build), version script assignment of 'LIBC' to symbol 'memcmp' failed: symbol not defined (also for memrchr, strnlen) "fmtlib_ndk", // http://b/187040371, cc_library_static, OK for bp2build but format-inl.h:11:10: fatal error: 'cassert' file not found for mixed builds - } + "libc_nomalloc", // cc_library_static, OK for bp2build but ld.lld: error: undefined symbol: pthread_mutex_lock (and others) + } // Used for quicker lookups bp2buildModuleDoNotConvert = map[string]bool{} diff --git a/android/bazel_handler.go b/android/bazel_handler.go index 77d48b304..a1206dc53 100644 --- a/android/bazel_handler.go +++ b/android/bazel_handler.go @@ -333,7 +333,7 @@ func (r *builtinBazelRunner) issueBazelCommand(paths *bazelPaths, runName bazel. // The actual platform values here may be overridden by configuration // transitions from the buildroot. cmdFlags = append(cmdFlags, - fmt.Sprintf("--platforms=%s", "//build/bazel/platforms:android_x86_64")) + fmt.Sprintf("--platforms=%s", "//build/bazel/platforms:android_arm")) cmdFlags = append(cmdFlags, fmt.Sprintf("--extra_toolchains=%s", "//prebuilts/clang/host/linux-x86:all")) // This should be parameterized on the host OS, but let's restrict to linux diff --git a/android/module.go b/android/module.go index c9a1662bb..69572dd23 100644 --- a/android/module.go +++ b/android/module.go @@ -1809,8 +1809,8 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) if m.Enabled() { // ensure all direct android.Module deps are enabled ctx.VisitDirectDepsBlueprint(func(bm blueprint.Module) { - if _, ok := bm.(Module); ok { - ctx.validateAndroidModule(bm, ctx.baseModuleContext.strictVisitDeps) + if m, ok := bm.(Module); ok { + ctx.validateAndroidModule(bm, ctx.OtherModuleDependencyTag(m), ctx.baseModuleContext.strictVisitDeps) } }) @@ -2234,7 +2234,12 @@ func (b *baseModuleContext) AddMissingDependencies(deps []string) { } } -func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, strict bool) Module { +type AllowDisabledModuleDependency interface { + blueprint.DependencyTag + AllowDisabledModuleDependency(target Module) bool +} + +func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool) Module { aModule, _ := module.(Module) if !strict { @@ -2247,10 +2252,12 @@ func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, stric } if !aModule.Enabled() { - if b.Config().AllowMissingDependencies() { - b.AddMissingDependencies([]string{b.OtherModuleName(aModule)}) - } else { - b.ModuleErrorf("depends on disabled module %q", b.OtherModuleName(aModule)) + if t, ok := tag.(AllowDisabledModuleDependency); !ok || !t.AllowDisabledModuleDependency(aModule) { + if b.Config().AllowMissingDependencies() { + b.AddMissingDependencies([]string{b.OtherModuleName(aModule)}) + } else { + b.ModuleErrorf("depends on disabled module %q", b.OtherModuleName(aModule)) + } } return nil } @@ -2343,7 +2350,7 @@ func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) { b.bp.VisitDirectDeps(func(module blueprint.Module) { - if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { visit(aModule) } }) @@ -2351,7 +2358,7 @@ func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) { func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) { b.bp.VisitDirectDeps(func(module blueprint.Module) { - if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { if b.bp.OtherModuleDependencyTag(aModule) == tag { visit(aModule) } @@ -2363,7 +2370,7 @@ func (b *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func b.bp.VisitDirectDepsIf( // pred func(module blueprint.Module) bool { - if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { return pred(aModule) } else { return false @@ -2377,7 +2384,7 @@ func (b *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func func (b *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) { b.bp.VisitDepsDepthFirst(func(module blueprint.Module) { - if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { visit(aModule) } }) @@ -2387,7 +2394,7 @@ func (b *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, visit b.bp.VisitDepsDepthFirstIf( // pred func(module blueprint.Module) bool { - if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { return pred(aModule) } else { return false diff --git a/apex/Android.bp b/apex/Android.bp index e234181d3..14c877100 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -32,6 +32,7 @@ bootstrap_go_package { "apex_test.go", "bootclasspath_fragment_test.go", "platform_bootclasspath_test.go", + "systemserver_classpath_fragment_test.go", "vndk_test.go", ], pluginFor: ["soong_build"], diff --git a/apex/apex.go b/apex/apex.go index a8fbf2274..da4f472d1 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -102,6 +102,9 @@ type apexBundleProperties struct { // List of bootclasspath fragments that are embedded inside this APEX bundle. Bootclasspath_fragments []string + // List of systemserverclasspath fragments that are embedded inside this APEX bundle. + Systemserverclasspath_fragments []string + // List of java libraries that are embedded inside this APEX bundle. Java_libs []string @@ -575,6 +578,7 @@ var ( executableTag = dependencyTag{name: "executable", payload: true} fsTag = dependencyTag{name: "filesystem", payload: true} bcpfTag = dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true} + sscpfTag = dependencyTag{name: "systemserverclasspathFragment", payload: true, sourceOnly: true} compatConfigTag = dependencyTag{name: "compatConfig", payload: true, sourceOnly: true} javaLibTag = dependencyTag{name: "javaLib", payload: true} jniLibTag = dependencyTag{name: "jniLib", payload: true} @@ -755,6 +759,7 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) { // Common-arch dependencies come next commonVariation := ctx.Config().AndroidCommonTarget.Variations() ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.properties.Bootclasspath_fragments...) + ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.properties.Systemserverclasspath_fragments...) ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...) ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.properties.Bpfs...) ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...) @@ -901,12 +906,16 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) { // This is the main part of this mutator. Mark the collected dependencies that they need to // be built for this apexBundle. + + // Note that there are many different names. + // ApexVariationName: this is the name of the apex variation apexInfo := android.ApexInfo{ - ApexVariationName: mctx.ModuleName(), + ApexVariationName: mctx.ModuleName(), // could be com.android.foo MinSdkVersion: minSdkVersion, RequiredSdks: a.RequiredSdks(), Updatable: a.Updatable(), - InApexes: []string{mctx.ModuleName()}, + InApexVariants: []string{mctx.ModuleName()}, // could be com.android.foo + InApexModules: []string{a.Name()}, // could be com.mycompany.android.foo ApexContents: []*android.ApexContents{apexContents}, } mctx.WalkDeps(func(child, parent android.Module) bool { @@ -1599,7 +1608,7 @@ func (a *apexBundle) WalkPayloadDeps(ctx android.ModuleContext, do android.Paylo } ai := ctx.OtherModuleProvider(child, android.ApexInfoProvider).(android.ApexInfo) - externalDep := !android.InList(ctx.ModuleName(), ai.InApexes) + externalDep := !android.InList(ctx.ModuleName(), ai.InApexVariants) // Visit actually return do(ctx, parent, am, externalDep) @@ -1715,6 +1724,15 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { filesInfo = append(filesInfo, filesToAdd...) return true } + case sscpfTag: + { + if _, ok := child.(*java.SystemServerClasspathModule); !ok { + ctx.PropertyErrorf("systemserverclasspath_fragments", "%q is not a systemserverclasspath_fragment module", depName) + return false + } + filesInfo = append(filesInfo, apexClasspathFragmentProtoFile(ctx, child)) + return true + } case javaLibTag: switch child.(type) { case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport, *java.Import: @@ -1941,7 +1959,16 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { default: ctx.PropertyErrorf("bootclasspath_fragments", "bootclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child)) } - + } else if java.IsSystemServerClasspathFragmentContentDepTag(depTag) { + // Add the contents of the systemserverclasspath fragment to the apex. + switch child.(type) { + case *java.Library, *java.SdkLibrary: + af := apexFileForJavaModule(ctx, child.(javaModule)) + filesInfo = append(filesInfo, af) + return true // track transitive dependencies + default: + ctx.PropertyErrorf("systemserverclasspath_fragments", "systemserverclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child)) + } } else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok { // nothing } else if am.CanHaveApexVariants() && am.IsInstallableToApex() { @@ -2106,13 +2133,19 @@ func apexBootclasspathFragmentFiles(ctx android.ModuleContext, module blueprint. } // Add classpaths.proto config. - classpathProtoOutput := bootclasspathFragmentInfo.ClasspathFragmentProtoOutput - classpathProto := newApexFile(ctx, classpathProtoOutput, classpathProtoOutput.Base(), bootclasspathFragmentInfo.ClasspathFragmentProtoInstallDir.Rel(), etc, nil) - filesToAdd = append(filesToAdd, classpathProto) + filesToAdd = append(filesToAdd, apexClasspathFragmentProtoFile(ctx, module)) return filesToAdd } +// apexClasspathFragmentProtoFile returns apexFile structure defining the classpath.proto config that +// the module contributes to the apex. +func apexClasspathFragmentProtoFile(ctx android.ModuleContext, module blueprint.Module) apexFile { + fragmentInfo := ctx.OtherModuleProvider(module, java.ClasspathFragmentProtoContentInfoProvider).(java.ClasspathFragmentProtoContentInfo) + classpathProtoOutput := fragmentInfo.ClasspathFragmentProtoOutput + return newApexFile(ctx, classpathProtoOutput, classpathProtoOutput.Base(), fragmentInfo.ClasspathFragmentProtoInstallDir.Rel(), etc, nil) +} + // apexFileForBootclasspathFragmentContentModule creates an apexFile for a bootclasspath_fragment // content module, i.e. a library that is part of the bootclasspath. func apexFileForBootclasspathFragmentContentModule(ctx android.ModuleContext, fragmentModule blueprint.Module, javaModule javaModule) apexFile { @@ -2120,7 +2153,10 @@ func apexFileForBootclasspathFragmentContentModule(ctx android.ModuleContext, fr // Get the dexBootJar from the bootclasspath_fragment as that is responsible for performing the // hidden API encpding. - dexBootJar := bootclasspathFragmentInfo.DexBootJarPathForContentModule(javaModule) + dexBootJar, err := bootclasspathFragmentInfo.DexBootJarPathForContentModule(javaModule) + if err != nil { + ctx.ModuleErrorf("%s", err) + } // Create an apexFile as for a normal java module but with the dex boot jar provided by the // bootclasspath_fragment. diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go index 7bb3ff611..9a8c7d0b6 100644 --- a/apex/bootclasspath_fragment_test.go +++ b/apex/bootclasspath_fragment_test.go @@ -433,6 +433,14 @@ func TestBootclasspathFragmentContentsNoName(t *testing.T) { result := android.GroupFixturePreparers( prepareForTestWithBootclasspathFragment, prepareForTestWithMyapex, + // Configure bootclasspath jars to ensure that hidden API encoding is performed on them. + java.FixtureConfigureBootJars("myapex:foo", "myapex:bar"), + // Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding + // is disabled. + android.FixtureAddTextFile("frameworks/base/Android.bp", ""), + + java.PrepareForTestWithJavaSdkLibraryFiles, + java.FixtureWithLastReleaseApis("foo"), ).RunTestWithBp(t, ` apex { name: "myapex", @@ -449,10 +457,11 @@ func TestBootclasspathFragmentContentsNoName(t *testing.T) { private_key: "testkey.pem", } - java_library { + java_sdk_library { name: "foo", srcs: ["b.java"], - installable: true, + shared_library: false, + public: {enabled: true}, apex_available: [ "myapex", ], @@ -491,6 +500,30 @@ func TestBootclasspathFragmentContentsNoName(t *testing.T) { `myapex.key`, `mybootclasspathfragment`, }) + + apex := result.ModuleForTests("myapex", "android_common_myapex_image") + apexRule := apex.Rule("apexRule") + copyCommands := apexRule.Args["copy_commands"] + + // Make sure that the fragment provides the hidden API encoded dex jars to the APEX. + fragment := result.Module("mybootclasspathfragment", "android_common_apex10000") + + info := result.ModuleProvider(fragment, java.BootclasspathFragmentApexContentInfoProvider).(java.BootclasspathFragmentApexContentInfo) + + checkFragmentExportedDexJar := func(name string, expectedDexJar string) { + module := result.Module(name, "android_common_apex10000") + dexJar, err := info.DexBootJarPathForContentModule(module) + if err != nil { + t.Error(err) + } + android.AssertPathRelativeToTopEquals(t, name+" dex", expectedDexJar, dexJar) + + expectedCopyCommand := fmt.Sprintf("&& cp -f %s out/soong/.intermediates/myapex/android_common_myapex_image/image.apex/javalib/%s.jar", expectedDexJar, name) + android.AssertStringDoesContain(t, name+" apex copy command", copyCommands, expectedCopyCommand) + } + + checkFragmentExportedDexJar("foo", "out/soong/.intermediates/foo/android_common_apex10000/hiddenapi/foo.jar") + checkFragmentExportedDexJar("bar", "out/soong/.intermediates/bar/android_common_apex10000/hiddenapi/bar.jar") } // TODO(b/177892522) - add test for host apex. diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go index 52b1689ba..ce12f4635 100644 --- a/apex/platform_bootclasspath_test.go +++ b/apex/platform_bootclasspath_test.go @@ -20,6 +20,7 @@ import ( "android/soong/android" "android/soong/java" "github.com/google/blueprint" + "github.com/google/blueprint/proptools" ) // Contains tests for platform_bootclasspath logic from java/platform_bootclasspath.go that requires @@ -174,6 +175,141 @@ func TestPlatformBootclasspathDependencies(t *testing.T) { }) } +// TestPlatformBootclasspath_AlwaysUsePrebuiltSdks verifies that the build does not fail when +// AlwaysUsePrebuiltSdk() returns true. The structure of the modules in this test matches what +// currently exists in some places in the Android build but it is not the intended structure. It is +// in fact an invalid structure that should cause build failures. However, fixing that structure +// will take too long so in the meantime this tests the workarounds to avoid build breakages. +// +// The main issues with this structure are: +// 1. There is no prebuilt_bootclasspath_fragment referencing the "foo" java_sdk_library_import. +// 2. There is no prebuilt_apex/apex_set which makes the dex implementation jar available to the +// prebuilt_bootclasspath_fragment and the "foo" java_sdk_library_import. +// +// Together these cause the following symptoms: +// 1. The "foo" java_sdk_library_import does not have a dex implementation jar. +// 2. The "foo" java_sdk_library_import does not have a myapex variant. +// +// TODO(b/179354495): Fix the structure in this test once the main Android build has been fixed. +func TestPlatformBootclasspath_AlwaysUsePrebuiltSdks(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForTestWithPlatformBootclasspath, + prepareForTestWithMyapex, + // Configure two libraries, the first is a java_sdk_library whose prebuilt will be used because + // of AlwaysUsePrebuiltsSdk() but does not have an appropriate apex variant and does not provide + // a boot dex jar. The second is a normal library that is unaffected. The order matters because + // if the dependency on myapex:foo is filtered out because of either of those conditions then + // the dependencies resolved by the platform_bootclasspath will not match the configured list + // and so will fail the test. + java.FixtureConfigureUpdatableBootJars("myapex:foo", "myapex:bar"), + java.PrepareForTestWithJavaSdkLibraryFiles, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true) + }), + java.FixtureWithPrebuiltApis(map[string][]string{ + "current": {}, + "30": {"foo"}, + }), + ).RunTestWithBp(t, ` + apex { + name: "myapex", + key: "myapex.key", + bootclasspath_fragments: [ + "mybootclasspath-fragment", + ], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + java_library { + name: "bar", + srcs: ["b.java"], + installable: true, + apex_available: ["myapex"], + permitted_packages: ["bar"], + } + + java_sdk_library { + name: "foo", + srcs: ["b.java"], + shared_library: false, + public: { + enabled: true, + }, + apex_available: ["myapex"], + permitted_packages: ["foo"], + } + + // A prebuilt java_sdk_library_import that is not preferred by default but will be preferred + // because AlwaysUsePrebuiltSdks() is true. + java_sdk_library_import { + name: "foo", + prefer: false, + shared_library: false, + public: { + jars: ["sdk_library/public/foo-stubs.jar"], + stub_srcs: ["sdk_library/public/foo_stub_sources"], + current_api: "sdk_library/public/foo.txt", + removed_api: "sdk_library/public/foo-removed.txt", + sdk_version: "current", + }, + apex_available: ["myapex"], + } + + // This always depends on the source foo module, its dependencies are not affected by the + // AlwaysUsePrebuiltSdks(). + bootclasspath_fragment { + name: "mybootclasspath-fragment", + apex_available: [ + "myapex", + ], + contents: [ + "foo", "bar", + ], + } + + platform_bootclasspath { + name: "myplatform-bootclasspath", + } +`, + ) + + java.CheckPlatformBootclasspathModules(t, result, "myplatform-bootclasspath", []string{ + // The configured contents of BootJars. + "platform:prebuilt_foo", // Note: This is the platform not myapex variant. + "myapex:bar", + }) + + // Make sure that the myplatform-bootclasspath has the correct dependencies. + CheckModuleDependencies(t, result.TestContext, "myplatform-bootclasspath", "android_common", []string{ + // The following are stubs. + "platform:prebuilt_sdk_public_current_android", + "platform:prebuilt_sdk_system_current_android", + "platform:prebuilt_sdk_test_current_android", + + // Not a prebuilt as no prebuilt existed when it was added. + "platform:legacy.core.platform.api.stubs", + + // Needed for generating the boot image. + `platform:dex2oatd`, + + // The platform_bootclasspath intentionally adds dependencies on both source and prebuilt + // modules when available as it does not know which one will be preferred. + // + // The source module has an APEX variant but the prebuilt does not. + "myapex:foo", + "platform:prebuilt_foo", + + // Only a source module exists. + "myapex:bar", + }) +} + // CheckModuleDependencies checks the dependencies of the selected module against the expected list. // // The expected list must be a list of strings of the form "<apex>:<module>", where <apex> is the diff --git a/apex/prebuilt.go b/apex/prebuilt.go index 9d632a9b1..6125ef50f 100644 --- a/apex/prebuilt.go +++ b/apex/prebuilt.go @@ -230,7 +230,8 @@ func (p *prebuiltCommon) apexInfoMutator(mctx android.TopDownMutatorContext) { // Create an ApexInfo for the prebuilt_apex. apexInfo := android.ApexInfo{ ApexVariationName: android.RemoveOptionalPrebuiltPrefix(mctx.ModuleName()), - InApexes: []string{mctx.ModuleName()}, + InApexVariants: []string{mctx.ModuleName()}, + InApexModules: []string{mctx.ModuleName()}, ApexContents: []*android.ApexContents{apexContents}, ForPrebuiltApex: true, } diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go new file mode 100644 index 000000000..e1a101ad9 --- /dev/null +++ b/apex/systemserver_classpath_fragment_test.go @@ -0,0 +1,78 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// 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 apex + +import ( + "testing" + + "android/soong/android" + "android/soong/java" +) + +var prepareForTestWithSystemserverclasspathFragment = android.GroupFixturePreparers( + java.PrepareForTestWithDexpreopt, + PrepareForTestWithApexBuildComponents, +) + +func TestSystemserverclasspathFragmentContents(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForTestWithSystemserverclasspathFragment, + prepareForTestWithMyapex, + ).RunTestWithBp(t, ` + apex { + name: "myapex", + key: "myapex.key", + systemserverclasspath_fragments: [ + "mysystemserverclasspathfragment", + ], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + java_library { + name: "foo", + srcs: ["b.java"], + installable: true, + apex_available: [ + "myapex", + ], + } + + systemserverclasspath_fragment { + name: "mysystemserverclasspathfragment", + contents: [ + "foo", + ], + apex_available: [ + "myapex", + ], + } + `) + + ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{ + "etc/classpaths/mysystemserverclasspathfragment.pb", + "javalib/foo.jar", + }) + + java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex_image", []string{ + `myapex.key`, + `mysystemserverclasspathfragment`, + }) +} diff --git a/bazel/aquery.go b/bazel/aquery.go index 555f1dcb4..bda3a1ae6 100644 --- a/bazel/aquery.go +++ b/bazel/aquery.go @@ -81,23 +81,30 @@ type BuildStatement struct { Mnemonic string } -// AqueryBuildStatements returns an array of BuildStatements which should be registered (and output -// to a ninja file) to correspond one-to-one with the given action graph json proto (from a bazel -// aquery invocation). -func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) { - buildStatements := []BuildStatement{} - - var aqueryResult actionGraphContainer - err := json.Unmarshal(aqueryJsonProto, &aqueryResult) - - if err != nil { - return nil, err - } +// A helper type for aquery processing which facilitates retrieval of path IDs from their +// less readable Bazel structures (depset and path fragment). +type aqueryArtifactHandler struct { + // Maps middleman artifact Id to input artifact depset ID. + // Middleman artifacts are treated as "substitute" artifacts for mixed builds. For example, + // if we find a middleman action which has outputs [foo, bar], and output [baz_middleman], then, + // for each other action which has input [baz_middleman], we add [foo, bar] to the inputs for + // that action instead. + middlemanIdToDepsetIds map[int][]int + // Maps depset Id to depset struct. + depsetIdToDepset map[int]depSetOfFiles + // depsetIdToArtifactIdsCache is a memoization of depset flattening, because flattening + // may be an expensive operation. + depsetIdToArtifactIdsCache map[int][]int + // Maps artifact Id to fully expanded path. + artifactIdToPath map[int]string +} +func newAqueryHandler(aqueryResult actionGraphContainer) (*aqueryArtifactHandler, error) { pathFragments := map[int]pathFragment{} for _, pathFragment := range aqueryResult.PathFragments { pathFragments[pathFragment.Id] = pathFragment } + artifactIdToPath := map[int]string{} for _, artifact := range aqueryResult.Artifacts { artifactPath, err := expandPathFragment(artifact.PathFragmentId, pathFragments) @@ -112,22 +119,87 @@ func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) { depsetIdToDepset[depset.Id] = depset } - // depsetIdToArtifactIdsCache is a memoization of depset flattening, because flattening - // may be an expensive operation. - depsetIdToArtifactIdsCache := map[int][]int{} - // Do a pass through all actions to identify which artifacts are middleman artifacts. - // These will be omitted from the inputs of other actions. - // TODO(b/180945500): Handle middleman actions; without proper handling, depending on generated - // headers may cause build failures. - middlemanArtifactIds := map[int]bool{} + middlemanIdToDepsetIds := map[int][]int{} for _, actionEntry := range aqueryResult.Actions { if actionEntry.Mnemonic == "Middleman" { for _, outputId := range actionEntry.OutputIds { - middlemanArtifactIds[outputId] = true + middlemanIdToDepsetIds[outputId] = actionEntry.InputDepSetIds + } + } + } + return &aqueryArtifactHandler{ + middlemanIdToDepsetIds: middlemanIdToDepsetIds, + depsetIdToDepset: depsetIdToDepset, + depsetIdToArtifactIdsCache: map[int][]int{}, + artifactIdToPath: artifactIdToPath, + }, nil +} + +func (a *aqueryArtifactHandler) getInputPaths(depsetIds []int) ([]string, error) { + inputPaths := []string{} + + for _, inputDepSetId := range depsetIds { + inputArtifacts, err := a.artifactIdsFromDepsetId(inputDepSetId) + if err != nil { + return nil, err + } + for _, inputId := range inputArtifacts { + if middlemanInputDepsetIds, isMiddlemanArtifact := a.middlemanIdToDepsetIds[inputId]; isMiddlemanArtifact { + // Add all inputs from middleman actions which created middleman artifacts which are + // in the inputs for this action. + swappedInputPaths, err := a.getInputPaths(middlemanInputDepsetIds) + if err != nil { + return nil, err + } + inputPaths = append(inputPaths, swappedInputPaths...) + } else { + inputPath, exists := a.artifactIdToPath[inputId] + if !exists { + return nil, fmt.Errorf("undefined input artifactId %d", inputId) + } + inputPaths = append(inputPaths, inputPath) } } } + return inputPaths, nil +} + +func (a *aqueryArtifactHandler) artifactIdsFromDepsetId(depsetId int) ([]int, error) { + if result, exists := a.depsetIdToArtifactIdsCache[depsetId]; exists { + return result, nil + } + if depset, exists := a.depsetIdToDepset[depsetId]; exists { + result := depset.DirectArtifactIds + for _, childId := range depset.TransitiveDepSetIds { + childArtifactIds, err := a.artifactIdsFromDepsetId(childId) + if err != nil { + return nil, err + } + result = append(result, childArtifactIds...) + } + a.depsetIdToArtifactIdsCache[depsetId] = result + return result, nil + } else { + return nil, fmt.Errorf("undefined input depsetId %d", depsetId) + } +} + +// AqueryBuildStatements returns an array of BuildStatements which should be registered (and output +// to a ninja file) to correspond one-to-one with the given action graph json proto (from a bazel +// aquery invocation). +func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) { + buildStatements := []BuildStatement{} + + var aqueryResult actionGraphContainer + err := json.Unmarshal(aqueryJsonProto, &aqueryResult) + if err != nil { + return nil, err + } + aqueryHandler, err := newAqueryHandler(aqueryResult) + if err != nil { + return nil, err + } for _, actionEntry := range aqueryResult.Actions { if shouldSkipAction(actionEntry) { @@ -136,7 +208,7 @@ func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) { outputPaths := []string{} var depfile *string for _, outputId := range actionEntry.OutputIds { - outputPath, exists := artifactIdToPath[outputId] + outputPath, exists := aqueryHandler.artifactIdToPath[outputId] if !exists { return nil, fmt.Errorf("undefined outputId %d", outputId) } @@ -151,25 +223,11 @@ func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) { outputPaths = append(outputPaths, outputPath) } } - inputPaths := []string{} - for _, inputDepSetId := range actionEntry.InputDepSetIds { - inputArtifacts, err := - artifactIdsFromDepsetId(depsetIdToDepset, depsetIdToArtifactIdsCache, inputDepSetId) - if err != nil { - return nil, err - } - for _, inputId := range inputArtifacts { - if _, isMiddlemanArtifact := middlemanArtifactIds[inputId]; isMiddlemanArtifact { - // Omit middleman artifacts. - continue - } - inputPath, exists := artifactIdToPath[inputId] - if !exists { - return nil, fmt.Errorf("undefined input artifactId %d", inputId) - } - inputPaths = append(inputPaths, inputPath) - } + inputPaths, err := aqueryHandler.getInputPaths(actionEntry.InputDepSetIds) + if err != nil { + return nil, err } + buildStatement := BuildStatement{ Command: strings.Join(proptools.ShellEscapeList(actionEntry.Arguments), " "), Depfile: depfile, @@ -192,8 +250,9 @@ func shouldSkipAction(a action) bool { if a.Mnemonic == "Symlink" || a.Mnemonic == "SourceSymlinkManifest" || a.Mnemonic == "SymlinkTree" { return true } - // TODO(b/180945500): Handle middleman actions; without proper handling, depending on generated - // headers may cause build failures. + // Middleman actions are not handled like other actions; they are handled separately as a + // preparatory step so that their inputs may be relayed to actions depending on middleman + // artifacts. if a.Mnemonic == "Middleman" { return true } @@ -209,28 +268,6 @@ func shouldSkipAction(a action) bool { return false } -func artifactIdsFromDepsetId(depsetIdToDepset map[int]depSetOfFiles, - depsetIdToArtifactIdsCache map[int][]int, depsetId int) ([]int, error) { - if result, exists := depsetIdToArtifactIdsCache[depsetId]; exists { - return result, nil - } - if depset, exists := depsetIdToDepset[depsetId]; exists { - result := depset.DirectArtifactIds - for _, childId := range depset.TransitiveDepSetIds { - childArtifactIds, err := - artifactIdsFromDepsetId(depsetIdToDepset, depsetIdToArtifactIdsCache, childId) - if err != nil { - return nil, err - } - result = append(result, childArtifactIds...) - } - depsetIdToArtifactIdsCache[depsetId] = result - return result, nil - } else { - return nil, fmt.Errorf("undefined input depsetId %d", depsetId) - } -} - func expandPathFragment(id int, pathFragmentsMap map[int]pathFragment) (string, error) { labels := []string{} currId := id diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go index fa8810f0d..7b40dcdda 100644 --- a/bazel/aquery_test.go +++ b/bazel/aquery_test.go @@ -718,6 +718,93 @@ func TestTransitiveInputDepsets(t *testing.T) { assertBuildStatements(t, expectedBuildStatements, actualbuildStatements) } +func TestMiddlemenAction(t *testing.T) { + const inputString = ` +{ + "artifacts": [{ + "id": 1, + "pathFragmentId": 1 + }, { + "id": 2, + "pathFragmentId": 2 + }, { + "id": 3, + "pathFragmentId": 3 + }, { + "id": 4, + "pathFragmentId": 4 + }, { + "id": 5, + "pathFragmentId": 5 + }, { + "id": 6, + "pathFragmentId": 6 + }], + "pathFragments": [{ + "id": 1, + "label": "middleinput_one" + }, { + "id": 2, + "label": "middleinput_two" + }, { + "id": 3, + "label": "middleman_artifact" + }, { + "id": 4, + "label": "maininput_one" + }, { + "id": 5, + "label": "maininput_two" + }, { + "id": 6, + "label": "output" + }], + "depSetOfFiles": [{ + "id": 1, + "directArtifactIds": [1, 2] + }, { + "id": 2, + "directArtifactIds": [3, 4, 5] + }], + "actions": [{ + "targetId": 1, + "actionKey": "x", + "mnemonic": "Middleman", + "arguments": ["touch", "foo"], + "inputDepSetIds": [1], + "outputIds": [3], + "primaryOutputId": 3 + }, { + "targetId": 2, + "actionKey": "y", + "mnemonic": "Main action", + "arguments": ["touch", "foo"], + "inputDepSetIds": [2], + "outputIds": [6], + "primaryOutputId": 6 + }] +}` + + actual, err := AqueryBuildStatements([]byte(inputString)) + if err != nil { + t.Errorf("Unexpected error %q", err) + } + if expected := 1; len(actual) != expected { + t.Fatalf("Expected %d build statements, got %d", expected, len(actual)) + } + + bs := actual[0] + expectedInputs := []string{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"} + if !reflect.DeepEqual(bs.InputPaths, expectedInputs) { + t.Errorf("Expected main action inputs %q, but got %q", expectedInputs, bs.InputPaths) + } + + expectedOutputs := []string{"output"} + if !reflect.DeepEqual(bs.OutputPaths, expectedOutputs) { + t.Errorf("Expected main action outputs %q, but got %q", expectedOutputs, bs.OutputPaths) + } +} + func assertError(t *testing.T, err error, expected string) { if err == nil { t.Errorf("expected error '%s', but got no error", expected) diff --git a/bazel/properties.go b/bazel/properties.go index a71b12bfd..3e778bb69 100644 --- a/bazel/properties.go +++ b/bazel/properties.go @@ -160,6 +160,14 @@ func SubtractBazelLabels(haystack []Label, needle []Label) []Label { return labels } +// Appends two LabelLists, returning the combined list. +func AppendBazelLabelLists(a LabelList, b LabelList) LabelList { + var result LabelList + result.Includes = append(a.Includes, b.Includes...) + result.Excludes = append(a.Excludes, b.Excludes...) + return result +} + // Subtract needle from haystack func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList { var result LabelList diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go index 63a6c2e5c..71660a8e1 100644 --- a/bp2build/build_conversion_test.go +++ b/bp2build/build_conversion_test.go @@ -324,11 +324,11 @@ custom { ctx.RegisterForBazelConversion() _, errs := ctx.ParseFileList(dir, []string{"Android.bp"}) - if Errored(t, "", errs) { + if errored(t, "", errs) { continue } _, errs = ctx.ResolveDependencies(config) - if Errored(t, "", errs) { + if errored(t, "", errs) { continue } @@ -925,11 +925,11 @@ genrule { ctx.RegisterForBazelConversion() _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } @@ -957,17 +957,6 @@ genrule { } } -func Errored(t *testing.T, desc string, errs []error) bool { - t.Helper() - if len(errs) > 0 { - for _, err := range errs { - t.Errorf("%s: %s", desc, err) - } - return true - } - return false -} - type bp2buildMutator = func(android.TopDownMutatorContext) func TestBp2BuildInlinesDefaults(t *testing.T) { @@ -1483,11 +1472,11 @@ filegroup { ctx.RegisterForBazelConversion() _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } @@ -1606,11 +1595,11 @@ func TestGlobExcludeSrcs(t *testing.T) { ctx.RegisterForBazelConversion() _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go index 32b12e42e..204c51986 100644 --- a/bp2build/bzl_conversion_test.go +++ b/bp2build/bzl_conversion_test.go @@ -22,8 +22,6 @@ import ( "testing" ) -var buildDir string - func setUp() { var err error buildDir, err = ioutil.TempDir("", "bazel_queryview_test") diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go index 8afc82bb7..a1ffabce9 100644 --- a/bp2build/cc_library_conversion_test.go +++ b/bp2build/cc_library_conversion_test.go @@ -113,6 +113,7 @@ cc_library { copts = [ "-Wall", "-I.", + "-I$(BINDIR)/.", ], deps = [":some-headers"], includes = ["foo-dir"], @@ -183,6 +184,7 @@ cc_library { "-Wunused", "-Werror", "-I.", + "-I$(BINDIR)/.", ], deps = [":libc_headers"], linkopts = [ @@ -242,7 +244,10 @@ cc_library { bp: soongCcLibraryPreamble, expectedBazelTargets: []string{`cc_library( name = "fake-libarm-optimized-routines-math", - copts = ["-Iexternal"] + select({ + copts = [ + "-Iexternal", + "-I$(BINDIR)/external", + ] + select({ "//build/bazel/platforms/arch:arm64": ["-DHAVE_FAST_FMA=1"], "//conditions:default": [], }), @@ -310,6 +315,7 @@ cc_library { name: "shared_dep_for_both" } copts = [ "bothflag", "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", ], deps = [":static_dep_for_both"], dynamic_deps = [":shared_dep_for_both"], @@ -347,7 +353,10 @@ cc_library { bp: soongCcLibraryPreamble, expectedBazelTargets: []string{`cc_library( name = "a", - copts = ["-Ifoo/bar"], + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], srcs = ["a.cpp"], version_script = "v.map", )`}, @@ -380,7 +389,10 @@ cc_library { bp: soongCcLibraryPreamble, expectedBazelTargets: []string{`cc_library( name = "a", - copts = ["-Ifoo/bar"], + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], srcs = ["a.cpp"], version_script = select({ "//build/bazel/platforms/arch:arm": "arm.map", @@ -413,11 +425,17 @@ cc_library { bp: soongCcLibraryPreamble, expectedBazelTargets: []string{`cc_library( name = "a", - copts = ["-Ifoo/bar"], + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], dynamic_deps = [":mylib"], )`, `cc_library( name = "mylib", - copts = ["-Ifoo/bar"], + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], )`}, }, { @@ -461,12 +479,18 @@ cc_library { bp: soongCcLibraryPreamble, expectedBazelTargets: []string{`cc_library( name = "a", - copts = ["-Ifoo/bar"], + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], linkopts = ["-Wl,--pack-dyn-relocs=none"], srcs = ["a.cpp"], )`, `cc_library( name = "b", - copts = ["-Ifoo/bar"], + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], linkopts = select({ "//build/bazel/platforms/arch:x86_64": ["-Wl,--pack-dyn-relocs=none"], "//conditions:default": [], @@ -474,7 +498,10 @@ cc_library { srcs = ["b.cpp"], )`, `cc_library( name = "c", - copts = ["-Ifoo/bar"], + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], linkopts = select({ "//build/bazel/platforms/os:darwin": ["-Wl,--pack-dyn-relocs=none"], "//conditions:default": [], @@ -505,6 +532,7 @@ cc_library { "-include", "header.h", "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", ], )`}, }, @@ -548,6 +576,7 @@ cc_library { "-fsigned-char", "-pedantic", "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", ] + select({ "//build/bazel/platforms/arch:arm64": ["-DARM64=1"], "//conditions:default": [], @@ -588,11 +617,11 @@ cc_library { ctx.RegisterForBazelConversion() _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go index 0905aba1f..a3965ed9d 100644 --- a/bp2build/cc_library_headers_conversion_test.go +++ b/bp2build/cc_library_headers_conversion_test.go @@ -131,7 +131,10 @@ cc_library_headers { }`, expectedBazelTargets: []string{`cc_library_headers( name = "foo_headers", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], deps = [ ":lib-1", ":lib-2", @@ -147,11 +150,17 @@ cc_library_headers { }), )`, `cc_library_headers( name = "lib-1", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], includes = ["lib-1"], )`, `cc_library_headers( name = "lib-2", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], includes = ["lib-2"], )`}, }, @@ -185,16 +194,28 @@ cc_library_headers { }`, expectedBazelTargets: []string{`cc_library_headers( name = "android-lib", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], )`, `cc_library_headers( name = "base-lib", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], )`, `cc_library_headers( name = "darwin-lib", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], )`, `cc_library_headers( name = "foo_headers", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], deps = [":base-lib"] + select({ "//build/bazel/platforms/os:android": [":android-lib"], "//build/bazel/platforms/os:darwin": [":darwin-lib"], @@ -206,16 +227,28 @@ cc_library_headers { }), )`, `cc_library_headers( name = "fuchsia-lib", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], )`, `cc_library_headers( name = "linux-lib", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], )`, `cc_library_headers( name = "linux_bionic-lib", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], )`, `cc_library_headers( name = "windows-lib", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], )`}, }, { @@ -236,13 +269,22 @@ cc_library_headers { }`, expectedBazelTargets: []string{`cc_library_headers( name = "android-lib", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], )`, `cc_library_headers( name = "exported-lib", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], )`, `cc_library_headers( name = "foo_headers", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], deps = select({ "//build/bazel/platforms/os:android": [ ":android-lib", @@ -296,7 +338,10 @@ cc_library_headers { }`, expectedBazelTargets: []string{`cc_library_headers( name = "foo_headers", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], includes = ["shared_include_dir"] + select({ "//build/bazel/platforms/arch:arm": ["arm_include_dir"], "//build/bazel/platforms/arch:x86_64": ["x86_64_include_dir"], @@ -340,11 +385,11 @@ cc_library_headers { ctx.RegisterForBazelConversion() _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go index d082db1bd..4fcf5e474 100644 --- a/bp2build/cc_library_static_conversion_test.go +++ b/bp2build/cc_library_static_conversion_test.go @@ -17,6 +17,8 @@ package bp2build import ( "android/soong/android" "android/soong/cc" + "android/soong/genrule" + "strings" "testing" ) @@ -179,10 +181,15 @@ cc_library_static { "-Dflag1", "-Dflag2", "-Iinclude_dir_1", + "-I$(BINDIR)/include_dir_1", "-Iinclude_dir_2", + "-I$(BINDIR)/include_dir_2", "-Ilocal_include_dir_1", + "-I$(BINDIR)/local_include_dir_1", "-Ilocal_include_dir_2", + "-I$(BINDIR)/local_include_dir_2", "-I.", + "-I$(BINDIR)/.", ], deps = [ ":header_lib_1", @@ -205,22 +212,34 @@ cc_library_static { ], )`, `cc_library_static( name = "static_lib_1", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["static_lib_1.cc"], )`, `cc_library_static( name = "static_lib_2", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["static_lib_2.cc"], )`, `cc_library_static( name = "whole_static_lib_1", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["whole_static_lib_1.cc"], )`, `cc_library_static( name = "whole_static_lib_2", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["whole_static_lib_2.cc"], )`}, @@ -257,7 +276,9 @@ cc_library_static { name = "foo_static", copts = [ "-Isubpackage", + "-I$(BINDIR)/subpackage", "-I.", + "-I$(BINDIR)/.", ], linkstatic = True, )`}, @@ -280,7 +301,10 @@ cc_library_static { }`, expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], includes = ["subpackage"], linkstatic = True, )`}, @@ -303,7 +327,10 @@ cc_library_static { }`, expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], includes = ["subpackage"], linkstatic = True, )`}, @@ -341,10 +368,15 @@ cc_library_static { name = "foo_static", copts = [ "-Isubpackage/subsubpackage", + "-I$(BINDIR)/subpackage/subsubpackage", "-Isubpackage2", + "-I$(BINDIR)/subpackage2", "-Isubpackage3/subsubpackage", + "-I$(BINDIR)/subpackage3/subsubpackage", "-Isubpackage/subsubpackage2", + "-I$(BINDIR)/subpackage/subsubpackage2", "-Isubpackage", + "-I$(BINDIR)/subpackage", ], includes = ["./exported_subsubpackage"], linkstatic = True, @@ -372,7 +404,9 @@ cc_library_static { name = "foo_static", copts = [ "-Isubpackage", + "-I$(BINDIR)/subpackage", "-Isubpackage2", + "-I$(BINDIR)/subpackage2", ], linkstatic = True, )`}, @@ -401,8 +435,11 @@ cc_library_static { name = "foo_static", copts = [ "-Isubpackage", + "-I$(BINDIR)/subpackage", "-Isubpackage2", + "-I$(BINDIR)/subpackage2", "-I.", + "-I$(BINDIR)/.", ], linkstatic = True, )`}, @@ -423,7 +460,10 @@ cc_library_static { }`, expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], deps = select({ "//build/bazel/platforms/arch:arm64": [":static_dep"], "//conditions:default": [], @@ -435,11 +475,17 @@ cc_library_static { }), )`, `cc_library_static( name = "static_dep", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, )`, `cc_library_static( name = "static_dep2", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, )`}, }, @@ -459,7 +505,10 @@ cc_library_static { }`, expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], deps = select({ "//build/bazel/platforms/os:android": [":static_dep"], "//conditions:default": [], @@ -471,11 +520,17 @@ cc_library_static { }), )`, `cc_library_static( name = "static_dep", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, )`, `cc_library_static( name = "static_dep2", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, )`}, }, @@ -500,7 +555,10 @@ cc_library_static { }`, expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], deps = [":static_dep"] + select({ "//build/bazel/platforms/arch:arm64": [":static_dep4"], "//conditions:default": [], @@ -512,19 +570,31 @@ cc_library_static { whole_archive_deps = [":static_dep2"], )`, `cc_library_static( name = "static_dep", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, )`, `cc_library_static( name = "static_dep2", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, )`, `cc_library_static( name = "static_dep3", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, )`, `cc_library_static( name = "static_dep4", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, )`}, }, @@ -547,7 +617,10 @@ cc_library_static { }`, expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = [ "common.c", @@ -573,7 +646,10 @@ cc_library_static { }`, expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["common.c"] + select({ "//build/bazel/platforms/arch:arm": ["foo-arm.c"], @@ -604,7 +680,10 @@ cc_library_static { }`, expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["common.c"] + select({ "//build/bazel/platforms/arch:arm": ["for-arm.c"], @@ -637,7 +716,10 @@ cc_library_static { } `, expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["common.c"] + select({ "//build/bazel/platforms/arch:arm": [ @@ -687,7 +769,10 @@ cc_library_static { } `, expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["common.c"] + select({ "//build/bazel/platforms/arch:arm": [ @@ -738,12 +823,18 @@ cc_library_static { }`, expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], deps = [":static_dep"], linkstatic = True, )`, `cc_library_static( name = "static_dep", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, )`}, }, @@ -768,7 +859,10 @@ cc_library_static { } `, expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["common.c"] + select({ "//build/bazel/platforms/arch:arm": ["for-lib32.c"], @@ -801,7 +895,10 @@ cc_library_static { } `, expectedBazelTargets: []string{`cc_library_static( name = "foo_static2", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["common.c"] + select({ "//build/bazel/platforms/arch:arm": [ @@ -867,7 +964,10 @@ cc_library_static { }`, expectedBazelTargets: []string{`cc_library_static( name = "foo_static3", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["common.c"] + select({ "//build/bazel/platforms/arch:arm": [ @@ -913,6 +1013,94 @@ cc_library_static { }), )`}, }, + { + description: "cc_library_static arch srcs/exclude_srcs with generated files", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + filesystem: map[string]string{ + "common.c": "", + "for-x86.c": "", + "not-for-x86.c": "", + "not-for-everything.c": "", + "dep/Android.bp": ` +genrule { + name: "generated_src_other_pkg", + out: ["generated_src_other_pkg.cpp"], + cmd: "nothing to see here", +} + +genrule { + name: "generated_hdr_other_pkg", + out: ["generated_hdr_other_pkg.cpp"], + cmd: "nothing to see here", +} + +genrule { + name: "generated_hdr_other_pkg_x86", + out: ["generated_hdr_other_pkg_x86.cpp"], + cmd: "nothing to see here", +}`, + }, + bp: soongCcLibraryStaticPreamble + ` +genrule { + name: "generated_src", + out: ["generated_src.cpp"], + cmd: "nothing to see here", +} + +genrule { + name: "generated_src_x86", + out: ["generated_src_x86.cpp"], + cmd: "nothing to see here", +} + +genrule { + name: "generated_hdr", + out: ["generated_hdr.h"], + cmd: "nothing to see here", +} + +cc_library_static { + name: "foo_static3", + srcs: ["common.c", "not-for-*.c"], + exclude_srcs: ["not-for-everything.c"], + generated_sources: ["generated_src", "generated_src_other_pkg"], + generated_headers: ["generated_hdr", "generated_hdr_other_pkg"], + arch: { + x86: { + srcs: ["for-x86.c"], + exclude_srcs: ["not-for-x86.c"], + generated_sources: ["generated_src_x86"], + generated_headers: ["generated_hdr_other_pkg_x86"], + }, + }, +} +`, + expectedBazelTargets: []string{`cc_library_static( + name = "foo_static3", + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], + linkstatic = True, + srcs = [ + "//dep:generated_hdr_other_pkg", + "//dep:generated_src_other_pkg", + ":generated_hdr", + ":generated_src", + "common.c", + ] + select({ + "//build/bazel/platforms/arch:x86": [ + "//dep:generated_hdr_other_pkg_x86", + ":generated_src_x86", + "for-x86.c", + ], + "//conditions:default": ["not-for-x86.c"], + }), +)`}, + }, } dir := "." @@ -933,6 +1121,7 @@ cc_library_static { cc.RegisterCCBuildComponents(ctx) ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory) ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory) + ctx.RegisterModuleType("genrule", genrule.GenRuleFactory) ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory) for _, m := range testCase.depsMutators { @@ -943,11 +1132,11 @@ cc_library_static { ctx.RegisterForBazelConversion() _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go index 9efdb53de..ebc9c9449 100644 --- a/bp2build/cc_object_conversion_test.go +++ b/bp2build/cc_object_conversion_test.go @@ -65,7 +65,9 @@ func TestCcObjectBp2Build(t *testing.T) { "-Wall", "-Werror", "-Iinclude", + "-I$(BINDIR)/include", "-I.", + "-I$(BINDIR)/.", ], srcs = ["a/b/c.c"], )`, @@ -109,7 +111,9 @@ cc_defaults { "-Werror", "-fno-addrsig", "-Iinclude", + "-I$(BINDIR)/include", "-I.", + "-I$(BINDIR)/.", ], srcs = ["a/b/c.c"], )`, @@ -140,6 +144,7 @@ cc_object { copts = [ "-fno-addrsig", "-I.", + "-I$(BINDIR)/.", ], srcs = ["x/y/z.c"], )`, `cc_object( @@ -147,6 +152,7 @@ cc_object { copts = [ "-fno-addrsig", "-I.", + "-I$(BINDIR)/.", ], deps = [":bar"], srcs = ["a/b/c.c"], @@ -222,11 +228,11 @@ cc_object { ctx.RegisterForBazelConversion() _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } @@ -284,6 +290,7 @@ func TestCcObjectConfigurableAttributesBp2Build(t *testing.T) { copts = [ "-fno-addrsig", "-I.", + "-I$(BINDIR)/.", ] + select({ "//build/bazel/platforms/arch:x86": ["-fPIC"], "//conditions:default": [], @@ -329,6 +336,7 @@ func TestCcObjectConfigurableAttributesBp2Build(t *testing.T) { copts = [ "-fno-addrsig", "-I.", + "-I$(BINDIR)/.", ] + select({ "//build/bazel/platforms/arch:arm": ["-Wall"], "//build/bazel/platforms/arch:arm64": ["-Wall"], @@ -373,6 +381,7 @@ func TestCcObjectConfigurableAttributesBp2Build(t *testing.T) { copts = [ "-fno-addrsig", "-I.", + "-I$(BINDIR)/.", ] + select({ "//build/bazel/platforms/os:android": ["-fPIC"], "//build/bazel/platforms/os:darwin": ["-Wall"], @@ -402,11 +411,11 @@ func TestCcObjectConfigurableAttributesBp2Build(t *testing.T) { ctx.RegisterForBazelConversion() _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } diff --git a/bp2build/constants.go b/bp2build/constants.go index 70f320e12..4870dffff 100644 --- a/bp2build/constants.go +++ b/bp2build/constants.go @@ -19,7 +19,10 @@ var ( // be preferred for use within a Bazel build. // The file name used for automatically generated files. - GeneratedBuildFileName = "BUILD" + GeneratedBuildFileName = "BUILD.bazel" + // The file name used for hand-crafted build targets. + // NOTE: It is okay that this matches GeneratedBuildFileName, since we generate BUILD files in a different directory to source files + // FIXME: Because there are hundreds of existing BUILD.bazel files in the AOSP tree, we should pick another name here, like BUILD.android HandcraftedBuildFileName = "BUILD.bazel" ) diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go index a08c03d05..0931ff7fc 100644 --- a/bp2build/conversion_test.go +++ b/bp2build/conversion_test.go @@ -29,7 +29,7 @@ func TestCreateBazelFiles_QueryView_AddsTopLevelFiles(t *testing.T) { expectedFilePaths := []bazelFilepath{ { dir: "", - basename: "BUILD", + basename: "BUILD.bazel", }, { dir: "", @@ -37,7 +37,7 @@ func TestCreateBazelFiles_QueryView_AddsTopLevelFiles(t *testing.T) { }, { dir: bazelRulesSubDir, - basename: "BUILD", + basename: "BUILD.bazel", }, { dir: bazelRulesSubDir, @@ -69,7 +69,7 @@ func TestCreateBazelFiles_QueryView_AddsTopLevelFiles(t *testing.T) { if actualFile.Dir != expectedFile.dir || actualFile.Basename != expectedFile.basename { t.Errorf("Did not find expected file %s/%s", actualFile.Dir, actualFile.Basename) - } else if actualFile.Basename == "BUILD" || actualFile.Basename == "WORKSPACE" { + } else if actualFile.Basename == "BUILD.bazel" || actualFile.Basename == "WORKSPACE" { if actualFile.Contents != "" { t.Errorf("Expected %s to have no content.", actualFile) } diff --git a/bp2build/python_binary_conversion_test.go b/bp2build/python_binary_conversion_test.go index 2054e0678..ed509bf15 100644 --- a/bp2build/python_binary_conversion_test.go +++ b/bp2build/python_binary_conversion_test.go @@ -128,11 +128,11 @@ func TestPythonBinaryHost(t *testing.T) { ctx.RegisterForBazelConversion() _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } diff --git a/bp2build/sh_conversion_test.go b/bp2build/sh_conversion_test.go index 37f542ef7..575bf585c 100644 --- a/bp2build/sh_conversion_test.go +++ b/bp2build/sh_conversion_test.go @@ -101,11 +101,11 @@ func TestShBinaryBp2Build(t *testing.T) { ctx.RegisterForBazelConversion() _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } diff --git a/bp2build/testing.go b/bp2build/testing.go index b925682c4..e575bc6ff 100644 --- a/bp2build/testing.go +++ b/bp2build/testing.go @@ -1,6 +1,8 @@ package bp2build import ( + "testing" + "android/soong/android" "android/soong/bazel" ) @@ -10,6 +12,8 @@ var ( bp2buildConfig = android.Bp2BuildConfig{ android.BP2BUILD_TOPLEVEL: android.Bp2BuildDefaultTrueRecursively, } + + buildDir string ) type nestedProps struct { @@ -39,6 +43,17 @@ type customModule struct { props customProps } +func errored(t *testing.T, desc string, errs []error) bool { + t.Helper() + if len(errs) > 0 { + for _, err := range errs { + t.Errorf("%s: %s", desc, err) + } + return true + } + return false +} + // OutputFiles is needed because some instances of this module use dist with a // tag property which requires the module implements OutputFileProducer. func (m *customModule) OutputFiles(tag string) (android.Paths, error) { diff --git a/cc/Android.bp b/cc/Android.bp index c32cca8b8..1fc8d9f68 100644 --- a/cc/Android.bp +++ b/cc/Android.bp @@ -92,6 +92,7 @@ bootstrap_go_package { "object_test.go", "prebuilt_test.go", "proto_test.go", + "sanitize_test.go", "test_data_test.go", "vendor_public_library_test.go", "vendor_snapshot_test.go", diff --git a/cc/bp2build.go b/cc/bp2build.go index 33bb2699c..75543acf2 100644 --- a/cc/bp2build.go +++ b/cc/bp2build.go @@ -14,10 +14,11 @@ package cc import ( - "android/soong/android" - "android/soong/bazel" "path/filepath" "strings" + + "android/soong/android" + "android/soong/bazel" ) // bp2build functions and helpers for converting cc_* modules to Bazel. @@ -49,6 +50,22 @@ func depsBp2BuildMutator(ctx android.BottomUpMutatorContext) { var allDeps []string + for _, p := range module.GetTargetProperties(ctx, &BaseCompilerProperties{}) { + // base compiler props + if baseCompilerProps, ok := p.(*BaseCompilerProperties); ok { + allDeps = append(allDeps, baseCompilerProps.Generated_headers...) + allDeps = append(allDeps, baseCompilerProps.Generated_sources...) + } + } + + for _, p := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) { + // arch specific compiler props + if baseCompilerProps, ok := p.(*BaseCompilerProperties); ok { + allDeps = append(allDeps, baseCompilerProps.Generated_headers...) + allDeps = append(allDeps, baseCompilerProps.Generated_sources...) + } + } + for _, p := range module.GetTargetProperties(ctx, &BaseLinkerProperties{}) { // arch specific linker props if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok { @@ -74,12 +91,15 @@ func depsBp2BuildMutator(ctx android.BottomUpMutatorContext) { allDeps = append(allDeps, lib.SharedProperties.Shared.Static_libs...) allDeps = append(allDeps, lib.SharedProperties.Shared.Whole_static_libs...) allDeps = append(allDeps, lib.SharedProperties.Shared.Shared_libs...) - allDeps = append(allDeps, lib.SharedProperties.Shared.System_shared_libs...) allDeps = append(allDeps, lib.StaticProperties.Static.Static_libs...) allDeps = append(allDeps, lib.StaticProperties.Static.Whole_static_libs...) allDeps = append(allDeps, lib.StaticProperties.Static.Shared_libs...) - allDeps = append(allDeps, lib.StaticProperties.Static.System_shared_libs...) + + // TODO(b/186024507, b/186489250): Temporarily exclude adding + // system_shared_libs deps until libc and libm builds. + // allDeps = append(allDeps, lib.SharedProperties.Shared.System_shared_libs...) + // allDeps = append(allDeps, lib.StaticProperties.Static.System_shared_libs...) } ctx.AddDependency(module, nil, android.SortedUniqueStrings(allDeps)...) @@ -173,11 +193,19 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul var srcs bazel.LabelListAttribute var copts bazel.StringListAttribute - // Creates the -I flag for a directory, while making the directory relative + // Creates the -I flags for a directory, while making the directory relative // to the exec root for Bazel to work. - includeFlag := func(dir string) string { + includeFlags := func(dir string) []string { // filepath.Join canonicalizes the path, i.e. it takes care of . or .. elements. - return "-I" + filepath.Join(ctx.ModuleDir(), dir) + moduleDirRootedPath := filepath.Join(ctx.ModuleDir(), dir) + return []string{ + "-I" + moduleDirRootedPath, + // Include the bindir-rooted path (using make variable substitution). This most + // closely matches Bazel's native include path handling, which allows for dependency + // on generated headers in these directories. + // TODO(b/188084383): Handle local include directories in Bazel. + "-I$(BINDIR)/" + moduleDirRootedPath, + } } // Parse the list of module-relative include directories (-I). @@ -197,7 +225,7 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul copts = append(copts, strings.Split(flag, " ")...) } for _, dir := range parseLocalIncludeDirs(baseCompilerProps) { - copts = append(copts, includeFlag(dir)) + copts = append(copts, includeFlags(dir)...) } return copts } @@ -215,9 +243,17 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul parseSrcs := func(baseCompilerProps *BaseCompilerProperties) bazel.LabelList { // Combine the base srcs and arch-specific srcs allSrcs := append(baseSrcs, baseCompilerProps.Srcs...) + // Add srcs-like dependencies such as generated files. + // First create a LabelList containing these dependencies, then merge the values with srcs. + generatedHdrsAndSrcs := baseCompilerProps.Generated_headers + generatedHdrsAndSrcs = append(generatedHdrsAndSrcs, baseCompilerProps.Generated_sources...) + + generatedHdrsAndSrcsLabelList := android.BazelLabelForModuleDeps(ctx, generatedHdrsAndSrcs) + // Combine the base exclude_srcs and configuration-specific exclude_srcs allExcludeSrcs := append(baseExcludeSrcs, baseCompilerProps.Exclude_srcs...) - return android.BazelLabelForModuleSrcExcludes(ctx, allSrcs, allExcludeSrcs) + allSrcsLabelList := android.BazelLabelForModuleSrcExcludes(ctx, allSrcs, allExcludeSrcs) + return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedHdrsAndSrcsLabelList) } for _, props := range module.compiler.compilerProps() { @@ -227,8 +263,8 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul // Used for arch-specific srcs later. baseSrcs = baseCompilerProps.Srcs - baseExcludeSrcs = baseCompilerProps.Exclude_srcs baseSrcsLabelList = parseSrcs(baseCompilerProps) + baseExcludeSrcs = baseCompilerProps.Exclude_srcs break } } @@ -237,9 +273,9 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul // target has access to all headers recursively in the package, and has // "-I<module-dir>" in its copts. if c, ok := module.compiler.(*baseCompiler); ok && c.includeBuildDirectory() { - copts.Value = append(copts.Value, includeFlag(".")) + copts.Value = append(copts.Value, includeFlags(".")...) } else if c, ok := module.compiler.(*libraryDecorator); ok && c.includeBuildDirectory() { - copts.Value = append(copts.Value, includeFlag(".")) + copts.Value = append(copts.Value, includeFlags(".")...) } for arch, props := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) { @@ -2833,7 +2833,7 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { // Add the dependency to the APEX(es) providing the library so that // m <module> can trigger building the APEXes as well. depApexInfo := ctx.OtherModuleProvider(dep, android.ApexInfoProvider).(android.ApexInfo) - for _, an := range depApexInfo.InApexes { + for _, an := range depApexInfo.InApexVariants { c.Properties.ApexesProvidingSharedLibs = append( c.Properties.ApexesProvidingSharedLibs, an) } diff --git a/cc/config/global.go b/cc/config/global.go index 59fe8e13c..d6eba0fb1 100644 --- a/cc/config/global.go +++ b/cc/config/global.go @@ -225,18 +225,19 @@ func init() { // Everything in these lists is a crime against abstraction and dependency tracking. // Do not add anything to this list. - pctx.PrefixedExistentPathsForSourcesVariable("CommonGlobalIncludes", "-I", - []string{ - "system/core/include", - "system/logging/liblog/include", - "system/media/audio/include", - "hardware/libhardware/include", - "hardware/libhardware_legacy/include", - "hardware/ril/include", - "frameworks/native/include", - "frameworks/native/opengl/include", - "frameworks/av/include", - }) + commonGlobalIncludes := []string{ + "system/core/include", + "system/logging/liblog/include", + "system/media/audio/include", + "hardware/libhardware/include", + "hardware/libhardware_legacy/include", + "hardware/ril/include", + "frameworks/native/include", + "frameworks/native/opengl/include", + "frameworks/av/include", + } + exportedVars.Set("CommonGlobalIncludes", commonGlobalIncludes) + pctx.PrefixedExistentPathsForSourcesVariable("CommonGlobalIncludes", "-I", commonGlobalIncludes) pctx.SourcePathVariable("ClangDefaultBase", ClangDefaultBase) pctx.VariableFunc("ClangBase", func(ctx android.PackageVarContext) string { diff --git a/cc/linker.go b/cc/linker.go index 196806d33..5bd21eda3 100644 --- a/cc/linker.go +++ b/cc/linker.go @@ -393,7 +393,7 @@ func CheckSdkVersionAtLeast(ctx ModuleContext, SdkVersion android.ApiLevel) bool if ctx.minSdkVersion() == "current" { return true } - parsedSdkVersion, err := android.ApiLevelFromUser(ctx, ctx.minSdkVersion()) + parsedSdkVersion, err := nativeApiLevelFromUser(ctx, ctx.minSdkVersion()) if err != nil { ctx.PropertyErrorf("min_sdk_version", "Invalid min_sdk_version value (must be int or current): %q", @@ -424,7 +424,7 @@ func (linker *baseLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags { // ANDROID_RELR relocations were supported at API level >= 28. // Relocation packer was supported at API level >= 23. // Do the best we can... - if !ctx.useSdk() || CheckSdkVersionAtLeast(ctx, android.FirstShtRelrVersion) { + if (!ctx.useSdk() && ctx.minSdkVersion() == "") || CheckSdkVersionAtLeast(ctx, android.FirstShtRelrVersion) { flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--pack-dyn-relocs=android+relr") } else if CheckSdkVersionAtLeast(ctx, android.FirstAndroidRelrVersion) { flags.Global.LdFlags = append(flags.Global.LdFlags, diff --git a/cc/makevars.go b/cc/makevars.go index da5f1fde2..2b326ef4f 100644 --- a/cc/makevars.go +++ b/cc/makevars.go @@ -288,9 +288,7 @@ func makeVarsToolchain(ctx android.MakeVarsContext, secondPrefix string, ctx.Strict(makePrefix+"OBJCOPY", "${config.ClangBin}/llvm-objcopy") ctx.Strict(makePrefix+"LD", "${config.ClangBin}/lld") ctx.Strict(makePrefix+"NDK_TRIPLE", config.NDKTriple(toolchain)) - // TODO: work out whether to make this "${config.ClangBin}/llvm-", which - // should mostly work, or remove it. - ctx.Strict(makePrefix+"TOOLS_PREFIX", gccCmd(toolchain, "")) + ctx.Strict(makePrefix+"TOOLS_PREFIX", "${config.ClangBin}/llvm-") // TODO: GCC version is obsolete now that GCC has been removed. ctx.Strict(makePrefix+"GCC_VERSION", toolchain.GccVersion()) } diff --git a/cc/sanitize.go b/cc/sanitize.go index 605a8d094..f486ee470 100644 --- a/cc/sanitize.go +++ b/cc/sanitize.go @@ -902,7 +902,7 @@ func sanitizerDepsMutator(t SanitizerType) func(android.TopDownMutatorContext) { if d, ok := child.(PlatformSanitizeable); ok && d.SanitizePropDefined() && !d.SanitizeNever() && !d.IsSanitizerExplicitlyDisabled(t) { - if t == cfi || t == Hwasan || t == scs { + if t == cfi || t == Hwasan || t == scs || t == Asan { if d.StaticallyLinked() && d.SanitizerSupported(t) { // Rust does not support some of these sanitizers, so we need to check if it's // supported before setting this true. @@ -1261,7 +1261,7 @@ func sanitizerMutator(t SanitizerType) func(android.BottomUpMutatorContext) { modules[0].(PlatformSanitizeable).SetSanitizer(t, true) } else if c.IsSanitizerEnabled(t) || c.SanitizeDep() { isSanitizerEnabled := c.IsSanitizerEnabled(t) - if c.StaticallyLinked() || c.Header() || t == Asan || t == Fuzzer { + if c.StaticallyLinked() || c.Header() || t == Fuzzer { // Static and header libs are split into non-sanitized and sanitized variants. // Shared libs are not split. However, for asan and fuzzer, we split even for shared // libs because a library sanitized for asan/fuzzer can't be linked from a library diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go new file mode 100644 index 000000000..f12634678 --- /dev/null +++ b/cc/sanitize_test.go @@ -0,0 +1,204 @@ +// 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 cc + +import ( + "testing" + + "android/soong/android" +) + +var prepareForAsanTest = android.FixtureAddFile("asan/Android.bp", []byte(` + cc_library_shared { + name: "libclang_rt.asan-aarch64-android", + } + + cc_library_shared { + name: "libclang_rt.asan-arm-android", + } +`)) + +func TestAsan(t *testing.T) { + bp := ` + cc_binary { + name: "bin_with_asan", + host_supported: true, + shared_libs: [ + "libshared", + "libasan", + ], + static_libs: [ + "libstatic", + "libnoasan", + ], + sanitize: { + address: true, + } + } + + cc_binary { + name: "bin_no_asan", + host_supported: true, + shared_libs: [ + "libshared", + "libasan", + ], + static_libs: [ + "libstatic", + "libnoasan", + ], + } + + cc_library_shared { + name: "libshared", + host_supported: true, + shared_libs: ["libtransitive"], + } + + cc_library_shared { + name: "libasan", + host_supported: true, + shared_libs: ["libtransitive"], + sanitize: { + address: true, + } + } + + cc_library_shared { + name: "libtransitive", + host_supported: true, + } + + cc_library_static { + name: "libstatic", + host_supported: true, + } + + cc_library_static { + name: "libnoasan", + host_supported: true, + sanitize: { + address: false, + } + } + ` + + result := android.GroupFixturePreparers( + prepareForCcTest, + prepareForAsanTest, + ).RunTestWithBp(t, bp) + + check := func(t *testing.T, result *android.TestResult, variant string) { + asanVariant := variant + "_asan" + sharedVariant := variant + "_shared" + sharedAsanVariant := sharedVariant + "_asan" + staticVariant := variant + "_static" + staticAsanVariant := staticVariant + "_asan" + + // The binaries, one with asan and one without + binWithAsan := result.ModuleForTests("bin_with_asan", asanVariant) + binNoAsan := result.ModuleForTests("bin_no_asan", variant) + + // Shared libraries that don't request asan + libShared := result.ModuleForTests("libshared", sharedVariant) + libTransitive := result.ModuleForTests("libtransitive", sharedVariant) + + // Shared library that requests asan + libAsan := result.ModuleForTests("libasan", sharedAsanVariant) + + // Static library that uses an asan variant for bin_with_asan and a non-asan variant + // for bin_no_asan. + libStaticAsanVariant := result.ModuleForTests("libstatic", staticAsanVariant) + libStaticNoAsanVariant := result.ModuleForTests("libstatic", staticVariant) + + // Static library that never uses asan. + libNoAsan := result.ModuleForTests("libnoasan", staticVariant) + + // expectSharedLinkDep verifies that the from module links against the to module as a + // shared library. + expectSharedLinkDep := func(from, to android.TestingModule) { + t.Helper() + fromLink := from.Description("link") + toLink := to.Description("strip") + + if g, w := fromLink.OrderOnly.Strings(), toLink.Output.String(); !android.InList(w, g) { + t.Errorf("%s should link against %s, expected %q, got %q", + from.Module(), to.Module(), w, g) + } + } + + // expectStaticLinkDep verifies that the from module links against the to module as a + // static library. + expectStaticLinkDep := func(from, to android.TestingModule) { + t.Helper() + fromLink := from.Description("link") + toLink := to.Description("static link") + + if g, w := fromLink.Implicits.Strings(), toLink.Output.String(); !android.InList(w, g) { + t.Errorf("%s should link against %s, expected %q, got %q", + from.Module(), to.Module(), w, g) + } + + } + + // expectInstallDep verifies that the install rule of the from module depends on the + // install rule of the to module. + expectInstallDep := func(from, to android.TestingModule) { + t.Helper() + fromInstalled := from.Description("install") + toInstalled := to.Description("install") + + // combine implicits and order-only dependencies, host uses implicit but device uses + // order-only. + got := append(fromInstalled.Implicits.Strings(), fromInstalled.OrderOnly.Strings()...) + want := toInstalled.Output.String() + if !android.InList(want, got) { + t.Errorf("%s installation should depend on %s, expected %q, got %q", + from.Module(), to.Module(), want, got) + } + } + + expectSharedLinkDep(binWithAsan, libShared) + expectSharedLinkDep(binWithAsan, libAsan) + expectSharedLinkDep(libShared, libTransitive) + expectSharedLinkDep(libAsan, libTransitive) + + expectStaticLinkDep(binWithAsan, libStaticAsanVariant) + expectStaticLinkDep(binWithAsan, libNoAsan) + + expectInstallDep(binWithAsan, libShared) + expectInstallDep(binWithAsan, libAsan) + expectInstallDep(binWithAsan, libTransitive) + expectInstallDep(libShared, libTransitive) + expectInstallDep(libAsan, libTransitive) + + expectSharedLinkDep(binNoAsan, libShared) + expectSharedLinkDep(binNoAsan, libAsan) + expectSharedLinkDep(libShared, libTransitive) + expectSharedLinkDep(libAsan, libTransitive) + + expectStaticLinkDep(binNoAsan, libStaticNoAsanVariant) + expectStaticLinkDep(binNoAsan, libNoAsan) + + expectInstallDep(binNoAsan, libShared) + expectInstallDep(binNoAsan, libAsan) + expectInstallDep(binNoAsan, libTransitive) + expectInstallDep(libShared, libTransitive) + expectInstallDep(libAsan, libTransitive) + } + + t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) }) + t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") }) +} diff --git a/dexpreopt/config.go b/dexpreopt/config.go index 7e73bf71d..0bcec17b5 100644 --- a/dexpreopt/config.go +++ b/dexpreopt/config.go @@ -372,6 +372,15 @@ func (d dex2oatDependencyTag) ExcludeFromVisibilityEnforcement() { func (d dex2oatDependencyTag) ExcludeFromApexContents() { } +func (d dex2oatDependencyTag) AllowDisabledModuleDependency(target android.Module) bool { + // RegisterToolDeps may run after the prebuilt mutators and hence register a + // dependency on the source module even when the prebuilt is to be used. + // dex2oatPathFromDep takes that into account when it retrieves the path to + // the binary, but we also need to disable the check for dependencies on + // disabled modules. + return target.IsReplacedByPrebuilt() +} + // Dex2oatDepTag represents the dependency onto the dex2oatd module. It is added to any module that // needs dexpreopting and so it makes no sense for it to be checked for visibility or included in // the apex. @@ -379,6 +388,7 @@ var Dex2oatDepTag = dex2oatDependencyTag{} var _ android.ExcludeFromVisibilityEnforcementTag = Dex2oatDepTag var _ android.ExcludeFromApexContentsTag = Dex2oatDepTag +var _ android.AllowDisabledModuleDependency = Dex2oatDepTag // RegisterToolDeps adds the necessary dependencies to binary modules for tools // that are required later when Get(Cached)GlobalSoongConfig is called. It diff --git a/java/base.go b/java/base.go index 03652be05..f7989b8a2 100644 --- a/java/base.go +++ b/java/base.go @@ -229,12 +229,6 @@ type DeviceProperties struct { // otherwise provides defaults libraries to add to the bootclasspath. System_modules *string - // The name of the module as used in build configuration. - // - // Allows a library to separate its actual name from the name used in - // build configuration, e.g.ctx.Config().BootJars(). - ConfigurationName *string `blueprint:"mutated"` - // set the name of the output Stem *string @@ -895,8 +889,8 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { kotlincFlags := j.properties.Kotlincflags CheckKotlincFlags(ctx, kotlincFlags) - // Dogfood the JVM_IR backend. - kotlincFlags = append(kotlincFlags, "-Xuse-ir") + // Workaround for KT-46512 + kotlincFlags = append(kotlincFlags, "-Xsam-conversions=class") // If there are kotlin files, compile them first but pass all the kotlin and java files // kotlinc will use the java files to resolve types referenced by the kotlin files, but @@ -1223,12 +1217,6 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { return } - // Update hidden API paths. - j.hiddenAPIUpdatePaths(ctx, dexOutputFile, j.implementationJarFile) - - // Encode hidden API flags in dex file. - dexOutputFile = j.hiddenAPIEncodeDex(ctx, dexOutputFile, proptools.Bool(j.dexProperties.Uncompress_dex)) - // merge dex jar with resources if necessary if j.resourceJar != nil { jars := android.Paths{dexOutputFile, j.resourceJar} @@ -1244,6 +1232,12 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { } } + // Initialize the hiddenapi structure. + j.initHiddenAPI(ctx, dexOutputFile, j.implementationJarFile, j.dexProperties.Uncompress_dex) + + // Encode hidden API flags in dex file, if needed. + dexOutputFile = j.hiddenAPIEncodeDex(ctx, dexOutputFile) + j.dexJarFile = dexOutputFile // Dexpreopting @@ -1501,15 +1495,6 @@ func (j *Module) Stem() string { return proptools.StringDefault(j.deviceProperties.Stem, j.Name()) } -// ConfigurationName returns the name of the module as used in build configuration. -// -// This is usually the same as BaseModuleName() except for the <x>.impl libraries created by -// java_sdk_library in which case this is the BaseModuleName() without the ".impl" suffix, -// i.e. just <x>. -func (j *Module) ConfigurationName() string { - return proptools.StringDefault(j.deviceProperties.ConfigurationName, j.BaseModuleName()) -} - func (j *Module) JacocoReportClassesFile() android.Path { return j.jacocoReportClassesFile } diff --git a/java/boot_jars.go b/java/boot_jars.go index 1fb3deb0a..7abda8031 100644 --- a/java/boot_jars.go +++ b/java/boot_jars.go @@ -89,7 +89,7 @@ func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) { name := android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName(module)) if apex, ok := moduleToApex[name]; ok { apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo) - if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApexByBaseName(apex) { + if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApexModule(apex) { // The module name/apex variant should be unique in the system but double check // just in case something has gone wrong. if existing, ok := nameToApexVariant[name]; ok { diff --git a/java/bootclasspath.go b/java/bootclasspath.go index 02833ab66..634959a7e 100644 --- a/java/bootclasspath.go +++ b/java/bootclasspath.go @@ -29,7 +29,7 @@ func init() { func registerBootclasspathBuildComponents(ctx android.RegistrationContext) { ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) { - ctx.BottomUp("bootclasspath_deps", bootclasspathDepsMutator) + ctx.BottomUp("bootclasspath_deps", bootclasspathDepsMutator).Parallel() }) } @@ -95,6 +95,15 @@ func addDependencyOntoApexModulePair(ctx android.BottomUpMutatorContext, apex st if ctx.OtherModuleDependencyVariantExists(variations, prebuiltName) { ctx.AddVariationDependencies(variations, tag, prebuiltName) addedDep = true + } else if ctx.Config().AlwaysUsePrebuiltSdks() && len(variations) > 0 { + // TODO(b/179354495): Remove this code path once the Android build has been fully migrated to + // use bootclasspath_fragment properly. + // Some prebuilt java_sdk_library modules do not yet have an APEX variations so try and add a + // dependency on the non-APEX variant. + if ctx.OtherModuleDependencyVariantExists(nil, prebuiltName) { + ctx.AddVariationDependencies(nil, tag, prebuiltName) + addedDep = true + } } // If no appropriate variant existing for this, so no dependency could be added, then it is an diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index 16aa5e2ea..6b395fbf0 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -112,6 +112,11 @@ type bootclasspathFragmentProperties struct { Coverage BootclasspathFragmentCoverageAffectedProperties Hidden_api HiddenAPIFlagFileProperties + + // Properties that allow a fragment to depend on other fragments. This is needed for hidden API + // processing as it needs access to all the classes used by a fragment including those provided + // by other fragments. + BootclasspathFragmentsDepsProperties } type BootclasspathFragmentModule struct { @@ -263,27 +268,15 @@ var BootclasspathFragmentApexContentInfoProvider = blueprint.NewProvider(Bootcla // BootclasspathFragmentApexContentInfo contains the bootclasspath_fragments contributions to the // apex contents. type BootclasspathFragmentApexContentInfo struct { - // ClasspathFragmentProtoOutput is an output path for the generated classpaths.proto config of this module. - // - // The file should be copied to a relevant place on device, see ClasspathFragmentProtoInstallDir - // for more details. - ClasspathFragmentProtoOutput android.OutputPath - - // ClasspathFragmentProtoInstallDir contains information about on device location for the generated classpaths.proto file. - // - // The path encodes expected sub-location within partitions, i.e. etc/classpaths/<proto-file>, - // for ClasspathFragmentProtoOutput. To get sub-location, instead of the full output / make path - // use android.InstallPath#Rel(). - // - // This is only relevant for APEX modules as they perform their own installation; while regular - // system files are installed via ClasspathFragmentBase#androidMkEntries(). - ClasspathFragmentProtoInstallDir android.InstallPath - // The image config, internal to this module (and the dex_bootjars singleton). // // Will be nil if the BootclasspathFragmentApexContentInfo has not been provided for a specific module. That can occur // when SkipDexpreoptBootJars(ctx) returns true. imageConfig *bootImageConfig + + // Map from the name of the context module (as returned by Name()) to the hidden API encoded dex + // jar path. + contentModuleDexJarPaths map[string]android.Path } func (i BootclasspathFragmentApexContentInfo) Modules() android.ConfiguredJarList { @@ -310,10 +303,14 @@ func (i BootclasspathFragmentApexContentInfo) AndroidBootImageFilesByArchType() // DexBootJarPathForContentModule returns the path to the dex boot jar for specified module. // // The dex boot jar is one which has had hidden API encoding performed on it. -func (i BootclasspathFragmentApexContentInfo) DexBootJarPathForContentModule(module android.Module) android.Path { - j := module.(UsesLibraryDependency) - dexJar := j.DexJarBuildPath() - return dexJar +func (i BootclasspathFragmentApexContentInfo) DexBootJarPathForContentModule(module android.Module) (android.Path, error) { + name := module.Name() + if dexJar, ok := i.contentModuleDexJarPaths[name]; ok { + return dexJar, nil + } else { + return nil, fmt.Errorf("unknown bootclasspath_fragment content module %s, expected one of %s", + name, strings.Join(android.SortedStringKeys(i.contentModuleDexJarPaths), ", ")) + } } func (b *BootclasspathFragmentModule) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool { @@ -391,27 +388,52 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo // Perform hidden API processing. b.generateHiddenAPIBuildActions(ctx, contents) - // Construct the boot image info from the config. + // Verify that the image_name specified on a bootclasspath_fragment is valid even if this is a + // prebuilt which will not use the image config. + imageConfig := b.getImageConfig(ctx) + + // A prebuilt fragment cannot contribute to the apex. + if !android.IsModulePrebuilt(ctx.Module()) { + // Provide the apex content info. + b.provideApexContentInfo(ctx, imageConfig, contents) + } +} + +// provideApexContentInfo creates, initializes and stores the apex content info for use by other +// modules. +func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) { + // Construct the apex content info from the config. info := BootclasspathFragmentApexContentInfo{ - ClasspathFragmentProtoInstallDir: b.classpathFragmentBase().installDirPath, - ClasspathFragmentProtoOutput: b.classpathFragmentBase().outputFilepath, - imageConfig: nil, + imageConfig: imageConfig, } + // Populate the apex content info with paths to the dex jars. + b.populateApexContentInfoDexJars(ctx, &info, contents) + if !SkipDexpreoptBootJars(ctx) { // Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars // GenerateSingletonBuildActions method as it cannot create it for itself. dexpreopt.GetGlobalSoongConfig(ctx) - info.imageConfig = b.getImageConfig(ctx) // Only generate the boot image if the configuration does not skip it. b.generateBootImageBuildActions(ctx, contents) } - // Make it available for other modules. + // Make the apex content info available for other modules. ctx.SetProvider(BootclasspathFragmentApexContentInfoProvider, info) } +// populateApexContentInfoDexJars adds paths to the dex jars provided by this fragment to the +// apex content info. +func (b *BootclasspathFragmentModule) populateApexContentInfoDexJars(ctx android.ModuleContext, info *BootclasspathFragmentApexContentInfo, contents []android.Module) { + info.contentModuleDexJarPaths = map[string]android.Path{} + for _, m := range contents { + j := m.(UsesLibraryDependency) + dexJar := j.DexJarBuildPath() + info.contentModuleDexJarPaths[m.Name()] = dexJar + } +} + // generateClasspathProtoBuildActions generates all required build actions for classpath.proto config func (b *BootclasspathFragmentModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) { var classpathJars []classpathJar @@ -448,12 +470,54 @@ func (b *BootclasspathFragmentModule) getImageConfig(ctx android.EarlyModuleCont return imageConfig } +// canPerformHiddenAPIProcessing determines whether hidden API processing should be performed. +// +// A temporary workaround to avoid existing bootclasspath_fragments that do not provide the +// appropriate information needed for hidden API processing breaking the build. +// TODO(b/179354495): Remove this workaround. +func (b *BootclasspathFragmentModule) canPerformHiddenAPIProcessing(ctx android.ModuleContext) bool { + // Hidden API processing is always enabled in tests. + if ctx.Config().TestProductVariables != nil { + return true + } + // A module that has fragments should have access to the information it needs in order to perform + // hidden API processing. + if len(b.properties.Fragments) != 0 { + return true + } + + // The art bootclasspath fragment does not depend on any other fragments but already supports + // hidden API processing. + imageName := proptools.String(b.properties.Image_name) + if imageName == "art" { + return true + } + + // Disable it for everything else. + return false +} + // generateHiddenAPIBuildActions generates all the hidden API related build rules. func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module) { + // A temporary workaround to avoid existing bootclasspath_fragments that do not provide the + // appropriate information needed for hidden API processing breaking the build. + if !b.canPerformHiddenAPIProcessing(ctx) { + // Nothing to do. + return + } + // Convert the kind specific lists of modules into kind specific lists of jars. stubJarsByKind := hiddenAPIGatherStubLibDexJarPaths(ctx, contents) + // Performing hidden API processing without stubs is not supported and it is unlikely to ever be + // required as the whole point of adding something to the bootclasspath fragment is to add it to + // the bootclasspath in order to be used by something else in the system. Without any stubs it + // cannot do that. + if len(stubJarsByKind) == 0 { + return + } + // Store the information for use by other modules. bootclasspathApiInfo := bootclasspathApiInfo{stubJarsByKind: stubJarsByKind} ctx.SetProvider(bootclasspathApiInfoProvider, bootclasspathApiInfo) @@ -474,15 +538,6 @@ func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android. // produceHiddenAPIAllFlagsFile produces the hidden API all-flags.csv file (and supporting files) // for the fragment. func (b *BootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, stubJarsByKind map[android.SdkKind]android.Paths, flagFileInfo *hiddenAPIFlagFileInfo) { - // If no stubs have been provided then don't perform hidden API processing. This is a temporary - // workaround to avoid existing bootclasspath_fragments that do not provide stubs breaking the - // build. - // TODO(b/179354495): Remove this workaround. - if len(stubJarsByKind) == 0 { - // Nothing to do. - return - } - // Generate the rules to create the hidden API flags and update the supplied flagFileInfo with the // paths to the created files. hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx, contents, stubJarsByKind, flagFileInfo) diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go index 7422fa2d5..bc0416a47 100644 --- a/java/classpath_fragment.go +++ b/java/classpath_fragment.go @@ -18,6 +18,7 @@ package java import ( "fmt" + "github.com/google/blueprint" "strings" "android/soong/android" @@ -120,6 +121,12 @@ func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.M FlagWithOutput("--output=", c.outputFilepath) rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String()) + + classpathProtoInfo := ClasspathFragmentProtoContentInfo{ + ClasspathFragmentProtoInstallDir: c.installDirPath, + ClasspathFragmentProtoOutput: c.outputFilepath, + } + ctx.SetProvider(ClasspathFragmentProtoContentInfoProvider, classpathProtoInfo) } func writeClasspathsJson(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) { @@ -129,7 +136,7 @@ func writeClasspathsJson(ctx android.ModuleContext, output android.WritablePath, for idx, jar := range jars { fmt.Fprintf(&content, "{\n") - fmt.Fprintf(&content, "\"relativePath\": \"%s\",\n", jar.path) + fmt.Fprintf(&content, "\"path\": \"%s\",\n", jar.path) fmt.Fprintf(&content, "\"classpath\": \"%s\"\n", jar.classpath) if idx < len(jars)-1 { @@ -157,3 +164,23 @@ func (c *ClasspathFragmentBase) androidMkEntries() []android.AndroidMkEntries { }, }} } + +var ClasspathFragmentProtoContentInfoProvider = blueprint.NewProvider(ClasspathFragmentProtoContentInfo{}) + +type ClasspathFragmentProtoContentInfo struct { + // ClasspathFragmentProtoOutput is an output path for the generated classpaths.proto config of this module. + // + // The file should be copied to a relevant place on device, see ClasspathFragmentProtoInstallDir + // for more details. + ClasspathFragmentProtoOutput android.OutputPath + + // ClasspathFragmentProtoInstallDir contains information about on device location for the generated classpaths.proto file. + // + // The path encodes expected sub-location within partitions, i.e. etc/classpaths/<proto-file>, + // for ClasspathFragmentProtoOutput. To get sub-location, instead of the full output / make path + // use android.InstallPath#Rel(). + // + // This is only relevant for APEX modules as they perform their own installation; while regular + // system files are installed via ClasspathFragmentBase#androidMkEntries(). + ClasspathFragmentProtoInstallDir android.InstallPath +} diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index be202c0db..e1a36507a 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -766,7 +766,7 @@ func generateUpdatableBcpPackagesRule(ctx android.ModuleContext, image *bootImag if len(pp) > 0 { updatablePackages = append(updatablePackages, pp...) } else { - ctx.ModuleErrorf("Missing permitted_packages") + ctx.OtherModuleErrorf(module, "Missing permitted_packages") } } } @@ -803,8 +803,7 @@ func dumpOatRules(ctx android.ModuleContext, image *bootImageConfig) { rule := android.NewRuleBuilder(pctx, ctx) imageLocationsOnHost, _ := image.imageLocations() rule.Command(). - // TODO: for now, use the debug version for better error reporting - BuiltTool("oatdumpd"). + BuiltTool("oatdump"). FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPathsDeps.Paths(), ":"). FlagWithList("--runtime-arg -Xbootclasspath-locations:", image.dexLocationsDeps, ":"). FlagWithArg("--image=", strings.Join(imageLocationsOnHost, ":")).Implicits(image.imagesDeps.Paths()). diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go index a9e0773b7..f065534d5 100644 --- a/java/dexpreopt_test.go +++ b/java/dexpreopt_test.go @@ -15,7 +15,12 @@ package java import ( + "fmt" "testing" + + "android/soong/android" + "android/soong/cc" + "android/soong/dexpreopt" ) func TestDexpreoptEnabled(t *testing.T) { @@ -166,3 +171,46 @@ func enabledString(enabled bool) string { return "disabled" } } + +func TestDex2oatToolDeps(t *testing.T) { + preparers := android.GroupFixturePreparers( + cc.PrepareForTestWithCcDefaultModules, + PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd, + dexpreopt.PrepareForTestByEnablingDexpreopt) + + testDex2oatToolDep := func(sourceEnabled, prebuiltEnabled, prebuiltPreferred bool, + expectedDex2oatPath string) { + name := fmt.Sprintf("sourceEnabled:%t,prebuiltEnabled:%t,prebuiltPreferred:%t", + sourceEnabled, prebuiltEnabled, prebuiltPreferred) + t.Run(name, func(t *testing.T) { + result := preparers.RunTestWithBp(t, fmt.Sprintf(` + cc_binary { + name: "dex2oatd", + enabled: %t, + host_supported: true, + } + cc_prebuilt_binary { + name: "dex2oatd", + enabled: %t, + prefer: %t, + host_supported: true, + srcs: ["x86_64/bin/dex2oatd"], + } + java_library { + name: "myjavalib", + } + `, sourceEnabled, prebuiltEnabled, prebuiltPreferred)) + pathContext := android.PathContextForTesting(result.Config) + dex2oatPath := dexpreopt.GetCachedGlobalSoongConfig(pathContext).Dex2oat + android.AssertStringEquals(t, "Testing "+name, expectedDex2oatPath, android.NormalizePathForTesting(dex2oatPath)) + }) + } + + sourceDex2oatPath := "host/linux-x86/bin/dex2oatd" + prebuiltDex2oatPath := ".intermediates/prebuilt_dex2oatd/linux_glibc_x86_64/dex2oatd" + + testDex2oatToolDep(true, false, false, sourceDex2oatPath) + testDex2oatToolDep(true, true, false, sourceDex2oatPath) + testDex2oatToolDep(true, true, true, prebuiltDex2oatPath) + testDex2oatToolDep(false, true, false, prebuiltDex2oatPath) +} diff --git a/java/hiddenapi.go b/java/hiddenapi.go index 829c47383..c9e3c29a3 100644 --- a/java/hiddenapi.go +++ b/java/hiddenapi.go @@ -26,27 +26,10 @@ var hiddenAPIGenerateCSVRule = pctx.AndroidStaticRule("hiddenAPIGenerateCSV", bl }, "outFlag", "stubAPIFlags") type hiddenAPI struct { - // The name of the module as it would be used in the boot jars configuration, e.g. without any - // prebuilt_ prefix (if it is a prebuilt) and without any ".impl" suffix if it is a - // java_sdk_library implementation library. - configurationName string - // True if the module containing this structure contributes to the hiddenapi information or has // that information encoded within it. active bool - // Identifies the active module variant which will be used as the source of hiddenapi information. - // - // A class may be compiled into a number of different module variants each of which will need the - // hiddenapi information encoded into it and so will be marked as active. However, only one of - // them must be used as a source of information by hiddenapi otherwise it will end up with - // duplicate entries. That module will have primary=true. - // - // Note, that modules <x>-hiddenapi that provide additional annotation information for module <x> - // that is on the bootclasspath are marked as primary=true as they are the primary source of that - // annotation information. - primary bool - // The path to the dex jar that is in the boot class path. If this is nil then the associated // module is not a boot jar, but could be one of the <x>-hiddenapi modules that provide additional // annotations for the <x> boot dex jar but which do not actually provide a boot dex jar @@ -60,6 +43,10 @@ type hiddenAPI struct { // the UnsupportedAppUsage annotation that need to be extracted as part of the hidden API // processing. classesJarPaths android.Paths + + // The compressed state of the dex file being encoded. This is used to ensure that the encoded + // dex file has the same state. + uncompressDexState *bool } func (h *hiddenAPI) bootDexJar() android.Path { @@ -70,6 +57,10 @@ func (h *hiddenAPI) classesJars() android.Paths { return h.classesJarPaths } +func (h *hiddenAPI) uncompressDex() *bool { + return h.uncompressDexState +} + // hiddenAPIModule is the interface a module that embeds the hiddenAPI structure must implement. type hiddenAPIModule interface { android.Module @@ -79,18 +70,39 @@ type hiddenAPIModule interface { type hiddenAPIIntf interface { bootDexJar() android.Path classesJars() android.Paths + uncompressDex() *bool } var _ hiddenAPIIntf = (*hiddenAPI)(nil) // Initialize the hiddenapi structure -func (h *hiddenAPI) initHiddenAPI(ctx android.BaseModuleContext, configurationName string) { +// +// uncompressedDexState should be nil when the module is a prebuilt and so does not require hidden +// API encoding. +func (h *hiddenAPI) initHiddenAPI(ctx android.ModuleContext, dexJar, classesJar android.Path, uncompressedDexState *bool) { + + // Save the classes jars even if this is not active as they may be used by modular hidden API + // processing. + classesJars := android.Paths{classesJar} + ctx.VisitDirectDepsWithTag(hiddenApiAnnotationsTag, func(dep android.Module) { + javaInfo := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo) + classesJars = append(classesJars, javaInfo.ImplementationJars...) + }) + h.classesJarPaths = classesJars + + // Save the unencoded dex jar so it can be used when generating the + // hiddenAPISingletonPathsStruct.stubFlags file. + h.bootDexJarPath = dexJar + + h.uncompressDexState = uncompressedDexState + // If hiddenapi processing is disabled treat this as inactive. if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") { return } - h.configurationName = configurationName + // The context module must implement hiddenAPIModule. + module := ctx.Module().(hiddenAPIModule) // If the frameworks/base directories does not exist and no prebuilt hidden API flag files have // been configured then it is not possible to do hidden API encoding. @@ -102,52 +114,7 @@ func (h *hiddenAPI) initHiddenAPI(ctx android.BaseModuleContext, configurationNa // on the boot jars list because the runtime only enforces access to the hidden API for the // bootclassloader. If information is gathered for modules not on the list then that will cause // failures in the CtsHiddenApiBlocklist... tests. - module := ctx.Module() h.active = isModuleInBootClassPath(ctx, module) - if !h.active { - // The rest of the properties will be ignored if active is false. - return - } - - // Determine whether this module is the primary module or not. - primary := true - - // A prebuilt module is only primary if it is preferred and conversely a source module is only - // primary if it has not been replaced by a prebuilt module. - if pi, ok := module.(android.PrebuiltInterface); ok { - if p := pi.Prebuilt(); p != nil { - primary = p.UsePrebuilt() - } - } else { - // The only module that will pass a different configurationName to its module name to this - // method is the implementation library of a java_sdk_library. It has a configuration name of - // <x> the same as its parent java_sdk_library but a module name of <x>.impl. It is not the - // primary module, the java_sdk_library with the name of <x> is. - primary = configurationName == ctx.ModuleName() - - // A source module that has been replaced by a prebuilt can never be the primary module. - if module.IsReplacedByPrebuilt() { - if ctx.HasProvider(android.ApexInfoProvider) { - // The source module is in an APEX but the prebuilt module on which it depends is not in an - // APEX and so is not the one that will actually be used for hidden API processing. That - // means it is not possible to check to see if it is a suitable replacement so just assume - // that it is. - primary = false - } else { - ctx.VisitDirectDepsWithTag(android.PrebuiltDepTag, func(prebuilt android.Module) { - if h, ok := prebuilt.(hiddenAPIIntf); ok && h.bootDexJar() != nil { - primary = false - } else { - ctx.ModuleErrorf( - "hiddenapi has determined that the source module %q should be ignored as it has been"+ - " replaced by the prebuilt module %q but unfortunately it does not provide a"+ - " suitable boot dex jar", ctx.ModuleName(), ctx.OtherModuleName(prebuilt)) - } - }) - } - } - } - h.primary = primary } func isModuleInBootClassPath(ctx android.BaseModuleContext, module android.Module) bool { @@ -166,13 +133,19 @@ func isModuleInBootClassPath(ctx android.BaseModuleContext, module android.Modul // // Otherwise, it creates a copy of the supplied dex file into which it has encoded the hiddenapi // flags and returns this instead of the supplied dex jar. -func (h *hiddenAPI) hiddenAPIEncodeDex(ctx android.ModuleContext, dexJar android.OutputPath, uncompressDex bool) android.OutputPath { +func (h *hiddenAPI) hiddenAPIEncodeDex(ctx android.ModuleContext, dexJar android.OutputPath) android.OutputPath { if !h.active { return dexJar } - hiddenAPIJar := android.PathForModuleOut(ctx, "hiddenapi", h.configurationName+".jar").OutputPath + // A nil uncompressDexState prevents the dex file from being encoded. + if h.uncompressDexState == nil { + ctx.ModuleErrorf("cannot encode dex file %s when uncompressDexState is nil", dexJar) + } + uncompressDex := *h.uncompressDexState + + hiddenAPIJar := android.PathForModuleOut(ctx, "hiddenapi", dexJar.Base()).OutputPath // Create a copy of the dex jar which has been encoded with hiddenapi flags. hiddenAPIEncodeDex(ctx, hiddenAPIJar, dexJar, uncompressDex) @@ -183,27 +156,6 @@ func (h *hiddenAPI) hiddenAPIEncodeDex(ctx android.ModuleContext, dexJar android return dexJar } -// hiddenAPIUpdatePaths generates ninja rules to extract the information from the classes -// jar, and outputs it to the appropriate module specific CSV file. -// -// It also makes the dex jar available for use when generating the -// hiddenAPISingletonPathsStruct.stubFlags. -func (h *hiddenAPI) hiddenAPIUpdatePaths(ctx android.ModuleContext, dexJar, classesJar android.Path) { - - // Save the classes jars even if this is not active as they may be used by modular hidden API - // processing. - classesJars := android.Paths{classesJar} - ctx.VisitDirectDepsWithTag(hiddenApiAnnotationsTag, func(dep android.Module) { - javaInfo := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo) - classesJars = append(classesJars, javaInfo.ImplementationJars...) - }) - h.classesJarPaths = classesJars - - // Save the unencoded dex jar so it can be used when generating the - // hiddenAPISingletonPathsStruct.stubFlags file. - h.bootDexJarPath = dexJar -} - // buildRuleToGenerateAnnotationFlags builds a ninja rule to generate the annotation-flags.csv file // from the classes jars and stub-flags.csv files. // @@ -283,7 +235,7 @@ var hiddenAPIEncodeDexRule = pctx.AndroidStaticRule("hiddenAPIEncodeDex", bluepr echo "--output-dex=$tmpDir/dex-output/$$(basename $${INPUT_DEX})"; done | xargs ${config.HiddenAPI} encode --api-flags=$flagsCsv $hiddenapiFlags && ${config.SoongZipCmd} $soongZipFlags -o $tmpDir/dex.jar -C $tmpDir/dex-output -f "$tmpDir/dex-output/classes*.dex" && - ${config.MergeZipsCmd} -D -zipToNotStrip $tmpDir/dex.jar -stripFile "classes*.dex" -stripFile "**/*.uau" $out $tmpDir/dex.jar $in`, + ${config.MergeZipsCmd} -j -D -zipToNotStrip $tmpDir/dex.jar -stripFile "classes*.dex" -stripFile "**/*.uau" $out $tmpDir/dex.jar $in`, CommandDeps: []string{ "${config.HiddenAPI}", "${config.SoongZipCmd}", diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go index 2dceb6535..f5afe5d5b 100644 --- a/java/hiddenapi_modular.go +++ b/java/hiddenapi_modular.go @@ -15,6 +15,7 @@ package java import ( + "fmt" "strings" "android/soong/android" @@ -560,7 +561,25 @@ func extractBootDexJarsFromHiddenAPIModules(ctx android.ModuleContext, contents for _, module := range contents { bootDexJar := module.bootDexJar() if bootDexJar == nil { - ctx.ModuleErrorf("module %s does not provide a dex jar", module) + if ctx.Config().AlwaysUsePrebuiltSdks() { + // TODO(b/179354495): Remove this work around when it is unnecessary. + // Prebuilt modules like framework-wifi do not yet provide dex implementation jars. So, + // create a fake one that will cause a build error only if it is used. + fake := android.PathForModuleOut(ctx, "fake/boot-dex/%s.jar", module.Name()) + + // Create an error rule that pretends to create the output file but will actually fail if it + // is run. + ctx.Build(pctx, android.BuildParams{ + Rule: android.ErrorRule, + Output: fake, + Args: map[string]string{ + "error": fmt.Sprintf("missing dependencies: boot dex jar for %s", module), + }, + }) + bootDexJars = append(bootDexJars, fake) + } else { + ctx.ModuleErrorf("module %s does not provide a dex jar", module) + } } else { bootDexJars = append(bootDexJars, bootDexJar) } diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go index 848aa59bc..bdf055abc 100644 --- a/java/hiddenapi_singleton.go +++ b/java/hiddenapi_singleton.go @@ -167,11 +167,11 @@ func isModuleInConfiguredList(ctx android.BaseModuleContext, module android.Modu // Now match the apex part of the boot image configuration. requiredApex := configuredBootJars.Apex(index) if requiredApex == "platform" || requiredApex == "system_ext" { - if len(apexInfo.InApexes) != 0 { + if len(apexInfo.InApexVariants) != 0 { // A platform variant is required but this is for an apex so ignore it. return false } - } else if !apexInfo.InApexByBaseName(requiredApex) { + } else if !apexInfo.InApexVariantByBaseName(requiredApex) { // An apex variant for a specific apex is required but this is the wrong apex. return false } diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go index e6b45ac20..dcd363c2c 100644 --- a/java/hiddenapi_singleton_test.go +++ b/java/hiddenapi_singleton_test.go @@ -274,3 +274,56 @@ func TestHiddenAPISingletonWithPrebuiltCsvFile(t *testing.T) { android.AssertStringEquals(t, "hiddenapi encode dex rule flags csv", expectedFlagsCsv, actualFlagsCsv) } + +func TestHiddenAPIEncoding_JavaSdkLibrary(t *testing.T) { + + result := android.GroupFixturePreparers( + hiddenApiFixtureFactory, + FixtureConfigureBootJars("platform:foo"), + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis("foo"), + + // Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding + // is disabled. + android.FixtureAddTextFile("frameworks/base/Android.bp", ""), + ).RunTestWithBp(t, ` + java_sdk_library { + name: "foo", + srcs: ["a.java"], + shared_library: false, + compile_dex: true, + public: {enabled: true}, + } + `) + + checkDexEncoded := func(t *testing.T, name, unencodedDexJar, encodedDexJar string) { + moduleForTests := result.ModuleForTests(name, "android_common") + + encodeDexRule := moduleForTests.Rule("hiddenAPIEncodeDex") + actualUnencodedDexJar := encodeDexRule.Input + + // Make sure that the module has its dex jar encoded. + android.AssertStringEquals(t, "encode embedded java_library", unencodedDexJar, actualUnencodedDexJar.String()) + + // Make sure that the encoded dex jar is the exported one. + exportedDexJar := moduleForTests.Module().(UsesLibraryDependency).DexJarBuildPath() + android.AssertPathRelativeToTopEquals(t, "encode embedded java_library", encodedDexJar, exportedDexJar) + } + + // The java_library embedded with the java_sdk_library must be dex encoded. + t.Run("foo", func(t *testing.T) { + expectedUnencodedDexJar := "out/soong/.intermediates/foo/android_common/aligned/foo.jar" + expectedEncodedDexJar := "out/soong/.intermediates/foo/android_common/hiddenapi/foo.jar" + checkDexEncoded(t, "foo", expectedUnencodedDexJar, expectedEncodedDexJar) + }) + + // The dex jar of the child implementation java_library of the java_sdk_library is not currently + // dex encoded. + t.Run("foo.impl", func(t *testing.T) { + fooImpl := result.ModuleForTests("foo.impl", "android_common") + encodeDexRule := fooImpl.MaybeRule("hiddenAPIEncodeDex") + if encodeDexRule.Rule != nil { + t.Errorf("foo.impl is not expected to be encoded") + } + }) +} diff --git a/java/java.go b/java/java.go index f85de3d77..2bbb5b102 100644 --- a/java/java.go +++ b/java/java.go @@ -57,6 +57,10 @@ func registerJavaBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("java_host_for_device", HostForDeviceFactory) ctx.RegisterModuleType("dex_import", DexImportFactory) + // This mutator registers dependencies on dex2oat for modules that should be + // dexpreopted. This is done late when the final variants have been + // established, to not get the dependencies split into the wrong variants and + // to support the checks in dexpreoptDisabled(). ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) { ctx.BottomUp("dexpreopt_tool_deps", dexpreoptToolDepsMutator).Parallel() }) @@ -481,11 +485,6 @@ func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bo } func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { - // Initialize the hiddenapi structure. Pass in the configuration name rather than the module name - // so the hidden api will encode the <x>.impl java_ library created by java_sdk_library just as it - // would the <x> library if <x> was configured as a boot jar. - j.initHiddenAPI(ctx, j.ConfigurationName()) - j.sdkVersion = j.SdkVersion(ctx) j.minSdkVersion = j.MinSdkVersion(ctx) @@ -1241,9 +1240,6 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.sdkVersion = j.SdkVersion(ctx) j.minSdkVersion = j.MinSdkVersion(ctx) - // Initialize the hiddenapi structure. - j.initHiddenAPI(ctx, j.BaseModuleName()) - if !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform() { j.hideApexVariantFromMake = true } @@ -1315,7 +1311,9 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo) if dexOutputPath := di.PrebuiltExportPath(j.BaseModuleName(), ".dexjar"); dexOutputPath != nil { j.dexJarFile = dexOutputPath - j.hiddenAPIUpdatePaths(ctx, dexOutputPath, outputFile) + + // Initialize the hiddenapi structure. + j.initHiddenAPI(ctx, dexOutputPath, outputFile, nil) } else { // This should never happen as a variant for a prebuilt_apex is only created if the // prebuilt_apex has been configured to export the java library dex file. @@ -1346,11 +1344,11 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { return } - // Update hidden API paths. - j.hiddenAPIUpdatePaths(ctx, dexOutputFile, outputFile) + // Initialize the hiddenapi structure. + j.initHiddenAPI(ctx, dexOutputFile, outputFile, j.dexProperties.Uncompress_dex) // Encode hidden API flags in dex file. - dexOutputFile = j.hiddenAPIEncodeDex(ctx, dexOutputFile, proptools.Bool(j.dexProperties.Uncompress_dex)) + dexOutputFile = j.hiddenAPIEncodeDex(ctx, dexOutputFile) j.dexJarFile = dexOutputFile } diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index c8fafede6..10bf179d2 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -225,7 +225,7 @@ func (b *platformBootclasspathModule) checkNonUpdatableModules(ctx android.Modul fromUpdatableApex := apexInfo.Updatable if fromUpdatableApex { // error: this jar is part of an updatable apex - ctx.ModuleErrorf("module %q from updatable apexes %q is not allowed in the framework boot image", ctx.OtherModuleName(m), apexInfo.InApexes) + ctx.ModuleErrorf("module %q from updatable apexes %q is not allowed in the framework boot image", ctx.OtherModuleName(m), apexInfo.InApexVariants) } else { // ok: this jar is part of the platform or a non-updatable apex } @@ -242,8 +242,15 @@ func (b *platformBootclasspathModule) checkUpdatableModules(ctx android.ModuleCo } else { name := ctx.OtherModuleName(m) if apexInfo.IsForPlatform() { - // error: this jar is part of the platform - ctx.ModuleErrorf("module %q from platform is not allowed in the updatable boot jars list", name) + // If AlwaysUsePrebuiltSdks() returns true then it is possible that the updatable list will + // include platform variants of a prebuilt module due to workarounds elsewhere. In that case + // do not treat this as an error. + // TODO(b/179354495): Always treat this as an error when migration to bootclasspath_fragment + // modules is complete. + if !ctx.Config().AlwaysUsePrebuiltSdks() { + // error: this jar is part of the platform + ctx.ModuleErrorf("module %q from platform is not allowed in the updatable boot jars list", name) + } } else { // TODO(b/177892522): Treat this as an error. // Cannot do that at the moment because framework-wifi and framework-tethering are in the diff --git a/java/sdk_library.go b/java/sdk_library.go index f04f837cc..b5b6232d6 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -1235,16 +1235,13 @@ func childModuleVisibility(childVisibility []string) []string { // Creates the implementation java library func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) { - moduleNamePtr := proptools.StringPtr(module.BaseModuleName()) - visibility := childModuleVisibility(module.sdkLibraryProperties.Impl_library_visibility) props := struct { - Name *string - Visibility []string - Instrument bool - Libs []string - ConfigurationName *string + Name *string + Visibility []string + Instrument bool + Libs []string }{ Name: proptools.StringPtr(module.implLibraryModuleName()), Visibility: visibility, @@ -1253,9 +1250,6 @@ func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) // Set the impl_only libs. Note that the module's "Libs" get appended as well, via the // addition of &module.properties below. Libs: module.sdkLibraryProperties.Impl_only_libs, - - // Make the created library behave as if it had the same name as this module. - ConfigurationName: moduleNamePtr, } properties := []interface{}{ @@ -1552,7 +1546,7 @@ func PrebuiltJars(ctx android.BaseModuleContext, baseName string, s android.SdkS func withinSameApexesAs(ctx android.BaseModuleContext, other android.Module) bool { apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) otherApexInfo := ctx.OtherModuleProvider(other, android.ApexInfoProvider).(android.ApexInfo) - return len(otherApexInfo.InApexes) > 0 && reflect.DeepEqual(apexInfo.InApexes, otherApexInfo.InApexes) + return len(otherApexInfo.InApexVariants) > 0 && reflect.DeepEqual(apexInfo.InApexVariants, otherApexInfo.InApexVariants) } func (module *SdkLibrary) sdkJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec, headerJars bool) android.Paths { @@ -2127,8 +2121,7 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo) if dexOutputPath := di.PrebuiltExportPath(module.BaseModuleName(), ".dexjar"); dexOutputPath != nil { module.dexJarFile = dexOutputPath - module.initHiddenAPI(ctx, module.configurationName) - module.hiddenAPIUpdatePaths(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0]) + module.initHiddenAPI(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil) } else { // This should never happen as a variant for a prebuilt_apex is only created if the // prebuilt_apex has been configured to export the java library dex file. diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go index 82cdb8926..a505c6d01 100644 --- a/java/systemserver_classpath_fragment.go +++ b/java/systemserver_classpath_fragment.go @@ -17,6 +17,7 @@ package java import ( "android/soong/android" "android/soong/dexpreopt" + "github.com/google/blueprint" ) func init() { @@ -24,8 +25,8 @@ func init() { } func registerSystemserverClasspathBuildComponents(ctx android.RegistrationContext) { - // TODO(satayev): add systemserver_classpath_fragment module ctx.RegisterModuleType("platform_systemserverclasspath", platformSystemServerClasspathFactory) + ctx.RegisterModuleType("systemserverclasspath_fragment", systemServerClasspathFactory) } type platformSystemServerClasspathModule struct { @@ -41,18 +42,18 @@ func platformSystemServerClasspathFactory() android.Module { return m } -func (b *platformSystemServerClasspathModule) AndroidMkEntries() (entries []android.AndroidMkEntries) { - return b.classpathFragmentBase().androidMkEntries() +func (p *platformSystemServerClasspathModule) AndroidMkEntries() (entries []android.AndroidMkEntries) { + return p.classpathFragmentBase().androidMkEntries() } -func (b *platformSystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { - configuredJars := configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), b.classpathType) - b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars) +func (p *platformSystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + configuredJars := configuredJarListToClasspathJars(ctx, p.ClasspathFragmentToConfiguredJarList(ctx), p.classpathType) + p.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars) } var platformSystemServerClasspathKey = android.NewOnceKey("platform_systemserverclasspath") -func (b *platformSystemServerClasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList { +func (p *platformSystemServerClasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList { return ctx.Config().Once(platformSystemServerClasspathKey, func() interface{} { global := dexpreopt.GetGlobalConfig(ctx) @@ -65,3 +66,64 @@ func (b *platformSystemServerClasspathModule) ClasspathFragmentToConfiguredJarLi return jars }).(android.ConfiguredJarList) } + +type SystemServerClasspathModule struct { + android.ModuleBase + android.ApexModuleBase + + ClasspathFragmentBase + + properties systemServerClasspathFragmentProperties +} + +func (s *SystemServerClasspathModule) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error { + return nil +} + +type systemServerClasspathFragmentProperties struct { + // The contents of this systemserverclasspath_fragment, could be either java_library, or java_sdk_library. + // + // The order of this list matters as it is the order that is used in the SYSTEMSERVERCLASSPATH. + Contents []string +} + +func systemServerClasspathFactory() android.Module { + m := &SystemServerClasspathModule{} + m.AddProperties(&m.properties) + android.InitApexModule(m) + initClasspathFragment(m, SYSTEMSERVERCLASSPATH) + android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) + return m +} + +func (s *SystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + if len(s.properties.Contents) == 0 { + ctx.PropertyErrorf("contents", "empty contents are not allowed") + } + + s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJarListToClasspathJars(ctx, s.ClasspathFragmentToConfiguredJarList(ctx))) +} + +func (s *SystemServerClasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList { + // TODO(satayev): populate with actual content + return android.EmptyConfiguredJarList() +} + +type systemServerClasspathFragmentContentDependencyTag struct { + blueprint.BaseDependencyTag +} + +// The tag used for the dependency between the systemserverclasspath_fragment module and its contents. +var systemServerClasspathFragmentContentDepTag = systemServerClasspathFragmentContentDependencyTag{} + +func IsSystemServerClasspathFragmentContentDepTag(tag blueprint.DependencyTag) bool { + return tag == systemServerClasspathFragmentContentDepTag +} + +func (s *SystemServerClasspathModule) ComponentDepsMutator(ctx android.BottomUpMutatorContext) { + module := ctx.Module() + + for _, name := range s.properties.Contents { + ctx.AddDependency(module, systemServerClasspathFragmentContentDepTag, name) + } +} diff --git a/java/systemserver_classpath_fragment_test.go b/java/systemserver_classpath_fragment_test.go index 6126d5eb1..5272f271d 100644 --- a/java/systemserver_classpath_fragment_test.go +++ b/java/systemserver_classpath_fragment_test.go @@ -20,13 +20,13 @@ import ( "android/soong/android" ) -var prepareForTestWithSystemserverClasspath = android.GroupFixturePreparers( +var prepareForTestWithSystemServerClasspath = android.GroupFixturePreparers( PrepareForTestWithJavaDefaultModules, ) -func TestSystemserverClasspathVariant(t *testing.T) { +func TestPlatformSystemserverClasspathVariant(t *testing.T) { result := android.GroupFixturePreparers( - prepareForTestWithSystemserverClasspath, + prepareForTestWithSystemServerClasspath, android.FixtureWithRootAndroidBp(` platform_systemserverclasspath { name: "platform-systemserverclasspath", @@ -38,9 +38,9 @@ func TestSystemserverClasspathVariant(t *testing.T) { android.AssertIntEquals(t, "expect 1 variant", 1, len(variants)) } -func TestSystemserverClasspath_ClasspathFragmentPaths(t *testing.T) { +func TestPlatformSystemserverClasspath_ClasspathFragmentPaths(t *testing.T) { result := android.GroupFixturePreparers( - prepareForTestWithSystemserverClasspath, + prepareForTestWithSystemServerClasspath, android.FixtureWithRootAndroidBp(` platform_systemserverclasspath { name: "platform-systemserverclasspath", @@ -53,9 +53,9 @@ func TestSystemserverClasspath_ClasspathFragmentPaths(t *testing.T) { android.AssertPathRelativeToTopEquals(t, "install filepath", "out/soong/target/product/test_device/system/etc/classpaths", p.ClasspathFragmentBase.installDirPath) } -func TestSystemserverClasspathModule_AndroidMkEntries(t *testing.T) { +func TestPlatformSystemserverClasspathModule_AndroidMkEntries(t *testing.T) { preparer := android.GroupFixturePreparers( - prepareForTestWithSystemserverClasspath, + prepareForTestWithSystemServerClasspath, android.FixtureWithRootAndroidBp(` platform_systemserverclasspath { name: "platform-systemserverclasspath", @@ -95,3 +95,14 @@ func TestSystemserverClasspathModule_AndroidMkEntries(t *testing.T) { } }) } + +func TestSystemserverclasspathFragmentWithoutContents(t *testing.T) { + prepareForTestWithSystemServerClasspath. + ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern( + `\Qempty contents are not allowed\E`)). + RunTestWithBp(t, ` + systemserverclasspath_fragment { + name: "systemserverclasspath-fragment", + } + `) +} diff --git a/java/testing.go b/java/testing.go index 387d59573..1fef337cc 100644 --- a/java/testing.go +++ b/java/testing.go @@ -24,6 +24,7 @@ import ( "android/soong/android" "android/soong/cc" "android/soong/dexpreopt" + "github.com/google/blueprint" ) @@ -55,8 +56,9 @@ var PrepareForTestWithJavaBuildComponents = android.GroupFixturePreparers( }.AddToFixture(), ) -// Test fixture preparer that will define default java modules, e.g. standard prebuilt modules. -var PrepareForTestWithJavaDefaultModules = android.GroupFixturePreparers( +// Test fixture preparer that will define all default java modules except the +// fake_tool_binary for dex2oatd. +var PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd = android.GroupFixturePreparers( // Make sure that all the module types used in the defaults are registered. PrepareForTestWithJavaBuildComponents, // Additional files needed when test disallows non-existent source. @@ -72,6 +74,11 @@ var PrepareForTestWithJavaDefaultModules = android.GroupFixturePreparers( android.FixtureAddTextFile(defaultJavaDir+"/Android.bp", gatherRequiredDepsForTest()), // Add dexpreopt compat libs (android.test.base, etc.) and a fake dex2oatd module. dexpreopt.PrepareForTestWithDexpreoptCompatLibs, +) + +// Test fixture preparer that will define default java modules, e.g. standard prebuilt modules. +var PrepareForTestWithJavaDefaultModules = android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd, dexpreopt.PrepareForTestWithFakeDex2oatd, ) @@ -371,7 +378,7 @@ func apexNamePairFromModule(ctx *android.TestContext, module android.Module) str if apexInfo.IsForPlatform() { apex = "platform" } else { - apex = apexInfo.InApexes[0] + apex = apexInfo.InApexVariants[0] } return fmt.Sprintf("%s:%s", apex, name) diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh index 3f51114f9..42363e9c0 100755 --- a/tests/bootstrap_test.sh +++ b/tests/bootstrap_test.sh @@ -7,6 +7,8 @@ set -o pipefail source "$(dirname "$0")/lib.sh" +readonly GENERATED_BUILD_FILE_NAME="BUILD.bazel" + function test_smoke { setup run_soong @@ -505,8 +507,8 @@ filegroup { EOF GENERATE_BAZEL_FILES=1 run_soong - [[ -e out/soong/bp2build/a/BUILD ]] || fail "a/BUILD not created" - [[ -L out/soong/workspace/a/BUILD ]] || fail "a/BUILD not symlinked" + [[ -e out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not created" + [[ -L out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not symlinked" mkdir -p b touch b/b.txt @@ -519,8 +521,8 @@ filegroup { EOF GENERATE_BAZEL_FILES=1 run_soong - [[ -e out/soong/bp2build/b/BUILD ]] || fail "a/BUILD not created" - [[ -L out/soong/workspace/b/BUILD ]] || fail "a/BUILD not symlinked" + [[ -e out/soong/bp2build/b/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not created" + [[ -L out/soong/workspace/b/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not symlinked" } function test_bp2build_null_build { @@ -551,11 +553,11 @@ filegroup { EOF GENERATE_BAZEL_FILES=1 run_soong - grep -q a1.txt out/soong/bp2build/a/BUILD || fail "a1.txt not in BUILD file" + grep -q a1.txt "out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME}" || fail "a1.txt not in ${GENERATED_BUILD_FILE_NAME} file" touch a/a2.txt GENERATE_BAZEL_FILES=1 run_soong - grep -q a2.txt out/soong/bp2build/a/BUILD || fail "a2.txt not in BUILD file" + grep -q a2.txt "out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME}" || fail "a2.txt not in ${GENERATED_BUILD_FILE_NAME} file" } function test_dump_json_module_graph() { @@ -583,8 +585,8 @@ EOF GENERATE_BAZEL_FILES=1 run_soong [[ -e out/soong/workspace ]] || fail "Bazel workspace not created" [[ -d out/soong/workspace/a/b ]] || fail "module directory not a directory" - [[ -L out/soong/workspace/a/b/BUILD ]] || fail "BUILD file not symlinked" - [[ "$(readlink -f out/soong/workspace/a/b/BUILD)" =~ bp2build/a/b/BUILD$ ]] \ + [[ -L "out/soong/workspace/a/b/${GENERATED_BUILD_FILE_NAME}" ]] || fail "${GENERATED_BUILD_FILE_NAME} file not symlinked" + [[ "$(readlink -f out/soong/workspace/a/b/${GENERATED_BUILD_FILE_NAME})" =~ "bp2build/a/b/${GENERATED_BUILD_FILE_NAME}"$ ]] \ || fail "BUILD files symlinked at the wrong place" [[ -L out/soong/workspace/a/b/b.txt ]] || fail "a/b/b.txt not symlinked" [[ -L out/soong/workspace/a/a.txt ]] || fail "a/b/a.txt not symlinked" @@ -616,7 +618,7 @@ function test_bp2build_build_file_precedence { mkdir -p a touch a/a.txt - touch a/BUILD + touch a/${GENERATED_BUILD_FILE_NAME} cat > a/Android.bp <<EOF filegroup { name: "a", @@ -626,15 +628,15 @@ filegroup { EOF GENERATE_BAZEL_FILES=1 run_soong - [[ -L out/soong/workspace/a/BUILD ]] || fail "BUILD file not symlinked" - [[ "$(readlink -f out/soong/workspace/a/BUILD)" =~ bp2build/a/BUILD$ ]] \ - || fail "BUILD files symlinked to the wrong place" + [[ -L "out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME}" ]] || fail "${GENERATED_BUILD_FILE_NAME} file not symlinked" + [[ "$(readlink -f out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME})" =~ "bp2build/a/${GENERATED_BUILD_FILE_NAME}"$ ]] \ + || fail "${GENERATED_BUILD_FILE_NAME} files symlinked to the wrong place" } function test_bp2build_reports_multiple_errors { setup - mkdir -p a/BUILD + mkdir -p "a/${GENERATED_BUILD_FILE_NAME}" touch a/a.txt cat > a/Android.bp <<EOF filegroup { @@ -644,7 +646,7 @@ filegroup { } EOF - mkdir -p b/BUILD + mkdir -p "b/${GENERATED_BUILD_FILE_NAME}" touch b/b.txt cat > b/Android.bp <<EOF filegroup { @@ -658,8 +660,8 @@ EOF fail "Build should have failed" fi - grep -q "a/BUILD' exist" "$MOCK_TOP/errors" || fail "Error for a/BUILD not found" - grep -q "b/BUILD' exist" "$MOCK_TOP/errors" || fail "Error for b/BUILD not found" + grep -q "a/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for a/${GENERATED_BUILD_FILE_NAME} not found" + grep -q "b/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for b/${GENERATED_BUILD_FILE_NAME} not found" } test_smoke diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh index 082cd0671..e3577107f 100755 --- a/tests/bp2build_bazel_test.sh +++ b/tests/bp2build_bazel_test.sh @@ -6,6 +6,8 @@ set -o pipefail source "$(dirname "$0")/lib.sh" +readonly GENERATED_BUILD_FILE_NAME="BUILD.bazel" + function test_bp2build_generates_all_buildfiles { setup create_mock_bazel @@ -40,24 +42,24 @@ EOF run_bp2build - if [[ ! -f "./out/soong/workspace/foo/convertible_soong_module/BUILD" ]]; then - fail "./out/soong/workspace/foo/convertible_soong_module/BUILD was not generated" + if [[ ! -f "./out/soong/workspace/foo/convertible_soong_module/${GENERATED_BUILD_FILE_NAME}" ]]; then + fail "./out/soong/workspace/foo/convertible_soong_module/${GENERATED_BUILD_FILE_NAME} was not generated" fi - if [[ ! -f "./out/soong/workspace/foo/unconvertible_soong_module/BUILD" ]]; then - fail "./out/soong/workspace/foo/unconvertible_soong_module/BUILD was not generated" + if [[ ! -f "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}" ]]; then + fail "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME} was not generated" fi - if ! grep "the_answer" "./out/soong/workspace/foo/convertible_soong_module/BUILD"; then - fail "missing BUILD target the_answer in convertible_soong_module/BUILD" + if ! grep "the_answer" "./out/soong/workspace/foo/convertible_soong_module/${GENERATED_BUILD_FILE_NAME}"; then + fail "missing BUILD target the_answer in convertible_soong_module/${GENERATED_BUILD_FILE_NAME}" fi - if grep "not_the_answer" "./out/soong/workspace/foo/unconvertible_soong_module/BUILD"; then - fail "found unexpected BUILD target not_the_answer in unconvertible_soong_module/BUILD" + if grep "not_the_answer" "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}"; then + fail "found unexpected BUILD target not_the_answer in unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}" fi - if ! grep "filegroup" "./out/soong/workspace/foo/unconvertible_soong_module/BUILD"; then - fail "missing filegroup in unconvertible_soong_module/BUILD" + if ! grep "filegroup" "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}"; then + fail "missing filegroup in unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}" fi # NOTE: We don't actually use the extra BUILD file for anything here diff --git a/ui/build/finder.go b/ui/build/finder.go index 2eb84ca38..09d53cc1e 100644 --- a/ui/build/finder.go +++ b/ui/build/finder.go @@ -76,6 +76,8 @@ func NewSourceFinder(ctx Context, config Config) (f *finder.Finder) { "Blueprints", // Bazel build definitions. "BUILD.bazel", + // Bazel build definitions. + "BUILD", // Kati clean definitions. "CleanSpec.mk", // Ownership definition. @@ -102,7 +104,7 @@ func NewSourceFinder(ctx Context, config Config) (f *finder.Finder) { func findBazelFiles(entries finder.DirEntries) (dirNames []string, fileNames []string) { matches := []string{} for _, foundName := range entries.FileNames { - if foundName == "BUILD.bazel" || foundName == "WORKSPACE" || strings.HasSuffix(foundName, ".bzl") { + if foundName == "BUILD.bazel" || foundName == "BUILD" || foundName == "WORKSPACE" || strings.HasSuffix(foundName, ".bzl") { matches = append(matches, foundName) } } |