diff options
71 files changed, 2525 insertions, 685 deletions
diff --git a/Android.bp b/Android.bp index 45e661e78..d6260b400 100644 --- a/Android.bp +++ b/Android.bp @@ -116,3 +116,13 @@ cc_genrule { dex_bootjars { name: "dex_bootjars", } + +// Pseudo-test that's run on checkbuilds to ensure that get_clang_version can +// parse cc/config/global.go. +genrule { + name: "get_clang_version_test", + cmd: "$(location get_clang_version) > $(out)", + tools: ["get_clang_version"], + srcs: ["cc/config/global.go"], + out: ["clang-prebuilts-version.txt"], +} @@ -213,8 +213,8 @@ has empty name. A module name's **scope** is the smallest namespace containing it. Suppose a source tree has `device/my` and `device/my/display` namespaces. If `libfoo` -module is defined in `device/co/display/lib/Android.bp`, its namespace is -`device/co/display`. +module is defined in `device/my/display/lib/Android.bp`, its namespace is +`device/my/display`. The name uniqueness thus means that module's name is unique within its scope. In other words, "//_scope_:_name_" is globally unique module reference, e.g, diff --git a/android/apex.go b/android/apex.go index 4618fe97e..b9efe4e5a 100644 --- a/android/apex.go +++ b/android/apex.go @@ -54,6 +54,10 @@ type ApexInfo struct { // True if this module comes from an updatable apexBundle. Updatable bool + // True if this module can use private platform APIs. Only non-updatable APEX can set this + // to true. + UsePlatformApis bool + // The list of SDK modules that the containing apexBundle depends on. RequiredSdks SdkRefs @@ -87,16 +91,31 @@ type ApexInfo struct { var ApexInfoProvider = blueprint.NewMutatorProvider(ApexInfo{}, "apex") +func (i ApexInfo) AddJSONData(d *map[string]interface{}) { + (*d)["Apex"] = map[string]interface{}{ + "ApexVariationName": i.ApexVariationName, + "MinSdkVersion": i.MinSdkVersion, + "InApexModules": i.InApexModules, + "InApexVariants": i.InApexVariants, + "ForPrebuiltApex": i.ForPrebuiltApex, + } +} + // mergedName gives the name of the alias variation that will be used when multiple apex variations // of a module can be deduped into one variation. For example, if libfoo is included in both apex.a // and apex.b, and if the two APEXes have the same min_sdk_version (say 29), then libfoo doesn't // have to be built twice, but only once. In that case, the two apex variations apex.a and apex.b -// are configured to have the same alias variation named apex29. +// are configured to have the same alias variation named apex29. Whether platform APIs is allowed +// or not also matters; if two APEXes don't have the same allowance, they get different names and +// thus wouldn't be merged. func (i ApexInfo) mergedName(ctx PathContext) string { name := "apex" + strconv.Itoa(i.MinSdkVersion.FinalOrFutureInt()) for _, sdk := range i.RequiredSdks { name += "_" + sdk.Name + "_" + sdk.Version } + if i.UsePlatformApis { + name += "_private" + } return name } @@ -527,6 +546,10 @@ func mergeApexVariations(ctx PathContext, apexInfos []ApexInfo) (merged []ApexIn 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 + if merged[index].UsePlatformApis != apexInfo.UsePlatformApis { + panic(fmt.Errorf("variants having different UsePlatformApis can't be merged")) + } + merged[index].UsePlatformApis = apexInfo.UsePlatformApis } else { seen[mergedName] = len(merged) apexInfo.ApexVariationName = mergedName diff --git a/android/apex_test.go b/android/apex_test.go index e1123692d..60a639b4b 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"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"apex10000", FutureApiLevel, false, 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"}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, false}}, + {"apex10000_baz_1", FutureApiLevel, false, 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"}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", uncheckedFinalApiLevel(30), false, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex30", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, - {"apex10000", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"apex30", uncheckedFinalApiLevel(30), false, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"apex10000", FutureApiLevel, false, 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"}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", FutureApiLevel, true, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, + {"apex10000", FutureApiLevel, true, false, 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"}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", FutureApiLevel, false, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"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}, + {"apex10000_baz_2", FutureApiLevel, false, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"apex10000_baz_1", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, }, wantAliases: [][2]string{ {"bar", "apex10000_baz_2"}, @@ -102,21 +102,36 @@ func Test_mergeApexVariations(t *testing.T) { { name: "don't merge when for prebuilt_apex", in: []ApexInfo{ - {"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", FutureApiLevel, true, false, 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"}, []string{"baz"}, nil, ForPrebuiltApex}, + {"baz", FutureApiLevel, true, false, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, - {"baz", FutureApiLevel, true, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex}, + {"apex10000", FutureApiLevel, true, false, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, + {"baz", FutureApiLevel, true, false, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex}, }, wantAliases: [][2]string{ {"bar", "apex10000"}, {"foo", "apex10000"}, }, }, + { + name: "don't merge different UsePlatformApis", + in: []ApexInfo{ + {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", FutureApiLevel, false, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + }, + wantMerged: []ApexInfo{ + {"apex10000_private", FutureApiLevel, false, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"apex10000", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + }, + wantAliases: [][2]string{ + {"bar", "apex10000_private"}, + {"foo", "apex10000"}, + }, + }, } for _, tt := range tests { diff --git a/android/module.go b/android/module.go index f745a4ab4..07d82f1a6 100644 --- a/android/module.go +++ b/android/module.go @@ -1190,6 +1190,10 @@ type ModuleBase struct { vintfFragmentsPaths Paths } +func (m *ModuleBase) AddJSONData(d *map[string]interface{}) { + (*d)["Android"] = map[string]interface{}{} +} + func (m *ModuleBase) ComponentDepsMutator(BottomUpMutatorContext) {} func (m *ModuleBase) DepsMutator(BottomUpMutatorContext) {} diff --git a/android/sdk.go b/android/sdk.go index 5c58612b0..e70003144 100644 --- a/android/sdk.go +++ b/android/sdk.go @@ -375,6 +375,8 @@ type SdkMemberTypeDependencyTag interface { // SdkMemberType returns the SdkMemberType that will be used to automatically add the child module // to the sdk. + // + // Returning nil will prevent the module being added to the sdk. SdkMemberType(child Module) SdkMemberType // ExportMember determines whether a module added to the sdk through this tag will be exported diff --git a/android/variable.go b/android/variable.go index c76612099..b6e168cbc 100644 --- a/android/variable.go +++ b/android/variable.go @@ -101,6 +101,9 @@ type variableProperties struct { Keep_symbols *bool Keep_symbols_and_debug_frame *bool } + Static_libs []string + Whole_static_libs []string + Shared_libs []string } // eng is true for -eng builds, and can be used to turn on additionaly heavyweight debugging diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go index 067dcba35..02ab89d0b 100644 --- a/androidmk/androidmk/androidmk_test.go +++ b/androidmk/androidmk/androidmk_test.go @@ -794,7 +794,7 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) include $(BUILD_CTS_SUPPORT_PACKAGE) `, expected: ` -android_test { +android_test_helper_app { name: "FooTest", defaults: ["cts_support_defaults"], test_suites: ["cts"], diff --git a/apex/apex.go b/apex/apex.go index 7ffa6cc81..baaf87475 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -130,6 +130,10 @@ type apexBundleProperties struct { // symlinking to the system libs. Default is true. Updatable *bool + // Whether this APEX can use platform APIs or not. Can be set to true only when `updatable: + // false`. Default is false. + Platform_apis *bool + // Whether this APEX is installable to one of the partitions like system, vendor, etc. // Default: true. Installable *bool @@ -182,6 +186,12 @@ type apexBundleProperties struct { // used in tests. Test_only_force_compression *bool + // Canonical name of this APEX bundle. Used to determine the path to the + // activated APEX on device (i.e. /apex/<apexVariationName>), and used for the + // apex mutator variations. For override_apex modules, this is the name of the + // overridden base module. + ApexVariationName string `blueprint:"mutated"` + IsCoverageVariant bool `blueprint:"mutated"` // List of sanitizer names that this APEX is enabled for @@ -824,6 +834,10 @@ var ApexBundleInfoProvider = blueprint.NewMutatorProvider(ApexBundleInfo{}, "ape var _ ApexInfoMutator = (*apexBundle)(nil) +func (a *apexBundle) ApexVariationName() string { + return a.properties.ApexVariationName +} + // ApexInfoMutator is responsible for collecting modules that need to have apex variants. They are // identified by doing a graph walk starting from an apexBundle. Basically, all the (direct and // indirect) dependencies are collected. But a few types of modules that shouldn't be included in @@ -912,15 +926,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 + apexVariationName := proptools.StringDefault(a.properties.Apex_name, mctx.ModuleName()) // could be com.android.foo + a.properties.ApexVariationName = apexVariationName apexInfo := android.ApexInfo{ - ApexVariationName: mctx.ModuleName(), // could be com.android.foo + ApexVariationName: apexVariationName, MinSdkVersion: minSdkVersion, RequiredSdks: a.RequiredSdks(), Updatable: a.Updatable(), - InApexVariants: []string{mctx.ModuleName()}, // could be com.android.foo - InApexModules: []string{a.Name()}, // could be com.mycompany.android.foo + UsePlatformApis: a.UsePlatformApis(), + InApexVariants: []string{apexVariationName}, + InApexModules: []string{a.Name()}, // could be com.mycompany.android.foo ApexContents: []*android.ApexContents{apexContents}, } mctx.WalkDeps(func(child, parent android.Module) bool { @@ -933,6 +948,10 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) { } type ApexInfoMutator interface { + // ApexVariationName returns the name of the APEX variation to use in the apex + // mutator etc. It is the same name as ApexInfo.ApexVariationName. + ApexVariationName() string + // ApexInfoMutator implementations must call BuildForApex(ApexInfo) on any modules that are // depended upon by an apex and which require an apex specific variant. ApexInfoMutator(android.TopDownMutatorContext) @@ -1058,10 +1077,8 @@ func apexMutator(mctx android.BottomUpMutatorContext) { } // apexBundle itself is mutated so that it and its dependencies have the same apex variant. - // TODO(jiyong): document the reason why the VNDK APEX is an exception here. - unprefixedModuleName := android.RemoveOptionalPrebuiltPrefix(mctx.ModuleName()) - if apexModuleTypeRequiresVariant(mctx.Module()) { - apexBundleName := unprefixedModuleName + if ai, ok := mctx.Module().(ApexInfoMutator); ok && apexModuleTypeRequiresVariant(ai) { + apexBundleName := ai.ApexVariationName() mctx.CreateVariations(apexBundleName) if strings.HasPrefix(apexBundleName, "com.android.art") { // Create an alias from the platform variant. This is done to make @@ -1084,6 +1101,7 @@ func apexMutator(mctx android.BottomUpMutatorContext) { // apex variant name. This name matches the name used to create the variations of modules for // which apexModuleTypeRequiresVariant return true. // TODO(b/191269918): Remove this workaround. + unprefixedModuleName := android.RemoveOptionalPrebuiltPrefix(mctx.ModuleName()) mctx.SetDefaultDependencyVariation(&unprefixedModuleName) mctx.CreateVariations(apexBundleName) if strings.HasPrefix(apexBundleName, "com.android.art") { @@ -1095,18 +1113,13 @@ func apexMutator(mctx android.BottomUpMutatorContext) { // apexModuleTypeRequiresVariant determines whether the module supplied requires an apex specific // variant. -func apexModuleTypeRequiresVariant(module android.Module) bool { +func apexModuleTypeRequiresVariant(module ApexInfoMutator) bool { if a, ok := module.(*apexBundle); ok { + // TODO(jiyong): document the reason why the VNDK APEX is an exception here. return !a.vndkApex } - // Match apex_set and prebuilt_apex. Would also match apexBundle but that is handled specially - // above. - if _, ok := module.(ApexInfoMutator); ok { - return true - } - - return false + return true } // See android.UpdateDirectlyInAnyApex @@ -1321,6 +1334,10 @@ func (a *apexBundle) Updatable() bool { return proptools.BoolDefault(a.properties.Updatable, true) } +func (a *apexBundle) UsePlatformApis() bool { + return proptools.BoolDefault(a.properties.Platform_apis, false) +} + // getCertString returns the name of the cert that should be used to sign this APEX. This is // basically from the "certificate" property, but could be overridden by the device config. func (a *apexBundle) getCertString(ctx android.BaseModuleContext) string { @@ -2374,6 +2391,9 @@ func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) { if String(a.properties.Min_sdk_version) == "" { ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well") } + if a.UsePlatformApis() { + ctx.PropertyErrorf("updatable", "updatable APEXes can't use platform APIs") + } a.checkJavaStableSdkVersion(ctx) a.checkClasspathFragments(ctx) } diff --git a/apex/apex_test.go b/apex/apex_test.go index 792f7f392..b5b1d4401 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -839,6 +839,7 @@ func TestApexWithStubs(t *testing.T) { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib", "mylib3"], + binaries: ["foo.rust"], updatable: false, } @@ -887,6 +888,25 @@ func TestApexWithStubs(t *testing.T) { stl: "none", apex_available: [ "myapex" ], } + + rust_binary { + name: "foo.rust", + srcs: ["foo.rs"], + shared_libs: ["libfoo.shared_from_rust"], + prefer_rlib: true, + apex_available: ["myapex"], + } + + cc_library_shared { + name: "libfoo.shared_from_rust", + srcs: ["mylib.cpp"], + system_shared_libs: [], + stl: "none", + stubs: { + versions: ["10", "11", "12"], + }, + } + `) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule") @@ -924,7 +944,90 @@ func TestApexWithStubs(t *testing.T) { "lib64/mylib.so", "lib64/mylib3.so", "lib64/mylib4.so", + "bin/foo.rust", + "lib64/libc++.so", // by the implicit dependency from foo.rust + "lib64/liblog.so", // by the implicit dependency from foo.rust }) + + // Ensure that stub dependency from a rust module is not included + ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so") + // The rust module is linked to the stub cc library + rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"] + ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so") + ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so") +} + +func TestApexCanUsePrivateApis(t *testing.T) { + ctx := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + native_shared_libs: ["mylib"], + binaries: ["foo.rust"], + updatable: false, + platform_apis: true, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "mylib", + srcs: ["mylib.cpp"], + shared_libs: ["mylib2"], + system_shared_libs: [], + stl: "none", + apex_available: [ "myapex" ], + } + + cc_library { + name: "mylib2", + srcs: ["mylib.cpp"], + cflags: ["-include mylib.h"], + system_shared_libs: [], + stl: "none", + stubs: { + versions: ["1", "2", "3"], + }, + } + + rust_binary { + name: "foo.rust", + srcs: ["foo.rs"], + shared_libs: ["libfoo.shared_from_rust"], + prefer_rlib: true, + apex_available: ["myapex"], + } + + cc_library_shared { + name: "libfoo.shared_from_rust", + srcs: ["mylib.cpp"], + system_shared_libs: [], + stl: "none", + stubs: { + versions: ["10", "11", "12"], + }, + } + `) + + apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule") + copyCmds := apexRule.Args["copy_commands"] + + // Ensure that indirect stubs dep is not included + ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so") + ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so") + + // Ensure that we are using non-stub variants of mylib2 and libfoo.shared_from_rust (because + // of the platform_apis: true) + mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000_private").Rule("ld").Args["libFlags"] + ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so") + ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so") + rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000_private").Rule("rustc").Args["linkFlags"] + ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so") + ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so") } func TestApexWithStubsWithMinSdkVersion(t *testing.T) { @@ -3972,13 +4075,13 @@ func TestApexName(t *testing.T) { } `) - module := ctx.ModuleForTests("myapex", "android_common_myapex_image") + module := ctx.ModuleForTests("myapex", "android_common_com.android.myapex_image") apexManifestRule := module.Rule("apexManifestRule") ensureContains(t, apexManifestRule.Args["opt"], "-v name com.android.myapex") apexRule := module.Rule("apexRule") ensureContains(t, apexRule.Args["opt_flags"], "--do_not_check_keyname") - apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) + apexBundle := module.Module().(*apexBundle) data := android.AndroidMkDataForTest(t, ctx, apexBundle) name := apexBundle.BaseModuleName() prefix := "TARGET_" @@ -4521,6 +4624,59 @@ func TestPrebuiltOverrides(t *testing.T) { } } +func TestPrebuiltApexName(t *testing.T) { + testApex(t, ` + prebuilt_apex { + name: "com.company.android.myapex", + apex_name: "com.android.myapex", + src: "company-myapex-arm.apex", + } + `).ModuleForTests("com.company.android.myapex", "android_common_com.android.myapex") + + testApex(t, ` + apex_set { + name: "com.company.android.myapex", + apex_name: "com.android.myapex", + set: "company-myapex.apks", + } + `).ModuleForTests("com.company.android.myapex", "android_common_com.android.myapex") +} + +func TestPrebuiltApexNameWithPlatformBootclasspath(t *testing.T) { + _ = android.GroupFixturePreparers( + java.PrepareForTestWithJavaDefaultModules, + PrepareForTestWithApexBuildComponents, + android.FixtureWithRootAndroidBp(` + platform_bootclasspath { + name: "platform-bootclasspath", + fragments: [ + { + apex: "com.android.art", + module: "art-bootclasspath-fragment", + }, + ], + } + + prebuilt_apex { + name: "com.company.android.art", + apex_name: "com.android.art", + src: "com.company.android.art-arm.apex", + exported_bootclasspath_fragments: ["art-bootclasspath-fragment"], + } + + prebuilt_bootclasspath_fragment { + name: "art-bootclasspath-fragment", + contents: ["core-oj"], + } + + java_import { + name: "core-oj", + jars: ["prebuilt.jar"], + } + `), + ).RunTest(t) +} + // These tests verify that the prebuilt_apex/deapexer to java_import wiring allows for the // propagation of paths to dex implementation jars from the former to the latter. func TestPrebuiltExportDexImplementationJars(t *testing.T) { diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go index 66bc9e069..4b1600e1c 100644 --- a/apex/bootclasspath_fragment_test.go +++ b/apex/bootclasspath_fragment_test.go @@ -22,6 +22,7 @@ import ( "android/soong/android" "android/soong/java" + "github.com/google/blueprint/proptools" ) // Contains tests for bootclasspath_fragment logic from java/bootclasspath_fragment.go as the ART @@ -216,9 +217,10 @@ func TestBootclasspathFragments_FragmentDependency(t *testing.T) { `, ) - checkSdkKindStubs := func(message string, info java.HiddenAPIInfo, kind android.SdkKind, expectedPaths ...string) { + checkAPIScopeStubs := func(message string, info java.HiddenAPIInfo, apiScope *java.HiddenAPIScope, expectedPaths ...string) { t.Helper() - android.AssertPathsRelativeToTopEquals(t, fmt.Sprintf("%s %s", message, kind), expectedPaths, info.TransitiveStubDexJarsByKind[kind]) + paths := info.TransitiveStubDexJarsByScope.StubDexJarsForScope(apiScope) + android.AssertPathsRelativeToTopEquals(t, fmt.Sprintf("%s %s", message, apiScope), expectedPaths, paths) } // Check stub dex paths exported by art. @@ -229,10 +231,10 @@ func TestBootclasspathFragments_FragmentDependency(t *testing.T) { bazSystemStubs := "out/soong/.intermediates/baz.stubs.system/android_common/dex/baz.stubs.system.jar" bazTestStubs := "out/soong/.intermediates/baz.stubs.test/android_common/dex/baz.stubs.test.jar" - checkSdkKindStubs("art", artInfo, android.SdkPublic, bazPublicStubs) - checkSdkKindStubs("art", artInfo, android.SdkSystem, bazSystemStubs) - checkSdkKindStubs("art", artInfo, android.SdkTest, bazTestStubs) - checkSdkKindStubs("art", artInfo, android.SdkCorePlatform) + checkAPIScopeStubs("art", artInfo, java.PublicHiddenAPIScope, bazPublicStubs) + checkAPIScopeStubs("art", artInfo, java.SystemHiddenAPIScope, bazSystemStubs) + checkAPIScopeStubs("art", artInfo, java.TestHiddenAPIScope, bazTestStubs) + checkAPIScopeStubs("art", artInfo, java.CorePlatformHiddenAPIScope) // Check stub dex paths exported by other. otherFragment := result.Module("other-bootclasspath-fragment", "android_common") @@ -241,10 +243,10 @@ func TestBootclasspathFragments_FragmentDependency(t *testing.T) { fooPublicStubs := "out/soong/.intermediates/foo.stubs/android_common/dex/foo.stubs.jar" fooSystemStubs := "out/soong/.intermediates/foo.stubs.system/android_common/dex/foo.stubs.system.jar" - checkSdkKindStubs("other", otherInfo, android.SdkPublic, bazPublicStubs, fooPublicStubs) - checkSdkKindStubs("other", otherInfo, android.SdkSystem, bazSystemStubs, fooSystemStubs) - checkSdkKindStubs("other", otherInfo, android.SdkTest, bazTestStubs, fooSystemStubs) - checkSdkKindStubs("other", otherInfo, android.SdkCorePlatform) + checkAPIScopeStubs("other", otherInfo, java.PublicHiddenAPIScope, bazPublicStubs, fooPublicStubs) + checkAPIScopeStubs("other", otherInfo, java.SystemHiddenAPIScope, bazSystemStubs, fooSystemStubs) + checkAPIScopeStubs("other", otherInfo, java.TestHiddenAPIScope, bazTestStubs, fooSystemStubs) + checkAPIScopeStubs("other", otherInfo, java.CorePlatformHiddenAPIScope) } func checkBootclasspathFragment(t *testing.T, result *android.TestResult, moduleName, variantName string, expectedConfiguredModules string, expectedBootclasspathFragmentFiles string) { @@ -718,4 +720,480 @@ func TestBootclasspathFragmentContentsNoName(t *testing.T) { checkFragmentExportedDexJar("bar", "out/soong/.intermediates/mybootclasspathfragment/android_common_apex10000/hiddenapi-modular/encoded/bar.jar") } +func getDexJarPath(result *android.TestResult, name string) string { + module := result.Module(name, "android_common") + return module.(java.UsesLibraryDependency).DexJarBuildPath().RelativeToTop().String() +} + +// TestBootclasspathFragment_HiddenAPIList checks to make sure that the correct parameters are +// passed to the hiddenapi list tool. +func TestBootclasspathFragment_HiddenAPIList(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForTestWithBootclasspathFragment, + prepareForTestWithArtApex, + prepareForTestWithMyapex, + // Configure bootclasspath jars to ensure that hidden API encoding is performed on them. + java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz"), + java.FixtureConfigureUpdatableBootJars("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", "quuz"), + ).RunTestWithBp(t, ` + apex { + name: "com.android.art", + key: "com.android.art.key", + bootclasspath_fragments: ["art-bootclasspath-fragment"], + updatable: false, + } + + apex_key { + name: "com.android.art.key", + public_key: "com.android.art.avbpubkey", + private_key: "com.android.art.pem", + } + + java_library { + name: "baz", + apex_available: [ + "com.android.art", + ], + srcs: ["b.java"], + compile_dex: true, + } + + java_sdk_library { + name: "quuz", + apex_available: [ + "com.android.art", + ], + srcs: ["b.java"], + compile_dex: true, + public: {enabled: true}, + system: {enabled: true}, + test: {enabled: true}, + module_lib: {enabled: true}, + } + + bootclasspath_fragment { + name: "art-bootclasspath-fragment", + image_name: "art", + // Must match the "com.android.art:" entries passed to FixtureConfigureBootJars above. + contents: ["baz", "quuz"], + apex_available: [ + "com.android.art", + ], + } + + apex { + name: "myapex", + key: "myapex.key", + bootclasspath_fragments: [ + "mybootclasspathfragment", + ], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + java_sdk_library { + name: "foo", + srcs: ["b.java"], + shared_library: false, + public: {enabled: true}, + apex_available: [ + "myapex", + ], + } + + java_library { + name: "bar", + srcs: ["b.java"], + installable: true, + apex_available: [ + "myapex", + ], + } + + bootclasspath_fragment { + name: "mybootclasspathfragment", + contents: [ + "foo", + "bar", + ], + apex_available: [ + "myapex", + ], + fragments: [ + { + apex: "com.android.art", + module: "art-bootclasspath-fragment", + }, + ], + } + `) + + java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{ + "art-bootclasspath-fragment", + "bar", + "dex2oatd", + "foo", + }) + + fooStubs := getDexJarPath(result, "foo.stubs") + quuzPublicStubs := getDexJarPath(result, "quuz.stubs") + quuzSystemStubs := getDexJarPath(result, "quuz.stubs.system") + quuzTestStubs := getDexJarPath(result, "quuz.stubs.test") + quuzModuleLibStubs := getDexJarPath(result, "quuz.stubs.module_lib") + + // Make sure that the fragment uses the quuz stub dex jars when generating the hidden API flags. + fragment := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000") + + rule := fragment.Rule("modularHiddenAPIStubFlagsFile") + command := rule.RuleParams.Command + android.AssertStringDoesContain(t, "check correct rule", command, "hiddenapi list") + + // Make sure that the quuz stubs are available for resolving references from the implementation + // boot dex jars provided by this module. + android.AssertStringDoesContain(t, "quuz widest", command, "--dependency-stub-dex="+quuzModuleLibStubs) + + // Make sure that the quuz stubs are available for resolving references from the different API + // stubs provided by this module. + android.AssertStringDoesContain(t, "public", command, "--public-stub-classpath="+quuzPublicStubs+":"+fooStubs) + android.AssertStringDoesContain(t, "system", command, "--system-stub-classpath="+quuzSystemStubs+":"+fooStubs) + android.AssertStringDoesContain(t, "test", command, "--test-stub-classpath="+quuzTestStubs+":"+fooStubs) +} + +// TestBootclasspathFragment_AndroidNonUpdatable checks to make sure that setting +// additional_stubs: ["android-non-updatable"] causes the source android-non-updatable modules to be +// added to the hiddenapi list tool. +func TestBootclasspathFragment_AndroidNonUpdatable(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForTestWithBootclasspathFragment, + prepareForTestWithArtApex, + prepareForTestWithMyapex, + // Configure bootclasspath jars to ensure that hidden API encoding is performed on them. + java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "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", "android-non-updatable"), + ).RunTestWithBp(t, ` + java_sdk_library { + name: "android-non-updatable", + srcs: ["b.java"], + compile_dex: true, + public: { + enabled: true, + }, + system: { + enabled: true, + }, + test: { + enabled: true, + }, + module_lib: { + enabled: true, + }, + } + + apex { + name: "com.android.art", + key: "com.android.art.key", + bootclasspath_fragments: ["art-bootclasspath-fragment"], + java_libs: [ + "baz", + "quuz", + ], + updatable: false, + } + + apex_key { + name: "com.android.art.key", + public_key: "com.android.art.avbpubkey", + private_key: "com.android.art.pem", + } + + java_library { + name: "baz", + apex_available: [ + "com.android.art", + ], + srcs: ["b.java"], + compile_dex: true, + } + + java_library { + name: "quuz", + apex_available: [ + "com.android.art", + ], + srcs: ["b.java"], + compile_dex: true, + } + + bootclasspath_fragment { + name: "art-bootclasspath-fragment", + image_name: "art", + // Must match the "com.android.art:" entries passed to FixtureConfigureBootJars above. + contents: ["baz", "quuz"], + apex_available: [ + "com.android.art", + ], + } + + apex { + name: "myapex", + key: "myapex.key", + bootclasspath_fragments: [ + "mybootclasspathfragment", + ], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + java_sdk_library { + name: "foo", + srcs: ["b.java"], + shared_library: false, + public: {enabled: true}, + apex_available: [ + "myapex", + ], + } + + java_library { + name: "bar", + srcs: ["b.java"], + installable: true, + apex_available: [ + "myapex", + ], + } + + bootclasspath_fragment { + name: "mybootclasspathfragment", + contents: [ + "foo", + "bar", + ], + apex_available: [ + "myapex", + ], + additional_stubs: ["android-non-updatable"], + fragments: [ + { + apex: "com.android.art", + module: "art-bootclasspath-fragment", + }, + ], + } + `) + + java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{ + "android-non-updatable.stubs", + "android-non-updatable.stubs.module_lib", + "android-non-updatable.stubs.system", + "android-non-updatable.stubs.test", + "art-bootclasspath-fragment", + "bar", + "dex2oatd", + "foo", + }) + + nonUpdatablePublicStubs := getDexJarPath(result, "android-non-updatable.stubs") + nonUpdatableSystemStubs := getDexJarPath(result, "android-non-updatable.stubs.system") + nonUpdatableTestStubs := getDexJarPath(result, "android-non-updatable.stubs.test") + nonUpdatableModuleLibStubs := getDexJarPath(result, "android-non-updatable.stubs.module_lib") + + // Make sure that the fragment uses the android-non-updatable modules when generating the hidden + // API flags. + fragment := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000") + + rule := fragment.Rule("modularHiddenAPIStubFlagsFile") + command := rule.RuleParams.Command + android.AssertStringDoesContain(t, "check correct rule", command, "hiddenapi list") + + // Make sure that the module_lib non-updatable stubs are available for resolving references from + // the implementation boot dex jars provided by this module. + android.AssertStringDoesContain(t, "android-non-updatable widest", command, "--dependency-stub-dex="+nonUpdatableModuleLibStubs) + + // Make sure that the appropriate non-updatable stubs are available for resolving references from + // the different API stubs provided by this module. + android.AssertStringDoesContain(t, "public", command, "--public-stub-classpath="+nonUpdatablePublicStubs) + android.AssertStringDoesContain(t, "system", command, "--system-stub-classpath="+nonUpdatableSystemStubs) + android.AssertStringDoesContain(t, "test", command, "--test-stub-classpath="+nonUpdatableTestStubs) +} + +// TestBootclasspathFragment_AndroidNonUpdatable_AlwaysUsePrebuiltSdks checks to make sure that +// setting additional_stubs: ["android-non-updatable"] causes the prebuilt android-non-updatable +// modules to be added to the hiddenapi list tool. +func TestBootclasspathFragment_AndroidNonUpdatable_AlwaysUsePrebuiltSdks(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForTestWithBootclasspathFragment, + java.PrepareForTestWithJavaDefaultModules, + prepareForTestWithArtApex, + prepareForTestWithMyapex, + // Configure bootclasspath jars to ensure that hidden API encoding is performed on them. + java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "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", ""), + + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true) + }), + + java.PrepareForTestWithJavaSdkLibraryFiles, + java.FixtureWithPrebuiltApis(map[string][]string{ + "current": {"android-non-updatable"}, + "30": {"foo"}, + }), + ).RunTestWithBp(t, ` + apex { + name: "com.android.art", + key: "com.android.art.key", + bootclasspath_fragments: ["art-bootclasspath-fragment"], + java_libs: [ + "baz", + "quuz", + ], + updatable: false, + } + + apex_key { + name: "com.android.art.key", + public_key: "com.android.art.avbpubkey", + private_key: "com.android.art.pem", + } + + java_library { + name: "baz", + apex_available: [ + "com.android.art", + ], + srcs: ["b.java"], + compile_dex: true, + } + + java_library { + name: "quuz", + apex_available: [ + "com.android.art", + ], + srcs: ["b.java"], + compile_dex: true, + } + + bootclasspath_fragment { + name: "art-bootclasspath-fragment", + image_name: "art", + // Must match the "com.android.art:" entries passed to FixtureConfigureBootJars above. + contents: ["baz", "quuz"], + apex_available: [ + "com.android.art", + ], + } + + apex { + name: "myapex", + key: "myapex.key", + bootclasspath_fragments: [ + "mybootclasspathfragment", + ], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + java_sdk_library { + name: "foo", + srcs: ["b.java"], + shared_library: false, + public: {enabled: true}, + apex_available: [ + "myapex", + ], + } + + java_library { + name: "bar", + srcs: ["b.java"], + installable: true, + apex_available: [ + "myapex", + ], + } + + bootclasspath_fragment { + name: "mybootclasspathfragment", + contents: [ + "foo", + "bar", + ], + apex_available: [ + "myapex", + ], + additional_stubs: ["android-non-updatable"], + fragments: [ + { + apex: "com.android.art", + module: "art-bootclasspath-fragment", + }, + ], + } + `) + + java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{ + "art-bootclasspath-fragment", + "bar", + "dex2oatd", + "foo", + "prebuilt_sdk_module-lib_current_android-non-updatable", + "prebuilt_sdk_public_current_android-non-updatable", + "prebuilt_sdk_system_current_android-non-updatable", + "prebuilt_sdk_test_current_android-non-updatable", + }) + + nonUpdatablePublicStubs := getDexJarPath(result, "sdk_public_current_android-non-updatable") + nonUpdatableSystemStubs := getDexJarPath(result, "sdk_system_current_android-non-updatable") + nonUpdatableTestStubs := getDexJarPath(result, "sdk_test_current_android-non-updatable") + nonUpdatableModuleLibStubs := getDexJarPath(result, "sdk_module-lib_current_android-non-updatable") + + // Make sure that the fragment uses the android-non-updatable modules when generating the hidden + // API flags. + fragment := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000") + + rule := fragment.Rule("modularHiddenAPIStubFlagsFile") + command := rule.RuleParams.Command + android.AssertStringDoesContain(t, "check correct rule", command, "hiddenapi list") + + // Make sure that the module_lib non-updatable stubs are available for resolving references from + // the implementation boot dex jars provided by this module. + android.AssertStringDoesContain(t, "android-non-updatable widest", command, "--dependency-stub-dex="+nonUpdatableModuleLibStubs) + + // Make sure that the appropriate non-updatable stubs are available for resolving references from + // the different API stubs provided by this module. + android.AssertStringDoesContain(t, "public", command, "--public-stub-classpath="+nonUpdatablePublicStubs) + android.AssertStringDoesContain(t, "system", command, "--system-stub-classpath="+nonUpdatableSystemStubs) + android.AssertStringDoesContain(t, "test", command, "--test-stub-classpath="+nonUpdatableTestStubs) +} + // TODO(b/177892522) - add test for host apex. diff --git a/apex/prebuilt.go b/apex/prebuilt.go index ea06d45cb..1bb0fb582 100644 --- a/apex/prebuilt.go +++ b/apex/prebuilt.go @@ -24,7 +24,6 @@ import ( "android/soong/java" "github.com/google/blueprint" - "github.com/google/blueprint/proptools" ) @@ -76,6 +75,10 @@ type sanitizedPrebuilt interface { type PrebuiltCommonProperties struct { SelectedApexProperties + // Canonical name of this APEX. Used to determine the path to the activated APEX on + // device (/apex/<apex_name>). If unspecified, follows the name property. + Apex_name *string + ForceDisable bool `blueprint:"mutated"` // whether the extracted apex file is installable. @@ -110,6 +113,10 @@ func (p *prebuiltCommon) initPrebuiltCommon(module android.Module, properties *P android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) } +func (p *prebuiltCommon) ApexVariationName() string { + return proptools.StringDefault(p.prebuiltCommonProperties.Apex_name, p.ModuleBase.BaseModuleName()) +} + func (p *prebuiltCommon) Prebuilt() *android.Prebuilt { return &p.prebuilt } @@ -369,6 +376,12 @@ func (p *prebuiltCommon) apexInfoMutator(mctx android.TopDownMutatorContext) { } } + // Ignore any modules that do not implement ApexModule as they cannot have an APEX specific + // variant. + if _, ok := child.(android.ApexModule); !ok { + return false + } + // Strip off the prebuilt_ prefix if present before storing content to ensure consistent // behavior whether there is a corresponding source module present or not. depName = android.RemoveOptionalPrebuiltPrefix(depName) @@ -390,11 +403,11 @@ func (p *prebuiltCommon) apexInfoMutator(mctx android.TopDownMutatorContext) { }) // Create an ApexInfo for the prebuilt_apex. - apexVariationName := android.RemoveOptionalPrebuiltPrefix(mctx.ModuleName()) + apexVariationName := p.ApexVariationName() apexInfo := android.ApexInfo{ ApexVariationName: apexVariationName, InApexVariants: []string{apexVariationName}, - InApexModules: []string{apexVariationName}, + InApexModules: []string{p.ModuleBase.BaseModuleName()}, // BaseModuleName() to avoid the prebuilt_ prefix. ApexContents: []*android.ApexContents{apexContents}, ForPrebuiltApex: true, } diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go index fae610189..96400241e 100644 --- a/bpfix/bpfix/bpfix.go +++ b/bpfix/bpfix/bpfix.go @@ -319,7 +319,7 @@ func rewriteCtsModuleTypes(f *Fixer) error { var defStr string switch mod.Type { case "cts_support_package": - mod.Type = "android_test" + mod.Type = "android_test_helper_app" defStr = "cts_support_defaults" case "cts_package": mod.Type = "android_test" @@ -622,12 +622,20 @@ func rewriteAndroidmkPrebuiltEtc(f *Fixer) error { func rewriteAndroidTest(f *Fixer) error { for _, def := range f.tree.Defs { mod, ok := def.(*parser.Module) - if !(ok && mod.Type == "android_test") { + if !ok { + // The definition is not a module. + continue + } + if mod.Type != "android_test" && mod.Type != "android_test_helper_app" { + // The module is not an android_test or android_test_helper_app. continue } // The rewriter converts LOCAL_MODULE_PATH attribute into a struct attribute // 'local_module_path'. For the android_test module, it should be $(TARGET_OUT_DATA_APPS), // that is, `local_module_path: { var: "TARGET_OUT_DATA_APPS"}` + // 1. if the `key: val` pair matches, (key is `local_module_path`, + // and val is `{ var: "TARGET_OUT_DATA_APPS"}`), this property is removed; + // 2. o/w, an error msg is thrown. const local_module_path = "local_module_path" if prop_local_module_path, ok := mod.GetProperty(local_module_path); ok { removeProperty(mod, local_module_path) @@ -637,7 +645,7 @@ func rewriteAndroidTest(f *Fixer) error { continue } return indicateAttributeError(mod, "filename", - "Only LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) is allowed for the android_test") + "Only LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) is allowed for the %s", mod.Type) } } return nil diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go index 61dfe1af4..ebfeb22c7 100644 --- a/bpfix/bpfix/bpfix_test.go +++ b/bpfix/bpfix/bpfix_test.go @@ -636,7 +636,7 @@ func TestRewriteCtsModuleTypes(t *testing.T) { } `, out: ` - android_test { + android_test_helper_app { name: "foo", defaults: ["cts_support_defaults"], } diff --git a/cc/binary.go b/cc/binary.go index 3aa3fdf01..48f70d9b8 100644 --- a/cc/binary.go +++ b/cc/binary.go @@ -146,16 +146,17 @@ func (binary *binaryDecorator) getStem(ctx BaseModuleContext) string { // modules common to most binaries, such as bionic libraries. func (binary *binaryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps { deps = binary.baseLinker.linkerDeps(ctx, deps) - if ctx.toolchain().Bionic() { - if !Bool(binary.baseLinker.Properties.Nocrt) { - if binary.static() { - deps.CrtBegin = []string{"crtbegin_static"} - } else { - deps.CrtBegin = []string{"crtbegin_dynamic"} - } - deps.CrtEnd = []string{"crtend_android"} + if !Bool(binary.baseLinker.Properties.Nocrt) { + if binary.static() { + deps.CrtBegin = ctx.toolchain().CrtBeginStaticBinary() + deps.CrtEnd = ctx.toolchain().CrtEndStaticBinary() + } else { + deps.CrtBegin = ctx.toolchain().CrtBeginSharedBinary() + deps.CrtEnd = ctx.toolchain().CrtEndSharedBinary() } + } + if ctx.toolchain().Bionic() { if binary.static() { if ctx.selectedStl() == "libc++_static" { deps.StaticLibs = append(deps.StaticLibs, "libm", "libc") @@ -169,16 +170,8 @@ func (binary *binaryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.LateStaticLibs = append(groupLibs, deps.LateStaticLibs...) } - // Embed the linker into host bionic binaries. This is needed to support host bionic, - // as the linux kernel requires that the ELF interpreter referenced by PT_INTERP be - // either an absolute path, or relative from CWD. To work around this, we extract - // the load sections from the runtime linker ELF binary and embed them into each host - // bionic binary, omitting the PT_INTERP declaration. The kernel will treat it as a static - // binary, and then we use a special entry point to fix up the arguments passed by - // the kernel before jumping to the embedded linker. if ctx.Os() == android.LinuxBionic && !binary.static() { deps.DynamicLinker = "linker" - deps.CrtBegin = append(deps.CrtBegin, "host_bionic_linker_script") } } @@ -571,7 +571,8 @@ type specifiedDeps struct { sharedLibs []string // Note nil and [] are semantically distinct. [] prevents linking against the defaults (usually // libc, libm, etc.) - systemSharedLibs []string + systemSharedLibs []string + defaultSharedLibs []string } // installer is the interface for an installer helper object. This helper is responsible for @@ -831,6 +832,46 @@ type Module struct { hideApexVariantFromMake bool } +func (c *Module) AddJSONData(d *map[string]interface{}) { + c.AndroidModuleBase().AddJSONData(d) + (*d)["Cc"] = map[string]interface{}{ + "SdkVersion": c.SdkVersion(), + "MinSdkVersion": c.MinSdkVersion(), + "VndkVersion": c.VndkVersion(), + "ProductSpecific": c.ProductSpecific(), + "SocSpecific": c.SocSpecific(), + "DeviceSpecific": c.DeviceSpecific(), + "InProduct": c.InProduct(), + "InVendor": c.InVendor(), + "InRamdisk": c.InRamdisk(), + "InVendorRamdisk": c.InVendorRamdisk(), + "InRecovery": c.InRecovery(), + "VendorAvailable": c.VendorAvailable(), + "ProductAvailable": c.ProductAvailable(), + "RamdiskAvailable": c.RamdiskAvailable(), + "VendorRamdiskAvailable": c.VendorRamdiskAvailable(), + "RecoveryAvailable": c.RecoveryAvailable(), + "OdmAvailable": c.OdmAvailable(), + "InstallInData": c.InstallInData(), + "InstallInRamdisk": c.InstallInRamdisk(), + "InstallInSanitizerDir": c.InstallInSanitizerDir(), + "InstallInVendorRamdisk": c.InstallInVendorRamdisk(), + "InstallInRecovery": c.InstallInRecovery(), + "InstallInRoot": c.InstallInRoot(), + "IsVndk": c.IsVndk(), + "IsVndkExt": c.IsVndkExt(), + "IsVndkPrivate": c.IsVndkPrivate(), + "IsVndkSp": c.IsVndkSp(), + "IsLlndk": c.IsLlndk(), + "IsLlndkPublic": c.IsLlndkPublic(), + "IsSnapshotLibrary": c.IsSnapshotLibrary(), + "IsSnapshotPrebuilt": c.IsSnapshotPrebuilt(), + "IsVendorPublicLibrary": c.IsVendorPublicLibrary(), + "ApexSdkVersion": c.apexSdkVersion, + "TestFor": c.TestFor(), + } +} + func (c *Module) SetPreventInstall() { c.Properties.PreventInstall = true } @@ -1259,7 +1300,7 @@ func (c *Module) ImplementationModuleNameForMake(ctx android.BaseModuleContext) return name } -func (c *Module) bootstrap() bool { +func (c *Module) Bootstrap() bool { return Bool(c.Properties.Bootstrap) } @@ -1504,7 +1545,7 @@ func (ctx *moduleContextImpl) apexSdkVersion() android.ApiLevel { } func (ctx *moduleContextImpl) bootstrap() bool { - return ctx.mod.bootstrap() + return ctx.mod.Bootstrap() } func (ctx *moduleContextImpl) nativeCoverage() bool { @@ -2646,66 +2687,8 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { return } - sharedLibraryInfo := ctx.OtherModuleProvider(dep, SharedLibraryInfoProvider).(SharedLibraryInfo) - sharedLibraryStubsInfo := ctx.OtherModuleProvider(dep, SharedLibraryStubsProvider).(SharedLibraryStubsInfo) - - if !libDepTag.explicitlyVersioned && len(sharedLibraryStubsInfo.SharedStubLibraries) > 0 { - useStubs := false - - if lib := moduleLibraryInterface(dep); lib.buildStubs() && c.UseVndk() { // LLNDK - if !apexInfo.IsForPlatform() { - // For platform libraries, use current version of LLNDK - // If this is for use_vendor apex we will apply the same rules - // of apex sdk enforcement below to choose right version. - useStubs = true - } - } else if apexInfo.IsForPlatform() { - // If not building for APEX, use stubs only when it is from - // an APEX (and not from platform) - // However, for host, ramdisk, vendor_ramdisk, recovery or bootstrap modules, - // always link to non-stub variant - useStubs = dep.(android.ApexModule).NotInPlatform() && !c.bootstrap() - if useStubs { - // Another exception: if this module is a test for an APEX, then - // it is linked with the non-stub variant of a module in the APEX - // as if this is part of the APEX. - testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo) - for _, apexContents := range testFor.ApexContents { - if apexContents.DirectlyInApex(depName) { - useStubs = false - break - } - } - } - if useStubs { - // Yet another exception: If this module and the dependency are - // available to the same APEXes then skip stubs between their - // platform variants. This complements the test_for case above, - // which avoids the stubs on a direct APEX library dependency, by - // avoiding stubs for indirect test dependencies as well. - // - // TODO(b/183882457): This doesn't work if the two libraries have - // only partially overlapping apex_available. For that test_for - // modules would need to be split into APEX variants and resolved - // separately for each APEX they have access to. - if android.AvailableToSameApexes(c, dep.(android.ApexModule)) { - useStubs = false - } - } - } else { - // If building for APEX, use stubs when the parent is in any APEX that - // the child is not in. - useStubs = !android.DirectlyInAllApexes(apexInfo, depName) - } - - // when to use (unspecified) stubs, use the latest one. - if useStubs { - stubs := sharedLibraryStubsInfo.SharedStubLibraries - toUse := stubs[len(stubs)-1] - sharedLibraryInfo = toUse.SharedLibraryInfo - depExporterInfo = toUse.FlagExporterInfo - } - } + sharedLibraryInfo, returnedDepExporterInfo := ChooseStubOrImpl(ctx, dep) + depExporterInfo = returnedDepExporterInfo // Stubs lib doesn't link to the shared lib dependencies. Don't set // linkFile, depFile, and ptr. @@ -2918,6 +2901,100 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { return depPaths } +// ChooseStubOrImpl determines whether a given dependency should be redirected to the stub variant +// of the dependency or not, and returns the SharedLibraryInfo and FlagExporterInfo for the right +// dependency. The stub variant is selected when the dependency crosses a boundary where each side +// has different level of updatability. For example, if a library foo in an APEX depends on a +// library bar which provides stable interface and exists in the platform, foo uses the stub variant +// of bar. If bar doesn't provide a stable interface (i.e. buildStubs() == false) or is in the +// same APEX as foo, the non-stub variant of bar is used. +func ChooseStubOrImpl(ctx android.ModuleContext, dep android.Module) (SharedLibraryInfo, FlagExporterInfo) { + depName := ctx.OtherModuleName(dep) + depTag := ctx.OtherModuleDependencyTag(dep) + libDepTag, ok := depTag.(libraryDependencyTag) + if !ok || !libDepTag.shared() { + panic(fmt.Errorf("Unexpected dependency tag: %T", depTag)) + } + + thisModule, ok := ctx.Module().(android.ApexModule) + if !ok { + panic(fmt.Errorf("Not an APEX module: %q", ctx.ModuleName())) + } + + useVndk := false + bootstrap := false + if linkable, ok := ctx.Module().(LinkableInterface); !ok { + panic(fmt.Errorf("Not a Linkable module: %q", ctx.ModuleName())) + } else { + useVndk = linkable.UseVndk() + bootstrap = linkable.Bootstrap() + } + + sharedLibraryInfo := ctx.OtherModuleProvider(dep, SharedLibraryInfoProvider).(SharedLibraryInfo) + depExporterInfo := ctx.OtherModuleProvider(dep, FlagExporterInfoProvider).(FlagExporterInfo) + sharedLibraryStubsInfo := ctx.OtherModuleProvider(dep, SharedLibraryStubsProvider).(SharedLibraryStubsInfo) + apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + + if !libDepTag.explicitlyVersioned && len(sharedLibraryStubsInfo.SharedStubLibraries) > 0 { + useStubs := false + + if lib := moduleLibraryInterface(dep); lib.buildStubs() && useVndk { // LLNDK + if !apexInfo.IsForPlatform() { + // For platform libraries, use current version of LLNDK + // If this is for use_vendor apex we will apply the same rules + // of apex sdk enforcement below to choose right version. + useStubs = true + } + } else if apexInfo.IsForPlatform() || apexInfo.UsePlatformApis { + // If not building for APEX or the containing APEX allows the use of + // platform APIs, use stubs only when it is from an APEX (and not from + // platform) However, for host, ramdisk, vendor_ramdisk, recovery or + // bootstrap modules, always link to non-stub variant + useStubs = dep.(android.ApexModule).NotInPlatform() && !bootstrap + if useStubs { + // Another exception: if this module is a test for an APEX, then + // it is linked with the non-stub variant of a module in the APEX + // as if this is part of the APEX. + testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo) + for _, apexContents := range testFor.ApexContents { + if apexContents.DirectlyInApex(depName) { + useStubs = false + break + } + } + } + if useStubs { + // Yet another exception: If this module and the dependency are + // available to the same APEXes then skip stubs between their + // platform variants. This complements the test_for case above, + // which avoids the stubs on a direct APEX library dependency, by + // avoiding stubs for indirect test dependencies as well. + // + // TODO(b/183882457): This doesn't work if the two libraries have + // only partially overlapping apex_available. For that test_for + // modules would need to be split into APEX variants and resolved + // separately for each APEX they have access to. + if android.AvailableToSameApexes(thisModule, dep.(android.ApexModule)) { + useStubs = false + } + } + } else { + // If building for APEX, use stubs when the parent is in any APEX that + // the child is not in. + useStubs = !android.DirectlyInAllApexes(apexInfo, depName) + } + + // when to use (unspecified) stubs, use the latest one. + if useStubs { + stubs := sharedLibraryStubsInfo.SharedStubLibraries + toUse := stubs[len(stubs)-1] + sharedLibraryInfo = toUse.SharedLibraryInfo + depExporterInfo = toUse.FlagExporterInfo + } + } + return sharedLibraryInfo, depExporterInfo +} + // orderStaticModuleDeps rearranges the order of the static library dependencies of the module // to match the topological order of the dependency tree, including any static analogues of // direct shared libraries. It returns the ordered static dependencies, and an android.DepSet diff --git a/cc/cc_test.go b/cc/cc_test.go index 2d0d78b86..0a74e5824 100644 --- a/cc/cc_test.go +++ b/cc/cc_test.go @@ -3662,305 +3662,6 @@ func TestMinSdkVersionInClangTriple(t *testing.T) { android.AssertStringDoesContain(t, "min sdk version", cFlags, "-target aarch64-linux-android29") } -type MemtagNoteType int - -const ( - None MemtagNoteType = iota + 1 - Sync - Async -) - -func (t MemtagNoteType) str() string { - switch t { - case None: - return "none" - case Sync: - return "sync" - case Async: - return "async" - default: - panic("invalid note type") - } -} - -func checkHasMemtagNote(t *testing.T, m android.TestingModule, expected MemtagNoteType) { - note_async := "note_memtag_heap_async" - note_sync := "note_memtag_heap_sync" - - found := None - implicits := m.Rule("ld").Implicits - for _, lib := range implicits { - if strings.Contains(lib.Rel(), note_async) { - found = Async - break - } else if strings.Contains(lib.Rel(), note_sync) { - found = Sync - break - } - } - - if found != expected { - t.Errorf("Wrong Memtag note in target %q: found %q, expected %q", m.Module().(*Module).Name(), found.str(), expected.str()) - } -} - -var prepareForTestWithMemtagHeap = android.GroupFixturePreparers( - android.FixtureModifyMockFS(func(fs android.MockFS) { - templateBp := ` - cc_test { - name: "%[1]s_test", - gtest: false, - } - - cc_test { - name: "%[1]s_test_false", - gtest: false, - sanitize: { memtag_heap: false }, - } - - cc_test { - name: "%[1]s_test_true", - gtest: false, - sanitize: { memtag_heap: true }, - } - - cc_test { - name: "%[1]s_test_true_nodiag", - gtest: false, - sanitize: { memtag_heap: true, diag: { memtag_heap: false } }, - } - - cc_test { - name: "%[1]s_test_true_diag", - gtest: false, - sanitize: { memtag_heap: true, diag: { memtag_heap: true } }, - } - - cc_binary { - name: "%[1]s_binary", - } - - cc_binary { - name: "%[1]s_binary_false", - sanitize: { memtag_heap: false }, - } - - cc_binary { - name: "%[1]s_binary_true", - sanitize: { memtag_heap: true }, - } - - cc_binary { - name: "%[1]s_binary_true_nodiag", - sanitize: { memtag_heap: true, diag: { memtag_heap: false } }, - } - - cc_binary { - name: "%[1]s_binary_true_diag", - sanitize: { memtag_heap: true, diag: { memtag_heap: true } }, - } - ` - subdirDefaultBp := fmt.Sprintf(templateBp, "default") - subdirExcludeBp := fmt.Sprintf(templateBp, "exclude") - subdirSyncBp := fmt.Sprintf(templateBp, "sync") - subdirAsyncBp := fmt.Sprintf(templateBp, "async") - - fs.Merge(android.MockFS{ - "subdir_default/Android.bp": []byte(subdirDefaultBp), - "subdir_exclude/Android.bp": []byte(subdirExcludeBp), - "subdir_sync/Android.bp": []byte(subdirSyncBp), - "subdir_async/Android.bp": []byte(subdirAsyncBp), - }) - }), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.MemtagHeapExcludePaths = []string{"subdir_exclude"} - // "subdir_exclude" is covered by both include and exclude paths. Exclude wins. - variables.MemtagHeapSyncIncludePaths = []string{"subdir_sync", "subdir_exclude"} - variables.MemtagHeapAsyncIncludePaths = []string{"subdir_async", "subdir_exclude"} - }), -) - -func TestSanitizeMemtagHeap(t *testing.T) { - variant := "android_arm64_armv8-a" - - result := android.GroupFixturePreparers( - prepareForCcTest, - prepareForTestWithMemtagHeap, - ).RunTest(t) - ctx := result.TestContext - - checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync) -} - -func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) { - variant := "android_arm64_armv8-a" - - result := android.GroupFixturePreparers( - prepareForCcTest, - prepareForTestWithMemtagHeap, - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.SanitizeDevice = []string{"memtag_heap"} - }), - ).RunTest(t) - ctx := result.TestContext - - checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync) -} - -func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) { - variant := "android_arm64_armv8-a" - - result := android.GroupFixturePreparers( - prepareForCcTest, - prepareForTestWithMemtagHeap, - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.SanitizeDevice = []string{"memtag_heap"} - variables.SanitizeDeviceDiag = []string{"memtag_heap"} - }), - ).RunTest(t) - ctx := result.TestContext - - checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync) - - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async) - checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync) -} - func TestIncludeDirsExporting(t *testing.T) { // Trim spaces from the beginning, end and immediately after any newline characters. Leaves @@ -4345,12 +4046,12 @@ func TestIncludeDirectoryOrdering(t *testing.T) { "${config.ArmToolchainClangCflags}", "${config.ArmClangArmv7ANeonCflags}", "${config.ArmClangGenericCflags}", - "export_include_dirs", - "linux_export_include_dirs", - "android_export_include_dirs", - "arm_export_include_dirs", - "lib32_export_include_dirs", "android_arm_export_include_dirs", + "lib32_export_include_dirs", + "arm_export_include_dirs", + "android_export_include_dirs", + "linux_export_include_dirs", + "export_include_dirs", "android_arm_local_include_dirs", "lib32_local_include_dirs", "arm_local_include_dirs", diff --git a/cc/compiler.go b/cc/compiler.go index 78a5a5da3..69ead3089 100644 --- a/cc/compiler.go +++ b/cc/compiler.go @@ -92,7 +92,7 @@ type BaseCompilerProperties struct { // list of generated headers to add to the include path. These are the names // of genrule modules. - Generated_headers []string `android:"arch_variant"` + Generated_headers []string `android:"arch_variant,variant_prepend"` // pass -frtti instead of -fno-rtti Rtti *bool diff --git a/cc/config/Android.bp b/cc/config/Android.bp index e4a8b6203..c1d4f1755 100644 --- a/cc/config/Android.bp +++ b/cc/config/Android.bp @@ -17,6 +17,8 @@ bootstrap_go_package { "toolchain.go", "vndk.go", + "bionic.go", + "arm_device.go", "arm64_device.go", "arm64_fuchsia_device.go", diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go index 864fba100..af6361bff 100644 --- a/cc/config/arm64_device.go +++ b/cc/config/arm64_device.go @@ -149,6 +149,7 @@ var ( ) type toolchainArm64 struct { + toolchainBionic toolchain64Bit ldflags string diff --git a/cc/config/arm64_fuchsia_device.go b/cc/config/arm64_fuchsia_device.go index 02c0c14d7..a6b5e8c67 100644 --- a/cc/config/arm64_fuchsia_device.go +++ b/cc/config/arm64_fuchsia_device.go @@ -82,10 +82,6 @@ func (t *toolchainFuchsiaArm64) ClangCflags() string { return "--target=arm64-fuchsia --sysroot=" + fuchsiaArm64SysRoot + " -I" + fuchsiaArm64SysRoot + "/include" } -func (t *toolchainFuchsiaArm64) Bionic() bool { - return false -} - func (t *toolchainFuchsiaArm64) ToolchainClangCflags() string { return "-march=armv8-a" } diff --git a/cc/config/arm64_linux_host.go b/cc/config/arm64_linux_host.go index 59c52d162..83bd7997b 100644 --- a/cc/config/arm64_linux_host.go +++ b/cc/config/arm64_linux_host.go @@ -45,6 +45,16 @@ var ( "-Wl,--hash-style=gnu", "-Wl,--no-undefined-version", }) + + // Embed the linker into host bionic binaries. This is needed to support host bionic, + // as the linux kernel requires that the ELF interpreter referenced by PT_INTERP be + // either an absolute path, or relative from CWD. To work around this, we extract + // the load sections from the runtime linker ELF binary and embed them into each host + // bionic binary, omitting the PT_INTERP declaration. The kernel will treat it as a static + // binary, and then we use a special entry point to fix up the arguments passed by + // the kernel before jumping to the embedded linker. + linuxArm64CrtBeginSharedBinary = append(android.CopyOf(bionicCrtBeginSharedBinary), + "host_bionic_linker_script") ) func init() { @@ -68,6 +78,10 @@ func (toolchainLinuxArm64) ClangCflags() string { return "${config.Arm64ClangCflags} ${config.LinuxBionicArm64Cflags}" } +func (toolchainLinuxArm64) CrtBeginSharedBinary() []string { + return linuxArm64CrtBeginSharedBinary +} + func linuxArm64ToolchainFactory(arch android.Arch) Toolchain { archVariant := "armv8-a" // for host, default to armv8-a toolchainClangCflags := []string{arm64ClangArchVariantCflagsVar[archVariant]} diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go index 439084e3b..3c2773021 100644 --- a/cc/config/arm_device.go +++ b/cc/config/arm_device.go @@ -237,6 +237,7 @@ var ( ) type toolchainArm struct { + toolchainBionic toolchain32Bit ldflags string lldflags string diff --git a/cc/config/bionic.go b/cc/config/bionic.go new file mode 100644 index 000000000..e87f5712b --- /dev/null +++ b/cc/config/bionic.go @@ -0,0 +1,37 @@ +// Copyright 2015 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 config + +type toolchainBionic struct { +} + +var ( + bionicDefaultSharedLibraries = []string{"libc", "libm", "libdl"} + + bionicCrtBeginStaticBinary, bionicCrtEndStaticBinary = []string{"crtbegin_static"}, []string{"crtend_android"} + bionicCrtBeginSharedBinary, bionicCrtEndSharedBinary = []string{"crtbegin_dynamic"}, []string{"crtend_android"} + bionicCrtBeginSharedLibrary, bionicCrtEndSharedLibrary = []string{"crtbegin_so"}, []string{"crtend_so"} +) + +func (toolchainBionic) Bionic() bool { return true } + +func (toolchainBionic) DefaultSharedLibraries() []string { return bionicDefaultSharedLibraries } + +func (toolchainBionic) CrtBeginStaticBinary() []string { return bionicCrtBeginStaticBinary } +func (toolchainBionic) CrtBeginSharedBinary() []string { return bionicCrtBeginSharedBinary } +func (toolchainBionic) CrtBeginSharedLibrary() []string { return bionicCrtBeginSharedLibrary } +func (toolchainBionic) CrtEndStaticBinary() []string { return bionicCrtEndStaticBinary } +func (toolchainBionic) CrtEndSharedBinary() []string { return bionicCrtEndSharedBinary } +func (toolchainBionic) CrtEndSharedLibrary() []string { return bionicCrtEndSharedLibrary } diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go index fce28c1ee..ab09751ac 100644 --- a/cc/config/toolchain.go +++ b/cc/config/toolchain.go @@ -106,6 +106,17 @@ type Toolchain interface { AvailableLibraries() []string + CrtBeginStaticBinary() []string + CrtBeginSharedBinary() []string + CrtBeginSharedLibrary() []string + CrtEndStaticBinary() []string + CrtEndSharedBinary() []string + CrtEndSharedLibrary() []string + + // DefaultSharedLibraries returns the list of shared libraries that will be added to all + // targets unless they explicitly specify system_shared_libs. + DefaultSharedLibraries() []string + Bionic() bool } @@ -165,11 +176,22 @@ func (toolchainBase) LibclangRuntimeLibraryArch() string { } func (toolchainBase) AvailableLibraries() []string { - return []string{} + return nil +} + +func (toolchainBase) CrtBeginStaticBinary() []string { return nil } +func (toolchainBase) CrtBeginSharedBinary() []string { return nil } +func (toolchainBase) CrtBeginSharedLibrary() []string { return nil } +func (toolchainBase) CrtEndStaticBinary() []string { return nil } +func (toolchainBase) CrtEndSharedBinary() []string { return nil } +func (toolchainBase) CrtEndSharedLibrary() []string { return nil } + +func (toolchainBase) DefaultSharedLibraries() []string { + return nil } func (toolchainBase) Bionic() bool { - return true + return false } func (t toolchainBase) ToolPath() string { diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go index 1e25a3b8f..54dc6d523 100644 --- a/cc/config/x86_64_device.go +++ b/cc/config/x86_64_device.go @@ -123,6 +123,7 @@ func init() { } type toolchainX86_64 struct { + toolchainBionic toolchain64Bit toolchainClangCflags string } diff --git a/cc/config/x86_64_fuchsia_device.go b/cc/config/x86_64_fuchsia_device.go index 0f2013b54..d6837c8b4 100644 --- a/cc/config/x86_64_fuchsia_device.go +++ b/cc/config/x86_64_fuchsia_device.go @@ -83,10 +83,6 @@ func (t *toolchainFuchsiaX8664) ClangCflags() string { return "--target=x86_64-fuchsia --sysroot=" + fuchsiaSysRoot + " -I" + fuchsiaSysRoot + "/include" } -func (t *toolchainFuchsiaX8664) Bionic() bool { - return false -} - func (t *toolchainFuchsiaX8664) YasmFlags() string { return "-f elf64 -m amd64" } diff --git a/cc/config/x86_darwin_host.go b/cc/config/x86_darwin_host.go index b0344af84..4e3e2a69d 100644 --- a/cc/config/x86_darwin_host.go +++ b/cc/config/x86_darwin_host.go @@ -241,10 +241,6 @@ func (t *toolchainDarwin) AvailableLibraries() []string { return darwinAvailableLibraries } -func (t *toolchainDarwin) Bionic() bool { - return false -} - func (t *toolchainDarwin) ToolPath() string { return "${config.MacToolPath}" } diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go index fe830982f..1507d9813 100644 --- a/cc/config/x86_device.go +++ b/cc/config/x86_device.go @@ -134,6 +134,7 @@ func init() { } type toolchainX86 struct { + toolchainBionic toolchain32Bit toolchainClangCflags string } diff --git a/cc/config/x86_linux_bionic_host.go b/cc/config/x86_linux_bionic_host.go index fa625e34a..e7e5f2dab 100644 --- a/cc/config/x86_linux_bionic_host.go +++ b/cc/config/x86_linux_bionic_host.go @@ -63,6 +63,16 @@ var ( }) linuxBionicLldflags = ClangFilterUnknownLldflags(linuxBionicLdflags) + + // Embed the linker into host bionic binaries. This is needed to support host bionic, + // as the linux kernel requires that the ELF interpreter referenced by PT_INTERP be + // either an absolute path, or relative from CWD. To work around this, we extract + // the load sections from the runtime linker ELF binary and embed them into each host + // bionic binary, omitting the PT_INTERP declaration. The kernel will treat it as a static + // binary, and then we use a special entry point to fix up the arguments passed by + // the kernel before jumping to the embedded linker. + linuxBionicCrtBeginSharedBinary = append(android.CopyOf(bionicCrtBeginSharedBinary), + "host_bionic_linker_script") ) func init() { @@ -76,6 +86,7 @@ func init() { type toolchainLinuxBionic struct { toolchain64Bit + toolchainBionic } func (t *toolchainLinuxBionic) Name() string { @@ -133,14 +144,14 @@ func (t *toolchainLinuxBionic) AvailableLibraries() []string { return nil } -func (t *toolchainLinuxBionic) Bionic() bool { - return true -} - func (toolchainLinuxBionic) LibclangRuntimeLibraryArch() string { return "x86_64" } +func (toolchainLinuxBionic) CrtBeginSharedBinary() []string { + return linuxBionicCrtBeginSharedBinary +} + var toolchainLinuxBionicSingleton Toolchain = &toolchainLinuxBionic{} func linuxBionicToolchainFactory(arch android.Arch) Toolchain { diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go index 13b55112a..c406c885f 100644 --- a/cc/config/x86_linux_host.go +++ b/cc/config/x86_linux_host.go @@ -245,10 +245,6 @@ func (t *toolchainLinux) AvailableLibraries() []string { return linuxAvailableLibraries } -func (t *toolchainLinux) Bionic() bool { - return false -} - var toolchainLinuxX86Singleton Toolchain = &toolchainLinuxX86{} var toolchainLinuxX8664Singleton Toolchain = &toolchainLinuxX8664{} diff --git a/cc/library.go b/cc/library.go index 5b6c6236d..4fd7c7475 100644 --- a/cc/library.go +++ b/cc/library.go @@ -147,11 +147,12 @@ type StaticOrSharedProperties struct { Cflags []string `android:"arch_variant"` - Enabled *bool `android:"arch_variant"` - Whole_static_libs []string `android:"arch_variant"` - Static_libs []string `android:"arch_variant"` - Shared_libs []string `android:"arch_variant"` - System_shared_libs []string `android:"arch_variant"` + Enabled *bool `android:"arch_variant"` + Whole_static_libs []string `android:"arch_variant"` + Static_libs []string `android:"arch_variant"` + Shared_libs []string `android:"arch_variant"` + System_shared_libs []string `android:"arch_variant"` + Default_shared_libs []string `android:"arch_variant"` Export_shared_lib_headers []string `android:"arch_variant"` Export_static_lib_headers []string `android:"arch_variant"` @@ -184,11 +185,11 @@ type FlagExporterProperties struct { // be added to the include path (using -I) for this module and any module that links // against this module. Directories listed in export_include_dirs do not need to be // listed in local_include_dirs. - Export_include_dirs []string `android:"arch_variant"` + Export_include_dirs []string `android:"arch_variant,variant_prepend"` // list of directories that will be added to the system include path // using -isystem for this module and any module that links against this module. - Export_system_include_dirs []string `android:"arch_variant"` + Export_system_include_dirs []string `android:"arch_variant,variant_prepend"` Target struct { Vendor, Product struct { @@ -1156,11 +1157,17 @@ func (library *libraryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps { if library.StaticProperties.Static.System_shared_libs != nil { library.baseLinker.Properties.System_shared_libs = library.StaticProperties.Static.System_shared_libs } + if library.StaticProperties.Static.Default_shared_libs != nil { + library.baseLinker.Properties.Default_shared_libs = library.StaticProperties.Static.Default_shared_libs + } } else if library.shared() { // Compare with nil because an empty list needs to be propagated. if library.SharedProperties.Shared.System_shared_libs != nil { library.baseLinker.Properties.System_shared_libs = library.SharedProperties.Shared.System_shared_libs } + if library.SharedProperties.Shared.Default_shared_libs != nil { + library.baseLinker.Properties.Default_shared_libs = library.SharedProperties.Shared.Default_shared_libs + } } deps = library.baseLinker.linkerDeps(ctx, deps) @@ -1174,9 +1181,9 @@ func (library *libraryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.StaticProperties.Static.Export_shared_lib_headers...) deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.StaticProperties.Static.Export_static_lib_headers...) } else if library.shared() { - if ctx.toolchain().Bionic() && !Bool(library.baseLinker.Properties.Nocrt) { - deps.CrtBegin = []string{"crtbegin_so"} - deps.CrtEnd = []string{"crtend_so"} + if !Bool(library.baseLinker.Properties.Nocrt) { + deps.CrtBegin = append(deps.CrtBegin, ctx.toolchain().CrtBeginSharedLibrary()...) + deps.CrtEnd = append(deps.CrtEnd, ctx.toolchain().CrtEndSharedLibrary()...) } deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.SharedProperties.Shared.Whole_static_libs...) deps.StaticLibs = append(deps.StaticLibs, library.SharedProperties.Shared.Static_libs...) @@ -1242,6 +1249,11 @@ func (library *libraryDecorator) linkerSpecifiedDeps(specifiedDeps specifiedDeps } else { specifiedDeps.systemSharedLibs = append(specifiedDeps.systemSharedLibs, properties.System_shared_libs...) } + if specifiedDeps.defaultSharedLibs == nil { + specifiedDeps.defaultSharedLibs = properties.Default_shared_libs + } else { + specifiedDeps.defaultSharedLibs = append(specifiedDeps.defaultSharedLibs, properties.Default_shared_libs...) + } specifiedDeps.sharedLibs = android.FirstUniqueStrings(specifiedDeps.sharedLibs) if len(specifiedDeps.systemSharedLibs) > 0 { @@ -1249,6 +1261,11 @@ func (library *libraryDecorator) linkerSpecifiedDeps(specifiedDeps specifiedDeps // retained. specifiedDeps.systemSharedLibs = android.FirstUniqueStrings(specifiedDeps.systemSharedLibs) } + if len(specifiedDeps.defaultSharedLibs) > 0 { + // Skip this if defaultSharedLibs is either nil or [], to ensure they are + // retained. + specifiedDeps.defaultSharedLibs = android.FirstUniqueStrings(specifiedDeps.defaultSharedLibs) + } return specifiedDeps } diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go index 9010a1a89..9ad2742f4 100644 --- a/cc/library_sdk_member.go +++ b/cc/library_sdk_member.go @@ -258,6 +258,12 @@ func addPossiblyArchSpecificProperties(sdkModuleContext android.ModuleContext, b outputProperties.AddPropertyWithTag("system_shared_libs", libInfo.SystemSharedLibs, builder.SdkMemberReferencePropertyTag(false)) } + // SystemSharedLibs needs to be propagated if it's a list, even if it's empty, + // so check for non-nil instead of nonzero length. + if libInfo.DefaultSharedLibs != nil { + outputProperties.AddPropertyWithTag("default_shared_libs", libInfo.DefaultSharedLibs, builder.SdkMemberReferencePropertyTag(false)) + } + // Map from property name to the include dirs to add to the prebuilt module in the snapshot. includeDirs := make(map[string][]string) @@ -387,6 +393,12 @@ type nativeLibInfoProperties struct { // This field is exported as its contents may not be arch specific. SystemSharedLibs []string `android:"arch_variant"` + // The set of default shared libraries. Note nil and [] are semantically + // distinct - see BaseLinkerProperties.Default_shared_libs. + // + // This field is exported as its contents may not be arch specific. + DefaultSharedLibs []string `android:"arch_variant"` + // The specific stubs version for the lib variant, or empty string if stubs // are not in use. // @@ -462,6 +474,7 @@ func (p *nativeLibInfoProperties) PopulateFromVariant(ctx android.SdkMemberConte } } p.SystemSharedLibs = specifiedDeps.systemSharedLibs + p.DefaultSharedLibs = specifiedDeps.defaultSharedLibs } p.ExportedGeneratedHeaders = exportedInfo.GeneratedHeaders diff --git a/cc/linkable.go b/cc/linkable.go index 0a5d16c16..231626ecc 100644 --- a/cc/linkable.go +++ b/cc/linkable.go @@ -165,6 +165,9 @@ type LinkableInterface interface { // "product_specific: true" modules are included here. UseVndk() bool + // Bootstrap tests if this module is allowed to use non-APEX version of libraries. + Bootstrap() bool + // IsVndkSp returns true if this is a VNDK-SP module. IsVndkSp() bool diff --git a/cc/linker.go b/cc/linker.go index d9ee0cfde..13df23296 100644 --- a/cc/linker.go +++ b/cc/linker.go @@ -45,11 +45,18 @@ type BaseLinkerProperties struct { // list of module-specific flags that will be used for all link steps Ldflags []string `android:"arch_variant"` - // list of system libraries that will be dynamically linked to - // shared library and executable modules. If unset, generally defaults to libc, - // libm, and libdl. Set to [] to prevent linking against the defaults. + // list of system libraries that will be dynamically linked to shared library and executable + // modules that build against bionic (device or Linux bionic modules). If unset, generally + // defaults to libc, libm, and libdl. Set to [] to prevent linking against the defaults. + // Equivalent to default_shared_libs for modules that build against bionic, and ignored on + // modules that do not build against bionic. System_shared_libs []string `android:"arch_variant"` + // list of system libraries that will be dynamically linked to shared library and executable + // modules. If unset, generally defaults to libc, libm, and libdl. Set to [] to prevent + // linking against the defaults. Equivalent to system_shared_libs, but applies to all modules. + Default_shared_libs []string `android:"arch_variant"` + // allow the module to contain undefined symbols. By default, // modules cannot contain undefined symbols that are not satisified by their immediate // dependencies. Set this flag to true to remove --no-undefined from the linker flags. @@ -231,6 +238,19 @@ func (linker *baseLinker) appendLdflags(flags []string) { linker.Properties.Ldflags = append(linker.Properties.Ldflags, flags...) } +// overrideDefaultSharedLibraries returns the contents of the default_shared_libs or +// system_shared_libs properties, and records an error if both are set. +func (linker *baseLinker) overrideDefaultSharedLibraries(ctx BaseModuleContext) []string { + if linker.Properties.System_shared_libs != nil && linker.Properties.Default_shared_libs != nil { + ctx.PropertyErrorf("system_shared_libs", "cannot be specified if default_shared_libs is also specified") + } + if ctx.toolchain().Bionic() && linker.Properties.System_shared_libs != nil { + // system_shared_libs is only honored when building against bionic. + return linker.Properties.System_shared_libs + } + return linker.Properties.Default_shared_libs +} + // linkerInit initializes dynamic properties of the linker (such as runpath). func (linker *baseLinker) linkerInit(ctx BaseModuleContext) { if ctx.toolchain().Is64Bit() { @@ -331,22 +351,22 @@ func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Target.Platform.Shared_libs...) } + deps.SystemSharedLibs = linker.overrideDefaultSharedLibraries(ctx) + // In Bazel conversion mode, variations have not been specified, so SystemSharedLibs may + // inaccuarately appear unset, which can cause issues with circular dependencies. + if deps.SystemSharedLibs == nil && !ctx.BazelConversionMode() { + // Provide a default set of shared libraries if default_shared_libs and system_shared_libs + // are unspecified. Note: If an empty list [] is specified, it implies that the module + // declines the default shared libraries. + deps.SystemSharedLibs = append(deps.SystemSharedLibs, ctx.toolchain().DefaultSharedLibraries()...) + } + if ctx.toolchain().Bionic() { // libclang_rt.builtins has to be last on the command line if !Bool(linker.Properties.No_libcrt) && !ctx.header() { deps.LateStaticLibs = append(deps.LateStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain())) } - deps.SystemSharedLibs = linker.Properties.System_shared_libs - // In Bazel conversion mode, variations have not been specified, so SystemSharedLibs may - // inaccuarately appear unset, which can cause issues with circular dependencies. - if deps.SystemSharedLibs == nil && !ctx.BazelConversionMode() { - // Provide a default system_shared_libs if it is unspecified. Note: If an - // empty list [] is specified, it implies that the module declines the - // default system_shared_libs. - deps.SystemSharedLibs = []string{"libc", "libm", "libdl"} - } - if inList("libdl", deps.SharedLibs) { // If system_shared_libs has libc but not libdl, make sure shared_libs does not // have libdl to avoid loading libdl before libc. @@ -365,10 +385,10 @@ func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { indexList("libdl", deps.SystemSharedLibs) < indexList("libc", deps.SystemSharedLibs) { ctx.PropertyErrorf("system_shared_libs", "libdl must be after libc") } - - deps.LateSharedLibs = append(deps.LateSharedLibs, deps.SystemSharedLibs...) } + deps.LateSharedLibs = append(deps.LateSharedLibs, deps.SystemSharedLibs...) + if ctx.Fuchsia() { if ctx.ModuleName() != "libbioniccompat" && ctx.ModuleName() != "libcompiler_rt-extras" && @@ -573,6 +593,11 @@ func (linker *baseLinker) linkerSpecifiedDeps(specifiedDeps specifiedDeps) speci } else { specifiedDeps.systemSharedLibs = append(specifiedDeps.systemSharedLibs, linker.Properties.System_shared_libs...) } + if specifiedDeps.defaultSharedLibs == nil { + specifiedDeps.defaultSharedLibs = linker.Properties.Default_shared_libs + } else { + specifiedDeps.defaultSharedLibs = append(specifiedDeps.defaultSharedLibs, linker.Properties.Default_shared_libs...) + } return specifiedDeps } diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py index 09551eabb..c8cd056a4 100755 --- a/cc/ndkstubgen/test_ndkstubgen.py +++ b/cc/ndkstubgen/test_ndkstubgen.py @@ -413,6 +413,40 @@ class IntegrationTest(unittest.TestCase): """) self.assertEqual(expected_version, version_file.getvalue()) + def test_empty_stub(self) -> None: + """Tests that empty stubs can be generated. + + This is not a common case, but libraries whose only behavior is to + interpose symbols to alter existing behavior do not need to expose + their interposing symbols as API, so it's possible for the stub to be + empty while still needing a stub to link against. libsigchain is an + example of this. + """ + input_file = io.StringIO(textwrap.dedent("""\ + VERSION_1 { + local: + *; + }; + """)) + parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), + 9, llndk=False, apex=True) + versions = parser.parse() + + src_file = io.StringIO() + version_file = io.StringIO() + symbol_list_file = io.StringIO() + generator = ndkstubgen.Generator(src_file, + version_file, + symbol_list_file, + Arch('arm'), + 9, + llndk=False, + apex=True) + generator.write(versions) + + self.assertEqual('', src_file.getvalue()) + self.assertEqual('', version_file.getvalue()) + def main() -> None: suite = unittest.TestLoader().loadTestsFromName(__name__) diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go index f12634678..4430fc38f 100644 --- a/cc/sanitize_test.go +++ b/cc/sanitize_test.go @@ -15,6 +15,8 @@ package cc import ( + "fmt" + "strings" "testing" "android/soong/android" @@ -202,3 +204,302 @@ func TestAsan(t *testing.T) { 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") }) } + +type MemtagNoteType int + +const ( + None MemtagNoteType = iota + 1 + Sync + Async +) + +func (t MemtagNoteType) str() string { + switch t { + case None: + return "none" + case Sync: + return "sync" + case Async: + return "async" + default: + panic("invalid note type") + } +} + +func checkHasMemtagNote(t *testing.T, m android.TestingModule, expected MemtagNoteType) { + note_async := "note_memtag_heap_async" + note_sync := "note_memtag_heap_sync" + + found := None + implicits := m.Rule("ld").Implicits + for _, lib := range implicits { + if strings.Contains(lib.Rel(), note_async) { + found = Async + break + } else if strings.Contains(lib.Rel(), note_sync) { + found = Sync + break + } + } + + if found != expected { + t.Errorf("Wrong Memtag note in target %q: found %q, expected %q", m.Module().(*Module).Name(), found.str(), expected.str()) + } +} + +var prepareForTestWithMemtagHeap = android.GroupFixturePreparers( + android.FixtureModifyMockFS(func(fs android.MockFS) { + templateBp := ` + cc_test { + name: "%[1]s_test", + gtest: false, + } + + cc_test { + name: "%[1]s_test_false", + gtest: false, + sanitize: { memtag_heap: false }, + } + + cc_test { + name: "%[1]s_test_true", + gtest: false, + sanitize: { memtag_heap: true }, + } + + cc_test { + name: "%[1]s_test_true_nodiag", + gtest: false, + sanitize: { memtag_heap: true, diag: { memtag_heap: false } }, + } + + cc_test { + name: "%[1]s_test_true_diag", + gtest: false, + sanitize: { memtag_heap: true, diag: { memtag_heap: true } }, + } + + cc_binary { + name: "%[1]s_binary", + } + + cc_binary { + name: "%[1]s_binary_false", + sanitize: { memtag_heap: false }, + } + + cc_binary { + name: "%[1]s_binary_true", + sanitize: { memtag_heap: true }, + } + + cc_binary { + name: "%[1]s_binary_true_nodiag", + sanitize: { memtag_heap: true, diag: { memtag_heap: false } }, + } + + cc_binary { + name: "%[1]s_binary_true_diag", + sanitize: { memtag_heap: true, diag: { memtag_heap: true } }, + } + ` + subdirDefaultBp := fmt.Sprintf(templateBp, "default") + subdirExcludeBp := fmt.Sprintf(templateBp, "exclude") + subdirSyncBp := fmt.Sprintf(templateBp, "sync") + subdirAsyncBp := fmt.Sprintf(templateBp, "async") + + fs.Merge(android.MockFS{ + "subdir_default/Android.bp": []byte(subdirDefaultBp), + "subdir_exclude/Android.bp": []byte(subdirExcludeBp), + "subdir_sync/Android.bp": []byte(subdirSyncBp), + "subdir_async/Android.bp": []byte(subdirAsyncBp), + }) + }), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.MemtagHeapExcludePaths = []string{"subdir_exclude"} + // "subdir_exclude" is covered by both include and exclude paths. Exclude wins. + variables.MemtagHeapSyncIncludePaths = []string{"subdir_sync", "subdir_exclude"} + variables.MemtagHeapAsyncIncludePaths = []string{"subdir_async", "subdir_exclude"} + }), +) + +func TestSanitizeMemtagHeap(t *testing.T) { + variant := "android_arm64_armv8-a" + + result := android.GroupFixturePreparers( + prepareForCcTest, + prepareForTestWithMemtagHeap, + ).RunTest(t) + ctx := result.TestContext + + checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync) +} + +func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) { + variant := "android_arm64_armv8-a" + + result := android.GroupFixturePreparers( + prepareForCcTest, + prepareForTestWithMemtagHeap, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.SanitizeDevice = []string{"memtag_heap"} + }), + ).RunTest(t) + ctx := result.TestContext + + checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync) +} + +func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) { + variant := "android_arm64_armv8-a" + + result := android.GroupFixturePreparers( + prepareForCcTest, + prepareForTestWithMemtagHeap, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.SanitizeDevice = []string{"memtag_heap"} + variables.SanitizeDeviceDiag = []string{"memtag_heap"} + }), + ).RunTest(t) + ctx := result.TestContext + + checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync) + + checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async) + checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync) +} diff --git a/dexpreopt/OWNERS b/dexpreopt/OWNERS index 166472fcd..5a2a1983f 100644 --- a/dexpreopt/OWNERS +++ b/dexpreopt/OWNERS @@ -1 +1 @@ -per-file * = ngeoffray@google.com,calin@google.com,mathieuc@google.com +per-file * = ngeoffray@google.com,calin@google.com,skvadrik@google.com diff --git a/genrule/genrule.go b/genrule/genrule.go index 77dae755a..8372a6450 100644 --- a/genrule/genrule.go +++ b/genrule/genrule.go @@ -211,6 +211,22 @@ func (g *Module) GeneratedDeps() android.Paths { return g.outputDeps } +func (g *Module) OutputFiles(tag string) (android.Paths, error) { + if tag == "" { + return append(android.Paths{}, g.outputFiles...), nil + } + // otherwise, tag should match one of outputs + for _, outputFile := range g.outputFiles { + if outputFile.Rel() == tag { + return android.Paths{outputFile}, nil + } + } + return nil, fmt.Errorf("unsupported module reference tag %q", tag) +} + +var _ android.SourceFileProducer = (*Module)(nil) +var _ android.OutputFileProducer = (*Module)(nil) + func toolDepsMutator(ctx android.BottomUpMutatorContext) { if g, ok := ctx.Module().(*Module); ok { for _, tool := range g.properties.Tools { diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go index 3ce4f85f9..714d2f8a9 100644 --- a/genrule/genrule_test.go +++ b/genrule/genrule_test.go @@ -31,12 +31,12 @@ func TestMain(m *testing.M) { var prepareForGenRuleTest = android.GroupFixturePreparers( android.PrepareForTestWithArchMutator, android.PrepareForTestWithDefaults, - android.PrepareForTestWithFilegroup, PrepareForTestWithGenRuleBuildComponents, android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { ctx.RegisterModuleType("tool", toolFactory) ctx.RegisterModuleType("output", outputProducerFactory) + ctx.RegisterModuleType("use_source", useSourceFactory) }), android.FixtureMergeMockFs(android.MockFS{ "tool": nil, @@ -684,6 +684,42 @@ func TestGenruleAllowMissingDependencies(t *testing.T) { } } +func TestGenruleOutputFiles(t *testing.T) { + bp := ` + genrule { + name: "gen", + out: ["foo", "sub/bar"], + cmd: "echo foo > $(location foo) && echo bar > $(location sub/bar)", + } + use_source { + name: "gen_foo", + srcs: [":gen{foo}"], + } + use_source { + name: "gen_bar", + srcs: [":gen{sub/bar}"], + } + use_source { + name: "gen_all", + srcs: [":gen"], + } + ` + + result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp) + android.AssertPathsRelativeToTopEquals(t, + "genrule.tag with output", + []string{"out/soong/.intermediates/gen/gen/foo"}, + result.ModuleForTests("gen_foo", "").Module().(*useSource).srcs) + android.AssertPathsRelativeToTopEquals(t, + "genrule.tag with output in subdir", + []string{"out/soong/.intermediates/gen/gen/sub/bar"}, + result.ModuleForTests("gen_bar", "").Module().(*useSource).srcs) + android.AssertPathsRelativeToTopEquals(t, + "genrule.tag with all", + []string{"out/soong/.intermediates/gen/gen/foo", "out/soong/.intermediates/gen/gen/sub/bar"}, + result.ModuleForTests("gen_all", "").Module().(*useSource).srcs) +} + func TestGenruleWithBazel(t *testing.T) { bp := ` genrule { @@ -750,3 +786,22 @@ func (t *testOutputProducer) OutputFiles(tag string) (android.Paths, error) { } var _ android.OutputFileProducer = (*testOutputProducer)(nil) + +type useSource struct { + android.ModuleBase + props struct { + Srcs []string `android:"path"` + } + srcs android.Paths +} + +func (s *useSource) GenerateAndroidBuildActions(ctx android.ModuleContext) { + s.srcs = android.PathsForModuleSrc(ctx, s.props.Srcs) +} + +func useSourceFactory() android.Module { + module := &useSource{} + module.AddProperties(&module.props) + android.InitAndroidModule(module) + return module +} diff --git a/java/Android.bp b/java/Android.bp index 59526024a..e5b8f9604 100644 --- a/java/Android.bp +++ b/java/Android.bp @@ -93,6 +93,7 @@ bootstrap_go_package { "plugin_test.go", "rro_test.go", "sdk_test.go", + "sdk_library_test.go", "system_modules_test.go", "systemserver_classpath_fragment_test.go", ], diff --git a/java/OWNERS b/java/OWNERS index 16ef4d812..52427120d 100644 --- a/java/OWNERS +++ b/java/OWNERS @@ -1 +1 @@ -per-file dexpreopt*.go = ngeoffray@google.com,calin@google.com,mathieuc@google.com +per-file dexpreopt*.go = ngeoffray@google.com,calin@google.com,skvadrik@google.com diff --git a/java/app_import.go b/java/app_import.go index 6fe620407..5a87b074b 100644 --- a/java/app_import.go +++ b/java/app_import.go @@ -61,7 +61,7 @@ type AndroidAppImport struct { type AndroidAppImportProperties struct { // A prebuilt apk to import - Apk *string + Apk *string `android:"path"` // The name of a certificate in the default certificate directory or an android_app_certificate // module name in the form ":module". Should be empty if presigned or default_dev_cert is set. diff --git a/java/base.go b/java/base.go index a251c3f3e..6b8119619 100644 --- a/java/base.go +++ b/java/base.go @@ -870,6 +870,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { if aaptSrcJar != nil { srcJars = append(srcJars, aaptSrcJar) } + srcFiles = srcFiles.FilterOutByExt(".srcjar") if j.properties.Jarjar_rules != nil { j.expandJarjarRules = android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules) @@ -1292,6 +1293,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { j.linter.minSdkVersion = lintSDKVersionString(j.MinSdkVersion(ctx)) j.linter.targetSdkVersion = lintSDKVersionString(j.TargetSdkVersion(ctx)) j.linter.compileSdkVersion = lintSDKVersionString(j.SdkVersion(ctx)) + j.linter.compileSdkKind = j.SdkVersion(ctx).Kind j.linter.javaLanguageLevel = flags.javaVersion.String() j.linter.kotlinLanguageLevel = "1.3" if !apexInfo.IsForPlatform() && ctx.Config().UnbundledBuildApps() { diff --git a/java/bootclasspath.go b/java/bootclasspath.go index ccb69a0ae..4108770cd 100644 --- a/java/bootclasspath.go +++ b/java/bootclasspath.go @@ -227,13 +227,13 @@ type BootclasspathAPIProperties struct { Core_platform_api BootclasspathNestedAPIProperties } -// sdkKindToStubLibs calculates the stub library modules for each relevant android.SdkKind from the +// apiScopeToStubLibs calculates the stub library modules for each relevant *HiddenAPIScope from the // Stub_libs properties. -func (p BootclasspathAPIProperties) sdkKindToStubLibs() map[android.SdkKind][]string { - m := map[android.SdkKind][]string{} - for _, kind := range []android.SdkKind{android.SdkPublic, android.SdkSystem, android.SdkTest} { - m[kind] = p.Api.Stub_libs +func (p BootclasspathAPIProperties) apiScopeToStubLibs() map[*HiddenAPIScope][]string { + m := map[*HiddenAPIScope][]string{} + for _, apiScope := range hiddenAPISdkLibrarySupportedScopes { + m[apiScope] = p.Api.Stub_libs } - m[android.SdkCorePlatform] = p.Core_platform_api.Stub_libs + m[CorePlatformHiddenAPIScope] = p.Core_platform_api.Stub_libs return m } diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index c7249b0ca..515dd89d3 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -121,8 +121,18 @@ type bootclasspathFragmentProperties struct { BootclasspathFragmentCoverageAffectedProperties Coverage BootclasspathFragmentCoverageAffectedProperties + // Hidden API related properties. Hidden_api HiddenAPIFlagFileProperties + // The list of additional stub libraries which this fragment's contents use but which are not + // provided by another bootclasspath_fragment. + // + // Note, "android-non-updatable" is treated specially. While no such module exists it is treated + // as if it was a java_sdk_library. So, when public API stubs are needed then it will be replaced + // with "android-non-updatable.stubs", with "androidn-non-updatable.system.stubs" when the system + // stubs are needed and so on. + Additional_stubs []string + // 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. @@ -378,7 +388,16 @@ func (b *BootclasspathFragmentModule) ComponentDepsMutator(ctx android.BottomUpM func (b *BootclasspathFragmentModule) DepsMutator(ctx android.BottomUpMutatorContext) { // Add dependencies onto all the modules that provide the API stubs for classes on this // bootclasspath fragment. - hiddenAPIAddStubLibDependencies(ctx, b.properties.sdkKindToStubLibs()) + hiddenAPIAddStubLibDependencies(ctx, b.properties.apiScopeToStubLibs()) + + for _, additionalStubModule := range b.properties.Additional_stubs { + for _, apiScope := range hiddenAPISdkLibrarySupportedScopes { + // Add a dependency onto a possibly scope specific stub library. + scopeSpecificDependency := apiScope.scopeSpecificStubModule(ctx, additionalStubModule) + tag := hiddenAPIStubsDependencyTag{apiScope: apiScope, fromAdditionalDependency: true} + ctx.AddVariationDependencies(nil, tag, scopeSpecificDependency) + } + } if SkipDexpreoptBootJars(ctx) { return @@ -595,7 +614,7 @@ func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android. // Other bootclasspath_fragments that depend on this need the transitive set of stub dex jars // from this to resolve any references from their code to classes provided by this fragment // and the fragments this depends upon. - TransitiveStubDexJarsByKind: input.transitiveStubDexJarsByKind(), + TransitiveStubDexJarsByScope: input.transitiveStubDexJarsByScope(), } // The monolithic hidden API processing also needs access to all the output files produced by @@ -639,8 +658,8 @@ func (b *BootclasspathFragmentModule) createHiddenAPIFlagInput(ctx android.Modul // Populate with flag file paths from the properties. input.extractFlagFilesFromProperties(ctx, &b.properties.Hidden_api) - // Store the stub dex jars from this module's fragment dependencies. - input.DependencyStubDexJarsByKind = dependencyHiddenApiInfo.TransitiveStubDexJarsByKind + // Add the stub dex jars from this module's fragment dependencies. + input.DependencyStubDexJarsByScope.addStubDexJarsByModule(dependencyHiddenApiInfo.TransitiveStubDexJarsByScope) return input } diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go index fba7d1a71..b46988636 100644 --- a/java/bootclasspath_fragment_test.go +++ b/java/bootclasspath_fragment_test.go @@ -245,17 +245,34 @@ func TestBootclasspathFragment_StubLibs(t *testing.T) { otherPublicStubsJar := "out/soong/.intermediates/myothersdklibrary.stubs/android_common/dex/myothersdklibrary.stubs.jar" // Check that SdkPublic uses public stubs for all sdk libraries. - android.AssertPathsRelativeToTopEquals(t, "public dex stubs jar", []string{otherPublicStubsJar, publicStubsJar, stubsJar}, info.TransitiveStubDexJarsByKind[android.SdkPublic]) + android.AssertPathsRelativeToTopEquals(t, "public dex stubs jar", []string{otherPublicStubsJar, publicStubsJar, stubsJar}, info.TransitiveStubDexJarsByScope.StubDexJarsForScope(PublicHiddenAPIScope)) // Check that SdkSystem uses system stubs for mysdklibrary and public stubs for myothersdklibrary // as it does not provide system stubs. - android.AssertPathsRelativeToTopEquals(t, "system dex stubs jar", []string{otherPublicStubsJar, systemStubsJar, stubsJar}, info.TransitiveStubDexJarsByKind[android.SdkSystem]) + android.AssertPathsRelativeToTopEquals(t, "system dex stubs jar", []string{otherPublicStubsJar, systemStubsJar, stubsJar}, info.TransitiveStubDexJarsByScope.StubDexJarsForScope(SystemHiddenAPIScope)) // Check that SdkTest also uses system stubs for mysdklibrary as it does not provide test stubs // and public stubs for myothersdklibrary as it does not provide test stubs either. - android.AssertPathsRelativeToTopEquals(t, "test dex stubs jar", []string{otherPublicStubsJar, systemStubsJar, stubsJar}, info.TransitiveStubDexJarsByKind[android.SdkTest]) + android.AssertPathsRelativeToTopEquals(t, "test dex stubs jar", []string{otherPublicStubsJar, systemStubsJar, stubsJar}, info.TransitiveStubDexJarsByScope.StubDexJarsForScope(TestHiddenAPIScope)) // Check that SdkCorePlatform uses public stubs from the mycoreplatform library. corePlatformStubsJar := "out/soong/.intermediates/mycoreplatform.stubs/android_common/dex/mycoreplatform.stubs.jar" - android.AssertPathsRelativeToTopEquals(t, "core platform dex stubs jar", []string{corePlatformStubsJar}, info.TransitiveStubDexJarsByKind[android.SdkCorePlatform]) + android.AssertPathsRelativeToTopEquals(t, "core platform dex stubs jar", []string{corePlatformStubsJar}, info.TransitiveStubDexJarsByScope.StubDexJarsForScope(CorePlatformHiddenAPIScope)) + + // Check the widest stubs.. The list contains the widest stub dex jar provided by each module. + expectedWidestPaths := []string{ + // mycoreplatform's widest API is core platform. + corePlatformStubsJar, + + // myothersdklibrary's widest API is public. + otherPublicStubsJar, + + // sdklibrary's widest API is system. + systemStubsJar, + + // mystublib's only provides one API and so it must be the widest. + stubsJar, + } + + android.AssertPathsRelativeToTopEquals(t, "widest dex stubs jar", expectedWidestPaths, info.TransitiveStubDexJarsByScope.StubDexJarsForWidestAPIScope()) } diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go index 6e2261480..0895951ab 100644 --- a/java/hiddenapi_modular.go +++ b/java/hiddenapi_modular.go @@ -25,9 +25,160 @@ import ( // Contains support for processing hiddenAPI in a modular fashion. +// HiddenAPIScope encapsulates all the information that the hidden API processing needs about API +// scopes, i.e. what is called android.SdkKind and apiScope. It does not just use those as they do +// not provide the information needed by hidden API processing. +type HiddenAPIScope struct { + // The name of the scope, used for debug purposes. + name string + + // The corresponding android.SdkKind, used for retrieving paths from java_sdk_library* modules. + sdkKind android.SdkKind + + // The option needed to passed to "hiddenapi list". + hiddenAPIListOption string + + // The name sof the source stub library modules that contain the API provided by the platform, + // i.e. by modules that are not in an APEX. + nonUpdatableSourceModule string + + // The names of the prebuilt stub library modules that contain the API provided by the platform, + // i.e. by modules that are not in an APEX. + nonUpdatablePrebuiltModule string +} + +// initHiddenAPIScope initializes the scope. +func initHiddenAPIScope(apiScope *HiddenAPIScope) *HiddenAPIScope { + sdkKind := apiScope.sdkKind + // The platform does not provide a core platform API. + if sdkKind != android.SdkCorePlatform { + kindAsString := sdkKind.String() + var insert string + if sdkKind == android.SdkPublic { + insert = "" + } else { + insert = "." + strings.ReplaceAll(kindAsString, "-", "_") + } + + nonUpdatableModule := "android-non-updatable" + + // Construct the name of the android-non-updatable source module for this scope. + apiScope.nonUpdatableSourceModule = fmt.Sprintf("%s.stubs%s", nonUpdatableModule, insert) + + prebuiltModuleName := func(name string, kind string) string { + return fmt.Sprintf("sdk_%s_current_%s", kind, name) + } + + // Construct the name of the android-non-updatable prebuilt module for this scope. + apiScope.nonUpdatablePrebuiltModule = prebuiltModuleName(nonUpdatableModule, kindAsString) + } + + return apiScope +} + +// android-non-updatable takes the name of a module and returns a possibly scope specific name of +// the module. +func (l *HiddenAPIScope) scopeSpecificStubModule(ctx android.BaseModuleContext, name string) string { + // The android-non-updatable is not a java_sdk_library but there are separate stub libraries for + // each scope. + // TODO(b/192067200): Remove special handling of android-non-updatable. + if name == "android-non-updatable" { + if ctx.Config().AlwaysUsePrebuiltSdks() { + return l.nonUpdatablePrebuiltModule + } else { + return l.nonUpdatableSourceModule + } + } else { + // Assume that the module is either a java_sdk_library (or equivalent) and so will provide + // separate stub jars for each scope or is a java_library (or equivalent) in which case it will + // have the same stub jar for each scope. + return name + } +} + +func (l *HiddenAPIScope) String() string { + return fmt.Sprintf("HiddenAPIScope{%s}", l.name) +} + +var ( + PublicHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{ + name: "public", + sdkKind: android.SdkPublic, + hiddenAPIListOption: "--public-stub-classpath", + }) + SystemHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{ + name: "system", + sdkKind: android.SdkSystem, + hiddenAPIListOption: "--system-stub-classpath", + }) + TestHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{ + name: "test", + sdkKind: android.SdkTest, + hiddenAPIListOption: "--test-stub-classpath", + }) + ModuleLibHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{ + name: "module-lib", + sdkKind: android.SdkModule, + }) + CorePlatformHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{ + name: "core-platform", + sdkKind: android.SdkCorePlatform, + hiddenAPIListOption: "--core-platform-stub-classpath", + }) + + // hiddenAPIRelevantSdkKinds lists all the android.SdkKind instances that are needed by the hidden + // API processing. + // + // These are roughly in order from narrowest API surface to widest. Widest means the API stubs + // with the biggest API surface, e.g. test is wider than system is wider than public. + // + // Core platform is considered wider than system/module-lib because those modules that provide + // core platform APIs either do not have any system/module-lib APIs at all, or if they do it is + // because the core platform API is being converted to system/module-lib APIs. In either case the + // system/module-lib APIs are subsets of the core platform API. + // + // This is not strictly in order from narrowest to widest as the Test API is wider than system but + // is neither wider or narrower than the module-lib or core platform APIs. However, this works + // well enough at the moment. + // TODO(b/191644675): Correctly reflect the sub/superset relationships between APIs. + hiddenAPIScopes = []*HiddenAPIScope{ + PublicHiddenAPIScope, + SystemHiddenAPIScope, + TestHiddenAPIScope, + ModuleLibHiddenAPIScope, + CorePlatformHiddenAPIScope, + } + + // The HiddenAPIScope instances that are supported by a java_sdk_library. + // + // CorePlatformHiddenAPIScope is not used as the java_sdk_library does not have special support + // for core_platform API, instead it is implemented as a customized form of PublicHiddenAPIScope. + hiddenAPISdkLibrarySupportedScopes = []*HiddenAPIScope{ + PublicHiddenAPIScope, + SystemHiddenAPIScope, + TestHiddenAPIScope, + ModuleLibHiddenAPIScope, + } + + // The HiddenAPIScope instances that are supported by the `hiddenapi list`. + hiddenAPIFlagScopes = []*HiddenAPIScope{ + PublicHiddenAPIScope, + SystemHiddenAPIScope, + TestHiddenAPIScope, + CorePlatformHiddenAPIScope, + } +) + type hiddenAPIStubsDependencyTag struct { blueprint.BaseDependencyTag - sdkKind android.SdkKind + + // The api scope for which this dependency was added. + apiScope *HiddenAPIScope + + // Indicates that the dependency is not for an API provided by the current bootclasspath fragment + // but is an additional API provided by a module that is not part of the current bootclasspath + // fragment. + fromAdditionalDependency bool } func (b hiddenAPIStubsDependencyTag) ExcludeFromApexContents() { @@ -38,6 +189,11 @@ func (b hiddenAPIStubsDependencyTag) ReplaceSourceWithPrebuilt() bool { } func (b hiddenAPIStubsDependencyTag) SdkMemberType(child android.Module) android.SdkMemberType { + // Do not add additional dependencies to the sdk. + if b.fromAdditionalDependency { + return nil + } + // If the module is a java_sdk_library then treat it as if it was specific in the java_sdk_libs // property, otherwise treat if it was specified in the java_header_libs property. if javaSdkLibrarySdkMemberType.IsInstance(child) { @@ -65,24 +221,9 @@ var _ android.ReplaceSourceWithPrebuilt = hiddenAPIStubsDependencyTag{} var _ android.ExcludeFromApexContentsTag = hiddenAPIStubsDependencyTag{} var _ android.SdkMemberTypeDependencyTag = hiddenAPIStubsDependencyTag{} -// hiddenAPIRelevantSdkKinds lists all the android.SdkKind instances that are needed by the hidden -// API processing. -// -// These are in order from narrowest API surface to widest. Widest means the API stubs with the -// biggest API surface, e.g. test is wider than system is wider than public. Core platform is -// considered wider than test even though it has no relationship with test because the libraries -// that provide core platform API don't provide test. While the core platform API is being converted -// to a system API the system API is still a subset of core platform. -var hiddenAPIRelevantSdkKinds = []android.SdkKind{ - android.SdkPublic, - android.SdkSystem, - android.SdkTest, - android.SdkCorePlatform, -} - // hiddenAPIComputeMonolithicStubLibModules computes the set of module names that provide stubs // needed to produce the hidden API monolithic stub flags file. -func hiddenAPIComputeMonolithicStubLibModules(config android.Config) map[android.SdkKind][]string { +func hiddenAPIComputeMonolithicStubLibModules(config android.Config) map[*HiddenAPIScope][]string { var publicStubModules []string var systemStubModules []string var testStubModules []string @@ -115,22 +256,22 @@ func hiddenAPIComputeMonolithicStubLibModules(config android.Config) map[android testStubModules = append(testStubModules, "jacoco-stubs") } - m := map[android.SdkKind][]string{} - m[android.SdkPublic] = publicStubModules - m[android.SdkSystem] = systemStubModules - m[android.SdkTest] = testStubModules - m[android.SdkCorePlatform] = corePlatformStubModules + m := map[*HiddenAPIScope][]string{} + m[PublicHiddenAPIScope] = publicStubModules + m[SystemHiddenAPIScope] = systemStubModules + m[TestHiddenAPIScope] = testStubModules + m[CorePlatformHiddenAPIScope] = corePlatformStubModules return m } // hiddenAPIAddStubLibDependencies adds dependencies onto the modules specified in -// sdkKindToStubLibModules. It adds them in a well known order and uses an SdkKind specific tag to -// identify the source of the dependency. -func hiddenAPIAddStubLibDependencies(ctx android.BottomUpMutatorContext, sdkKindToStubLibModules map[android.SdkKind][]string) { +// apiScopeToStubLibModules. It adds them in a well known order and uses a HiddenAPIScope specific +// tag to identify the source of the dependency. +func hiddenAPIAddStubLibDependencies(ctx android.BottomUpMutatorContext, apiScopeToStubLibModules map[*HiddenAPIScope][]string) { module := ctx.Module() - for _, sdkKind := range hiddenAPIRelevantSdkKinds { - modules := sdkKindToStubLibModules[sdkKind] - ctx.AddDependency(module, hiddenAPIStubsDependencyTag{sdkKind: sdkKind}, modules...) + for _, apiScope := range hiddenAPIScopes { + modules := apiScopeToStubLibModules[apiScope] + ctx.AddDependency(module, hiddenAPIStubsDependencyTag{apiScope: apiScope}, modules...) } } @@ -153,33 +294,21 @@ func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android. return dexJar } -var sdkKindToHiddenapiListOption = map[android.SdkKind]string{ - android.SdkPublic: "public-stub-classpath", - android.SdkSystem: "system-stub-classpath", - android.SdkTest: "test-stub-classpath", - android.SdkCorePlatform: "core-platform-stub-classpath", -} - -// ruleToGenerateHiddenAPIStubFlagsFile creates a rule to create a hidden API stub flags file. +// buildRuleToGenerateHiddenAPIStubFlagsFile creates a rule to create a hidden API stub flags file. // // The rule is initialized but not built so that the caller can modify it and select an appropriate // name. -func ruleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, outputPath android.WritablePath, bootDexJars android.Paths, input HiddenAPIFlagInput) *android.RuleBuilder { +func buildRuleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, name, desc string, outputPath android.WritablePath, bootDexJars android.Paths, input HiddenAPIFlagInput, moduleStubFlagsPaths android.Paths) { // Singleton rule which applies hiddenapi on all boot class path dex files. rule := android.NewRuleBuilder(pctx, ctx) tempPath := tempPathForRestat(ctx, outputPath) // Find the widest API stubs provided by the fragments on which this depends, if any. - var dependencyStubDexJars android.Paths - for i := len(hiddenAPIRelevantSdkKinds) - 1; i >= 0; i-- { - kind := hiddenAPIRelevantSdkKinds[i] - stubsForKind := input.DependencyStubDexJarsByKind[kind] - if len(stubsForKind) != 0 { - dependencyStubDexJars = stubsForKind - break - } - } + dependencyStubDexJars := input.DependencyStubDexJarsByScope.StubDexJarsForWidestAPIScope() + + // Add widest API stubs from the additional dependencies of this, if any. + dependencyStubDexJars = append(dependencyStubDexJars, input.AdditionalStubDexJarsByScope.StubDexJarsForWidestAPIScope()...) command := rule.Command(). Tool(ctx.Config().HostToolPath(ctx, "hiddenapi")). @@ -187,24 +316,46 @@ func ruleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, outputPath FlagForEachInput("--dependency-stub-dex=", dependencyStubDexJars). FlagForEachInput("--boot-dex=", bootDexJars) - // Iterate over the sdk kinds in a fixed order. - for _, sdkKind := range hiddenAPIRelevantSdkKinds { - // Merge in the stub dex jar paths for this kind from the fragments on which it depends. They - // will be needed to resolve dependencies from this fragment's stubs to classes in the other - // fragment's APIs. - dependencyPaths := input.DependencyStubDexJarsByKind[sdkKind] - paths := append(dependencyPaths, input.StubDexJarsByKind[sdkKind]...) + // If no module stub flags paths are provided then this must be being called for a + // bootclasspath_fragment and not the whole platform_bootclasspath. + if moduleStubFlagsPaths == nil { + // This is being run on a fragment of the bootclasspath. + command.Flag("--fragment") + } + + // Iterate over the api scopes in a fixed order. + for _, apiScope := range hiddenAPIFlagScopes { + // Merge in the stub dex jar paths for this api scope from the fragments on which it depends. + // They will be needed to resolve dependencies from this fragment's stubs to classes in the + // other fragment's APIs. + var paths android.Paths + paths = append(paths, input.DependencyStubDexJarsByScope.StubDexJarsForScope(apiScope)...) + paths = append(paths, input.AdditionalStubDexJarsByScope.StubDexJarsForScope(apiScope)...) + paths = append(paths, input.StubDexJarsByScope.StubDexJarsForScope(apiScope)...) if len(paths) > 0 { - option := sdkKindToHiddenapiListOption[sdkKind] - command.FlagWithInputList("--"+option+"=", paths, ":") + option := apiScope.hiddenAPIListOption + command.FlagWithInputList(option+"=", paths, ":") } } // Add the output path. command.FlagWithOutput("--out-api-flags=", tempPath) + // If there are stub flag files that have been generated by fragments on which this depends then + // use them to validate the stub flag file generated by the rules created by this method. + if len(moduleStubFlagsPaths) > 0 { + validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, moduleStubFlagsPaths) + + // Add the file that indicates that the file generated by this is valid. + // + // This will cause the validation rule above to be run any time that the output of this rule + // changes but the validation will run in parallel with other rules that depend on this file. + command.Validation(validFile) + } + commitChangeForRestat(rule, tempPath, outputPath) - return rule + + rule.Build(name, desc) } // HiddenAPIFlagFileProperties contains paths to the flag files that can be used to augment the @@ -377,8 +528,9 @@ type HiddenAPIInfo struct { // that category. FlagFilesByCategory FlagFilesByCategory - // The paths to the stub dex jars for each of the android.SdkKind in hiddenAPIRelevantSdkKinds. - TransitiveStubDexJarsByKind StubDexJarsByKind + // The paths to the stub dex jars for each of the *HiddenAPIScope in hiddenAPIScopes provided by + // this fragment and the fragments on which this depends. + TransitiveStubDexJarsByScope StubDexJarsByModule // The output from the hidden API processing needs to be made available to other modules. HiddenAPIFlagOutput @@ -386,8 +538,8 @@ type HiddenAPIInfo struct { func newHiddenAPIInfo() *HiddenAPIInfo { info := HiddenAPIInfo{ - FlagFilesByCategory: FlagFilesByCategory{}, - TransitiveStubDexJarsByKind: StubDexJarsByKind{}, + FlagFilesByCategory: FlagFilesByCategory{}, + TransitiveStubDexJarsByScope: StubDexJarsByModule{}, } return &info } @@ -398,36 +550,113 @@ func (i *HiddenAPIInfo) mergeFromFragmentDeps(ctx android.ModuleContext, fragmen for _, fragment := range fragments { if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) { info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo) - i.TransitiveStubDexJarsByKind.append(info.TransitiveStubDexJarsByKind) + i.TransitiveStubDexJarsByScope.addStubDexJarsByModule(info.TransitiveStubDexJarsByScope) } } - - // Dedup and sort paths. - i.TransitiveStubDexJarsByKind.dedupAndSort() } var HiddenAPIInfoProvider = blueprint.NewProvider(HiddenAPIInfo{}) -// StubDexJarsByKind maps an android.SdkKind to the paths to stub dex jars appropriate for that -// level. See hiddenAPIRelevantSdkKinds for a list of the acceptable android.SdkKind values. -type StubDexJarsByKind map[android.SdkKind]android.Paths +// ModuleStubDexJars contains the stub dex jars provided by a single module. +// +// It maps a *HiddenAPIScope to the path to stub dex jars appropriate for that scope. See +// hiddenAPIScopes for a list of the acceptable *HiddenAPIScope values. +type ModuleStubDexJars map[*HiddenAPIScope]android.Path -// append appends the supplied kind specific stub dex jar pargs to the corresponding kind in this +// stubDexJarForWidestAPIScope returns the stub dex jars for the widest API scope provided by this // map. -func (s StubDexJarsByKind) append(other StubDexJarsByKind) { - for _, kind := range hiddenAPIRelevantSdkKinds { - s[kind] = append(s[kind], other[kind]...) +// +// The relative width of APIs is determined by their order in hiddenAPIScopes. +func (s ModuleStubDexJars) stubDexJarForWidestAPIScope() android.Path { + for i := len(hiddenAPIScopes) - 1; i >= 0; i-- { + apiScope := hiddenAPIScopes[i] + if stubsForAPIScope, ok := s[apiScope]; ok { + return stubsForAPIScope + } + } + + return nil +} + +// StubDexJarsByModule contains the stub dex jars provided by a set of modules. +// +// It maps a module name to the path to the stub dex jars provided by that module. +type StubDexJarsByModule map[string]ModuleStubDexJars + +// addStubDexJar adds a stub dex jar path provided by the specified module for the specified scope. +func (s StubDexJarsByModule) addStubDexJar(ctx android.ModuleContext, module android.Module, scope *HiddenAPIScope, stubDexJar android.Path) { + name := android.RemoveOptionalPrebuiltPrefix(module.Name()) + if name == scope.nonUpdatablePrebuiltModule || name == scope.nonUpdatableSourceModule { + // Treat all *android-non-updatable* modules as if they were part of an android-non-updatable + // java_sdk_library. + // TODO(b/192067200): Remove once android-non-updatable is a java_sdk_library or equivalent. + name = "android-non-updatable" + } else if name == "legacy.art.module.platform.api" { + // Treat legacy.art.module.platform.api as if it was an API scope provided by the + // art.module.public.api java_sdk_library which will be the case once the former has been + // migrated to a module_lib API. + name = "art.module.public.api" + } else if name == "legacy.i18n.module.platform.api" { + // Treat legacy.i18n.module.platform.api as if it was an API scope provided by the + // i18n.module.public.api java_sdk_library which will be the case once the former has been + // migrated to a module_lib API. + name = "i18n.module.public.api" + } else if name == "conscrypt.module.platform.api" { + // Treat conscrypt.module.platform.api as if it was an API scope provided by the + // conscrypt.module.public.api java_sdk_library which will be the case once the former has been + // migrated to a module_lib API. + name = "conscrypt.module.public.api" } + stubDexJarsByScope := s[name] + if stubDexJarsByScope == nil { + stubDexJarsByScope = ModuleStubDexJars{} + s[name] = stubDexJarsByScope + } + stubDexJarsByScope[scope] = stubDexJar } -// dedupAndSort removes duplicates in the stub dex jar paths and sorts them into a consistent and -// deterministic order. -func (s StubDexJarsByKind) dedupAndSort() { - for kind, paths := range s { - s[kind] = android.SortedUniquePaths(paths) +// addStubDexJarsByModule adds the stub dex jars in the supplied StubDexJarsByModule to this map. +func (s StubDexJarsByModule) addStubDexJarsByModule(other StubDexJarsByModule) { + for module, stubDexJarsByScope := range other { + s[module] = stubDexJarsByScope } } +// StubDexJarsForWidestAPIScope returns a list of stub dex jars containing the widest API scope +// provided by each module. +// +// The relative width of APIs is determined by their order in hiddenAPIScopes. +func (s StubDexJarsByModule) StubDexJarsForWidestAPIScope() android.Paths { + stubDexJars := android.Paths{} + modules := android.SortedStringKeys(s) + for _, module := range modules { + stubDexJarsByScope := s[module] + + stubDexJars = append(stubDexJars, stubDexJarsByScope.stubDexJarForWidestAPIScope()) + } + + return stubDexJars +} + +// StubDexJarsForScope returns a list of stub dex jars containing the stub dex jars provided by each +// module for the specified scope. +// +// If a module does not provide a stub dex jar for the supplied scope then it does not contribute to +// the returned list. +func (s StubDexJarsByModule) StubDexJarsForScope(scope *HiddenAPIScope) android.Paths { + stubDexJars := android.Paths{} + modules := android.SortedStringKeys(s) + for _, module := range modules { + stubDexJarsByScope := s[module] + // Not every module will have the same set of + if jars, ok := stubDexJarsByScope[scope]; ok { + stubDexJars = append(stubDexJars, jars) + } + } + + return stubDexJars +} + // HiddenAPIFlagInput encapsulates information obtained from a module and its dependencies that are // needed for hidden API flag generation. type HiddenAPIFlagInput struct { @@ -435,14 +664,21 @@ type HiddenAPIFlagInput struct { // from the stub dex files. FlagFilesByCategory FlagFilesByCategory - // StubDexJarsByKind contains the stub dex jars for different android.SdkKind and which determine + // StubDexJarsByScope contains the stub dex jars for different *HiddenAPIScope and which determine // the initial flags for each dex member. - StubDexJarsByKind StubDexJarsByKind + StubDexJarsByScope StubDexJarsByModule - // DependencyStubDexJarsByKind contains the stub dex jars provided by the fragments on which this - // depends. It is the result of merging HiddenAPIInfo.TransitiveStubDexJarsByKind from each + // DependencyStubDexJarsByScope contains the stub dex jars provided by the fragments on which this + // depends. It is the result of merging HiddenAPIInfo.TransitiveStubDexJarsByScope from each // fragment on which this depends. - DependencyStubDexJarsByKind StubDexJarsByKind + DependencyStubDexJarsByScope StubDexJarsByModule + + // AdditionalStubDexJarsByScope contains stub dex jars provided by other modules in addition to + // the ones that are obtained from fragments on which this depends. + // + // These are kept separate from stub dex jars in HiddenAPIFlagInput.DependencyStubDexJarsByScope + // as there are not propagated transitively to other fragments that depend on this. + AdditionalStubDexJarsByScope StubDexJarsByModule // RemovedTxtFiles is the list of removed.txt files provided by java_sdk_library modules that are // specified in the bootclasspath_fragment's stub_libs and contents properties. @@ -452,8 +688,10 @@ type HiddenAPIFlagInput struct { // newHiddenAPIFlagInput creates a new initialize HiddenAPIFlagInput struct. func newHiddenAPIFlagInput() HiddenAPIFlagInput { input := HiddenAPIFlagInput{ - FlagFilesByCategory: FlagFilesByCategory{}, - StubDexJarsByKind: StubDexJarsByKind{}, + FlagFilesByCategory: FlagFilesByCategory{}, + StubDexJarsByScope: StubDexJarsByModule{}, + DependencyStubDexJarsByScope: StubDexJarsByModule{}, + AdditionalStubDexJarsByScope: StubDexJarsByModule{}, } return input @@ -469,7 +707,7 @@ func (i *HiddenAPIFlagInput) canPerformHiddenAPIProcessing(ctx android.ModuleCon // 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(i.StubDexJarsByKind) == 0 { + if len(i.StubDexJarsByScope) == 0 { return false } @@ -500,14 +738,15 @@ func (i *HiddenAPIFlagInput) canPerformHiddenAPIProcessing(ctx android.ModuleCon // // That includes paths to the stub dex jars as well as paths to the *removed.txt files. func (i *HiddenAPIFlagInput) gatherStubLibInfo(ctx android.ModuleContext, contents []android.Module) { - addFromModule := func(ctx android.ModuleContext, module android.Module, kind android.SdkKind) { - dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, kind) + addFromModule := func(ctx android.ModuleContext, module android.Module, apiScope *HiddenAPIScope) { + sdkKind := apiScope.sdkKind + dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, sdkKind) if dexJar != nil { - i.StubDexJarsByKind[kind] = append(i.StubDexJarsByKind[kind], dexJar) + i.StubDexJarsByScope.addStubDexJar(ctx, module, apiScope, dexJar) } if sdkLibrary, ok := module.(SdkLibraryDependency); ok { - removedTxtFile := sdkLibrary.SdkRemovedTxtFile(ctx, kind) + removedTxtFile := sdkLibrary.SdkRemovedTxtFile(ctx, sdkKind) i.RemovedTxtFiles = append(i.RemovedTxtFiles, removedTxtFile.AsPaths()...) } } @@ -515,11 +754,9 @@ func (i *HiddenAPIFlagInput) gatherStubLibInfo(ctx android.ModuleContext, conten // If the contents includes any java_sdk_library modules then add them to the stubs. for _, module := range contents { if _, ok := module.(SdkLibraryDependency); ok { - // Add information for every possible kind needed by hidden API. SdkCorePlatform is not used - // as the java_sdk_library does not have special support for core_platform API, instead it is - // implemented as a customized form of SdkPublic. - for _, kind := range []android.SdkKind{android.SdkPublic, android.SdkSystem, android.SdkTest} { - addFromModule(ctx, module, kind) + // Add information for every possible API scope needed by hidden API. + for _, apiScope := range hiddenAPISdkLibrarySupportedScopes { + addFromModule(ctx, module, apiScope) } } } @@ -527,13 +764,19 @@ func (i *HiddenAPIFlagInput) gatherStubLibInfo(ctx android.ModuleContext, conten ctx.VisitDirectDeps(func(module android.Module) { tag := ctx.OtherModuleDependencyTag(module) if hiddenAPIStubsTag, ok := tag.(hiddenAPIStubsDependencyTag); ok { - kind := hiddenAPIStubsTag.sdkKind - addFromModule(ctx, module, kind) + apiScope := hiddenAPIStubsTag.apiScope + if hiddenAPIStubsTag.fromAdditionalDependency { + dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, apiScope.sdkKind) + if dexJar != nil { + i.AdditionalStubDexJarsByScope.addStubDexJar(ctx, module, apiScope, dexJar) + } + } else { + addFromModule(ctx, module, apiScope) + } } }) // Normalize the paths, i.e. remove duplicates and sort. - i.StubDexJarsByKind.dedupAndSort() i.RemovedTxtFiles = android.SortedUniquePaths(i.RemovedTxtFiles) } @@ -546,9 +789,9 @@ func (i *HiddenAPIFlagInput) extractFlagFilesFromProperties(ctx android.ModuleCo } } -func (i *HiddenAPIFlagInput) transitiveStubDexJarsByKind() StubDexJarsByKind { - transitive := i.DependencyStubDexJarsByKind - transitive.append(i.StubDexJarsByKind) +func (i *HiddenAPIFlagInput) transitiveStubDexJarsByScope() StubDexJarsByModule { + transitive := i.DependencyStubDexJarsByScope + transitive.addStubDexJarsByModule(i.StubDexJarsByScope) return transitive } @@ -637,28 +880,6 @@ func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc st outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlagPaths android.Paths, flagFilesByCategory FlagFilesByCategory, allFlagsPaths android.Paths, generatedRemovedDexSignatures android.OptionalPath) { - // The file which is used to record that the flags file is valid. - var validFile android.WritablePath - - // If there are flag files that have been generated by fragments on which this depends then use - // them to validate the flag file generated by the rules created by this method. - if len(allFlagsPaths) > 0 { - // The flags file generated by the rule created by this method needs to be validated to ensure - // that it is consistent with the flag files generated by the individual fragments. - - validFile = pathForValidation(ctx, outputPath) - - // Create a rule to validate the output from the following rule. - rule := android.NewRuleBuilder(pctx, ctx) - rule.Command(). - BuiltTool("verify_overlaps"). - Input(outputPath). - Inputs(allFlagsPaths). - // If validation passes then update the file that records that. - Text("&& touch").Output(validFile) - rule.Build(name+"Validation", desc+" validation") - } - // Create the rule that will generate the flag files. tempPath := tempPathForRestat(ctx, outputPath) rule := android.NewRuleBuilder(pctx, ctx) @@ -684,7 +905,11 @@ func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc st commitChangeForRestat(rule, tempPath, outputPath) - if validFile != nil { + // If there are flag files that have been generated by fragments on which this depends then use + // them to validate the flag file generated by the rules created by this method. + if len(allFlagsPaths) > 0 { + validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, allFlagsPaths) + // Add the file that indicates that the file generated by this is valid. // // This will cause the validation rule above to be run any time that the output of this rule @@ -695,6 +920,25 @@ func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc st rule.Build(name, desc) } +// buildRuleValidateOverlappingCsvFiles checks that the modular CSV files, i.e. the files generated +// by the individual bootclasspath_fragment modules are subsets of the monolithic CSV file. +func buildRuleValidateOverlappingCsvFiles(ctx android.BuilderContext, name string, desc string, monolithicFilePath android.WritablePath, modularFilePaths android.Paths) android.WritablePath { + // The file which is used to record that the flags file is valid. + validFile := pathForValidation(ctx, monolithicFilePath) + + // Create a rule to validate the output from the following rule. + rule := android.NewRuleBuilder(pctx, ctx) + rule.Command(). + BuiltTool("verify_overlaps"). + Input(monolithicFilePath). + Inputs(modularFilePaths). + // If validation passes then update the file that records that. + Text("&& touch").Output(validFile) + rule.Build(name+"Validation", desc+" validation") + + return validFile +} + // hiddenAPIRulesForBootclasspathFragment will generate all the flags for a fragment of the // bootclasspath and then encode the flags into the boot dex files. // @@ -718,8 +962,7 @@ func hiddenAPIRulesForBootclasspathFragment(ctx android.ModuleContext, contents // Generate the stub-flags.csv. stubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "stub-flags.csv") - rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlagsCSV, bootDexInfoByModule.bootDexJars(), input) - rule.Build("modularHiddenAPIStubFlagsFile", "modular hiddenapi stub flags") + buildRuleToGenerateHiddenAPIStubFlagsFile(ctx, "modularHiddenAPIStubFlagsFile", "modular hiddenapi stub flags", stubFlagsCSV, bootDexInfoByModule.bootDexJars(), input, nil) // Extract the classes jars from the contents. classesJars := extractClassesJarsFromModules(contents) diff --git a/java/jacoco.go b/java/jacoco.go index 9162161d3..e11c2ce69 100644 --- a/java/jacoco.go +++ b/java/jacoco.go @@ -94,7 +94,7 @@ func jacocoFiltersToZipCommand(includes, excludes []string) string { if len(includes) > 0 { specs += strings.Join(includes, " ") } else { - specs += "**/*.class" + specs += "'**/*.class'" } return specs } diff --git a/java/jacoco_test.go b/java/jacoco_test.go index 91f05535a..1882908ca 100644 --- a/java/jacoco_test.go +++ b/java/jacoco_test.go @@ -74,7 +74,7 @@ func TestJacocoFiltersToZipCommand(t *testing.T) { { name: "implicit wildcard", includes: []string{}, - out: "**/*.class", + out: "'**/*.class'", }, { name: "only include", diff --git a/java/lint.go b/java/lint.go index 1511cfe26..dd5e4fb13 100644 --- a/java/lint.go +++ b/java/lint.go @@ -78,6 +78,7 @@ type linter struct { minSdkVersion string targetSdkVersion string compileSdkVersion string + compileSdkKind android.SdkKind javaLanguageLevel string kotlinLanguageLevel string outputs lintOutputs @@ -389,13 +390,25 @@ func (l *linter) lint(ctx android.ModuleContext) { rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String()) rule.Command().Text("rm -f").Output(html).Output(text).Output(xml) + var apiVersionsName, apiVersionsPrebuilt string + if l.compileSdkKind == android.SdkModule { + // When compiling an SDK module we use the filtered database because otherwise lint's + // NewApi check produces too many false positives; This database excludes information + // about classes created in mainline modules hence removing those false positives. + apiVersionsName = "api_versions_public_filtered.xml" + apiVersionsPrebuilt = "prebuilts/sdk/current/public/data/api-versions-filtered.xml" + } else { + apiVersionsName = "api_versions.xml" + apiVersionsPrebuilt = "prebuilts/sdk/current/public/data/api-versions.xml" + } + var annotationsZipPath, apiVersionsXMLPath android.Path if ctx.Config().AlwaysUsePrebuiltSdks() { annotationsZipPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/annotations.zip") - apiVersionsXMLPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/api-versions.xml") + apiVersionsXMLPath = android.PathForSource(ctx, apiVersionsPrebuilt) } else { annotationsZipPath = copiedAnnotationsZipPath(ctx) - apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx) + apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx, apiVersionsName) } cmd := rule.Command() @@ -487,23 +500,27 @@ func (l *lintSingleton) GenerateBuildActions(ctx android.SingletonContext) { l.copyLintDependencies(ctx) } -func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) { - if ctx.Config().AlwaysUsePrebuiltSdks() { - return - } - - var frameworkDocStubs android.Module +func findModuleOrErr(ctx android.SingletonContext, moduleName string) android.Module { + var res android.Module ctx.VisitAllModules(func(m android.Module) { - if ctx.ModuleName(m) == "framework-doc-stubs" { - if frameworkDocStubs == nil { - frameworkDocStubs = m + if ctx.ModuleName(m) == moduleName { + if res == nil { + res = m } else { - ctx.Errorf("lint: multiple framework-doc-stubs modules found: %s and %s", - ctx.ModuleSubDir(m), ctx.ModuleSubDir(frameworkDocStubs)) + ctx.Errorf("lint: multiple %s modules found: %s and %s", moduleName, + ctx.ModuleSubDir(m), ctx.ModuleSubDir(res)) } } }) + return res +} +func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) { + if ctx.Config().AlwaysUsePrebuiltSdks() { + return + } + + frameworkDocStubs := findModuleOrErr(ctx, "framework-doc-stubs") if frameworkDocStubs == nil { if !ctx.Config().AllowMissingDependencies() { ctx.Errorf("lint: missing framework-doc-stubs") @@ -511,6 +528,14 @@ func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) { return } + filteredDb := findModuleOrErr(ctx, "api-versions-xml-public-filtered") + if filteredDb == nil { + if !ctx.Config().AllowMissingDependencies() { + ctx.Errorf("lint: missing api-versions-xml-public-filtered") + } + return + } + ctx.Build(pctx, android.BuildParams{ Rule: android.CpIfChanged, Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".annotations.zip"), @@ -520,7 +545,13 @@ func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) { ctx.Build(pctx, android.BuildParams{ Rule: android.CpIfChanged, Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".api_versions.xml"), - Output: copiedAPIVersionsXmlPath(ctx), + Output: copiedAPIVersionsXmlPath(ctx, "api_versions.xml"), + }) + + ctx.Build(pctx, android.BuildParams{ + Rule: android.CpIfChanged, + Input: android.OutputFileForModule(ctx, filteredDb, ""), + Output: copiedAPIVersionsXmlPath(ctx, "api_versions_public_filtered.xml"), }) } @@ -528,8 +559,8 @@ func copiedAnnotationsZipPath(ctx android.PathContext) android.WritablePath { return android.PathForOutput(ctx, "lint", "annotations.zip") } -func copiedAPIVersionsXmlPath(ctx android.PathContext) android.WritablePath { - return android.PathForOutput(ctx, "lint", "api_versions.xml") +func copiedAPIVersionsXmlPath(ctx android.PathContext, name string) android.WritablePath { + return android.PathForOutput(ctx, "lint", name) } func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) { diff --git a/java/lint_defaults.txt b/java/lint_defaults.txt index 2de05b0a0..75de7dc6c 100644 --- a/java/lint_defaults.txt +++ b/java/lint_defaults.txt @@ -78,3 +78,7 @@ --disable_check HardcodedDebugMode --warning_check QueryAllPackagesPermission + +--warning_check CoarseFineLocation +--warning_check IntentFilterExportedReceiver +--warning_check RemoteViewLayout diff --git a/java/lint_test.go b/java/lint_test.go index 6d64de7e9..9cf1c33fe 100644 --- a/java/lint_test.go +++ b/java/lint_test.go @@ -219,3 +219,72 @@ func TestJavaLintStrictUpdatabilityLinting(t *testing.T) { t.Error("did not restrict baselining NewApi") } } + +func TestJavaLintDatabaseSelectionFull(t *testing.T) { + testCases := []string{ + "current", "core_platform", "system_current", "S", "30", "10000", + } + bp := ` + java_library { + name: "foo", + srcs: [ + "a.java", + ], + min_sdk_version: "29", + sdk_version: "XXX", + lint: { + strict_updatability_linting: true, + }, + } +` + for _, testCase := range testCases { + thisBp := strings.Replace(bp, "XXX", testCase, 1) + + result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules, FixtureWithPrebuiltApis(map[string][]string{ + "30": {"foo"}, + "10000": {"foo"}, + })). + RunTestWithBp(t, thisBp) + + foo := result.ModuleForTests("foo", "android_common") + sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto")) + if strings.Contains(*sboxProto.Commands[0].Command, + "/api_versions_public_filtered.xml") { + t.Error("used public-filtered lint api database for case", testCase) + } + if !strings.Contains(*sboxProto.Commands[0].Command, + "/api_versions.xml") { + t.Error("did not use full api database for case", testCase) + } + } + +} + +func TestJavaLintDatabaseSelectionPublicFiltered(t *testing.T) { + bp := ` + java_library { + name: "foo", + srcs: [ + "a.java", + ], + min_sdk_version: "29", + sdk_version: "module_current", + lint: { + strict_updatability_linting: true, + }, + } +` + result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules). + RunTestWithBp(t, bp) + + foo := result.ModuleForTests("foo", "android_common") + sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto")) + if !strings.Contains(*sboxProto.Commands[0].Command, + "/api_versions_public_filtered.xml") { + t.Error("did not use public-filtered lint api database", *sboxProto.Commands[0].Command) + } + if strings.Contains(*sboxProto.Commands[0].Command, + "/api_versions.xml") { + t.Error("used full api database") + } +} diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index a444de00b..d72cee0c3 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -119,8 +119,8 @@ func (b *platformBootclasspathModule) hiddenAPIDepsMutator(ctx android.BottomUpM } // Add dependencies onto the stub lib modules. - sdkKindToStubLibModules := hiddenAPIComputeMonolithicStubLibModules(ctx.Config()) - hiddenAPIAddStubLibDependencies(ctx, sdkKindToStubLibModules) + apiLevelToStubLibModules := hiddenAPIComputeMonolithicStubLibModules(ctx.Config()) + hiddenAPIAddStubLibDependencies(ctx, apiLevelToStubLibModules) } func (b *platformBootclasspathModule) BootclasspathDepsMutator(ctx android.BottomUpMutatorContext) { @@ -303,7 +303,7 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android. // the fragments will have already provided the flags that are needed. classesJars := monolithicInfo.ClassesJars - // Create the input to pass to ruleToGenerateHiddenAPIStubFlagsFile + // Create the input to pass to buildRuleToGenerateHiddenAPIStubFlagsFile input := newHiddenAPIFlagInput() // Gather stub library information from the dependencies on modules provided by @@ -315,8 +315,7 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android. // Generate the monolithic stub-flags.csv file. stubFlags := hiddenAPISingletonPaths(ctx).stubFlags - rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlags, bootDexJarByModule.bootDexJars(), input) - rule.Build("platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags") + buildRuleToGenerateHiddenAPIStubFlagsFile(ctx, "platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags", stubFlags, bootDexJarByModule.bootDexJars(), input, monolithicInfo.StubFlagsPaths) // Generate the annotation-flags.csv file from all the module annotations. annotationFlags := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "annotation-flags-from-classes.csv") diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go index 65af95314..8e0618e31 100644 --- a/java/sdk_library_test.go +++ b/java/sdk_library_test.go @@ -122,7 +122,7 @@ func TestJavaSdkLibrary(t *testing.T) { result.ModuleForTests("foo.api.system.28", "") result.ModuleForTests("foo.api.test.28", "") - exportedComponentsInfo := result.ModuleProvider(foo.Module(), ExportedComponentsInfoProvider).(ExportedComponentsInfo) + exportedComponentsInfo := result.ModuleProvider(foo.Module(), android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo) expectedFooExportedComponents := []string{ "foo.stubs", "foo.stubs.source", diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go index 394fcc52d..31ec3f12c 100644 --- a/rust/config/allowed_list.go +++ b/rust/config/allowed_list.go @@ -26,9 +26,18 @@ var ( "system/logging/rust", "system/security", "system/tools/aidl", + "tools/security/fuzzing/example_rust_fuzzer", + } + + DownstreamRustAllowedPaths = []string{ + // Add downstream allowed Rust paths here. } RustModuleTypes = []string{ + // Don't add rust_bindgen or rust_protobuf as these are code generation modules + // and can be expected to be in paths without Rust code. + "rust_benchmark", + "rust_benchmark_host", "rust_binary", "rust_binary_host", "rust_library", @@ -37,6 +46,7 @@ var ( "rust_ffi", "rust_ffi_shared", "rust_ffi_static", + "rust_fuzz", "rust_library_host", "rust_library_host_dylib", "rust_library_host_rlib", diff --git a/rust/rust.go b/rust/rust.go index b8c8be5a2..05f639939 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -35,7 +35,7 @@ func init() { android.AddNeverAllowRules( android.NeverAllow(). - NotIn(config.RustAllowedPaths...). + NotIn(append(config.RustAllowedPaths, config.DownstreamRustAllowedPaths...)...). ModuleType(config.RustModuleTypes...)) android.RegisterModuleType("rust_defaults", defaultsFactory) @@ -288,6 +288,10 @@ func (mod *Module) UseVndk() bool { return mod.Properties.VndkVersion != "" } +func (mod *Module) Bootstrap() bool { + return false +} + func (mod *Module) MustUseVendorVariant() bool { return true } @@ -952,7 +956,7 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { directRlibDeps := []*Module{} directDylibDeps := []*Module{} directProcMacroDeps := []*Module{} - directSharedLibDeps := [](cc.LinkableInterface){} + directSharedLibDeps := []cc.SharedLibraryInfo{} directStaticLibDeps := [](cc.LinkableInterface){} directSrcProvidersDeps := []*Module{} directSrcDeps := [](android.SourceFileProducer){} @@ -1073,14 +1077,23 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { directStaticLibDeps = append(directStaticLibDeps, ccDep) mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, makeLibName) case cc.IsSharedDepTag(depTag): + // For the shared lib dependencies, we may link to the stub variant + // of the dependency depending on the context (e.g. if this + // dependency crosses the APEX boundaries). + sharedLibraryInfo, exportedInfo := cc.ChooseStubOrImpl(ctx, dep) + + // Re-get linkObject as ChooseStubOrImpl actually tells us which + // object (either from stub or non-stub) to use. + linkObject = android.OptionalPathForPath(sharedLibraryInfo.SharedLibrary) + linkPath = linkPathFromFilePath(linkObject.Path()) + depPaths.linkDirs = append(depPaths.linkDirs, linkPath) depPaths.linkObjects = append(depPaths.linkObjects, linkObject.String()) - exportedInfo := ctx.OtherModuleProvider(dep, cc.FlagExporterInfoProvider).(cc.FlagExporterInfo) depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...) depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...) depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...) depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...) - directSharedLibDeps = append(directSharedLibDeps, ccDep) + directSharedLibDeps = append(directSharedLibDeps, sharedLibraryInfo) // Record baseLibName for snapshots. mod.Properties.SnapshotSharedLibs = append(mod.Properties.SnapshotSharedLibs, cc.BaseLibName(depName)) @@ -1135,11 +1148,11 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { var sharedLibFiles android.Paths var sharedLibDepFiles android.Paths for _, dep := range directSharedLibDeps { - sharedLibFiles = append(sharedLibFiles, dep.OutputFile().Path()) - if dep.Toc().Valid() { - sharedLibDepFiles = append(sharedLibDepFiles, dep.Toc().Path()) + sharedLibFiles = append(sharedLibFiles, dep.SharedLibrary) + if dep.TableOfContents.Valid() { + sharedLibDepFiles = append(sharedLibDepFiles, dep.TableOfContents.Path()) } else { - sharedLibDepFiles = append(sharedLibDepFiles, dep.OutputFile().Path()) + sharedLibDepFiles = append(sharedLibDepFiles, dep.SharedLibrary) } } diff --git a/scripts/Android.bp b/scripts/Android.bp index 1c02bd0b8..635be10b3 100644 --- a/scripts/Android.bp +++ b/scripts/Android.bp @@ -265,3 +265,19 @@ python_binary_host { "linker_config_proto", ], } + +python_binary_host { + name: "get_clang_version", + main: "get_clang_version.py", + srcs: [ + "get_clang_version.py", + ], + version: { + py2: { + enabled: false, + }, + py3: { + enabled: true, + }, + }, +} diff --git a/scripts/OWNERS b/scripts/OWNERS index 2b9c2ded9..1830a1823 100644 --- a/scripts/OWNERS +++ b/scripts/OWNERS @@ -1,6 +1,6 @@ per-file system-clang-format,system-clang-format-2 = enh@google.com,smoreland@google.com per-file build-mainline-modules.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com per-file build-aml-prebuilts.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com -per-file construct_context.py = ngeoffray@google.com,calin@google.com,mathieuc@google.com,skvadrik@google.com +per-file construct_context.py = ngeoffray@google.com,calin@google.com,skvadrik@google.com per-file conv_linker_config.py = kiyoungkim@google.com, jiyong@google.com, jooyung@google.com per-file gen_ndk*.sh = sophiez@google.com, allenhair@google.com diff --git a/scripts/get_clang_version.py b/scripts/get_clang_version.py new file mode 100755 index 000000000..622fca15c --- /dev/null +++ b/scripts/get_clang_version.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# +# 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. +"""A tool to report the current clang version used during build""" + +import os +import re +import sys + + +ANDROID_BUILD_TOP = os.environ.get("ANDROID_BUILD_TOP", ".") + +def get_clang_prebuilts_version(global_go): + # TODO(b/187231324): Get clang version from the json file once it is no longer + # hard-coded in global.go + if global_go is None: + global_go = ANDROID_BUILD_TOP + '/build/soong/cc/config/global.go' + with open(global_go) as infile: + contents = infile.read() + + regex_rev = r'\tClangDefaultVersion\s+= "clang-(?P<rev>r\d+[a-z]?\d?)"' + match_rev = re.search(regex_rev, contents) + if match_rev is None: + raise RuntimeError('Parsing clang info failed') + return match_rev.group('rev') + + +def main(): + global_go = sys.argv[1] if len(sys.argv) > 1 else None + print(get_clang_prebuilts_version(global_go)); + + +if __name__ == '__main__': + main() diff --git a/scripts/get_clang_version_test.py b/scripts/get_clang_version_test.py new file mode 100644 index 000000000..e57df6c18 --- /dev/null +++ b/scripts/get_clang_version_test.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# +# 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. +# +"""Unit tests for get_clang_version.py.""" + +import unittest + +import get_clang_version + +class GetClangVersionTest(unittest.TestCase): + """Unit tests for get_clang_version.""" + + def test_get_clang_version(self): + """Test parsing of clang prebuilts version.""" + self.assertIsNotNone(get_clang_version.get_clang_prebuilts_version()) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/scripts/hiddenapi/verify_overlaps.py b/scripts/hiddenapi/verify_overlaps.py index c8e387931..bb0917e53 100755 --- a/scripts/hiddenapi/verify_overlaps.py +++ b/scripts/hiddenapi/verify_overlaps.py @@ -47,9 +47,9 @@ for subsetPath in args.subsets: if signature in allFlagsBySignature: allFlags = allFlagsBySignature.get(signature) if allFlags != row: - mismatchingSignatures.append((signature, row[None], allFlags[None])) + mismatchingSignatures.append((signature, row.get(None, []), allFlags.get(None, []))) else: - mismatchingSignatures.append((signature, row[None], [])) + mismatchingSignatures.append((signature, row.get(None, []), [])) if mismatchingSignatures: @@ -60,7 +60,7 @@ for subsetPath in args.subsets: for mismatch in mismatchingSignatures: print() print("< " + mismatch[0] + "," + ",".join(mismatch[1])) - if mismatch[2] != None: + if mismatch[2] != []: print("> " + mismatch[0] + "," + ",".join(mismatch[2])) else: print("> " + mismatch[0] + " - missing") diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go index 31555c02e..98697dcf4 100644 --- a/sdk/cc_sdk_test.go +++ b/sdk/cc_sdk_test.go @@ -2189,7 +2189,7 @@ func TestSystemSharedLibPropagation(t *testing.T) { result := testSdkWithCc(t, ` sdk { name: "mysdk", - native_shared_libs: ["sslnil", "sslempty", "sslnonempty"], + native_shared_libs: ["sslnil", "sslempty", "sslnonempty", "dslnil", "dslempty", "dslnonempty"], } cc_library { @@ -2206,6 +2206,21 @@ func TestSystemSharedLibPropagation(t *testing.T) { name: "sslnonempty", system_shared_libs: ["sslnil"], } + + cc_library { + name: "dslnil", + host_supported: true, + } + + cc_library { + name: "dslempty", + default_shared_libs: [], + } + + cc_library { + name: "dslnonempty", + default_shared_libs: ["sslnil"], + } `) CheckSnapshot(t, result, "mysdk", "", @@ -2261,13 +2276,62 @@ cc_prebuilt_library_shared { }, }, } -`)) + +cc_prebuilt_library_shared { + name: "dslnil", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + compile_multilib: "both", + arch: { + arm64: { + srcs: ["arm64/lib/dslnil.so"], + }, + arm: { + srcs: ["arm/lib/dslnil.so"], + }, + }, +} + +cc_prebuilt_library_shared { + name: "dslempty", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + compile_multilib: "both", + default_shared_libs: [], + arch: { + arm64: { + srcs: ["arm64/lib/dslempty.so"], + }, + arm: { + srcs: ["arm/lib/dslempty.so"], + }, + }, +} + +cc_prebuilt_library_shared { + name: "dslnonempty", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + compile_multilib: "both", + default_shared_libs: ["sslnil"], + arch: { + arm64: { + srcs: ["arm64/lib/dslnonempty.so"], + }, + arm: { + srcs: ["arm/lib/dslnonempty.so"], + }, + }, +}`)) result = testSdkWithCc(t, ` sdk { name: "mysdk", host_supported: true, - native_shared_libs: ["sslvariants"], + native_shared_libs: ["sslvariants", "dslvariants"], } cc_library { @@ -2279,6 +2343,16 @@ cc_prebuilt_library_shared { }, }, } + + cc_library { + name: "dslvariants", + host_supported: true, + target: { + android: { + default_shared_libs: [], + }, + }, + } `) CheckSnapshot(t, result, "mysdk", "", @@ -2315,6 +2389,37 @@ cc_prebuilt_library_shared { }, }, } + +cc_prebuilt_library_shared { + name: "dslvariants", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + host_supported: true, + compile_multilib: "both", + target: { + host: { + enabled: false, + }, + android: { + default_shared_libs: [], + }, + android_arm64: { + srcs: ["android/arm64/lib/dslvariants.so"], + }, + android_arm: { + srcs: ["android/arm/lib/dslvariants.so"], + }, + linux_glibc_x86_64: { + enabled: true, + srcs: ["linux_glibc/x86_64/lib/dslvariants.so"], + }, + linux_glibc_x86: { + enabled: true, + srcs: ["linux_glibc/x86/lib/dslvariants.so"], + }, + }, +} `), checkVersionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. @@ -2351,11 +2456,46 @@ cc_prebuilt_library_shared { }, } +cc_prebuilt_library_shared { + name: "mysdk_dslvariants@current", + sdk_member_name: "dslvariants", + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + host_supported: true, + installable: false, + compile_multilib: "both", + target: { + host: { + enabled: false, + }, + android: { + default_shared_libs: [], + }, + android_arm64: { + srcs: ["android/arm64/lib/dslvariants.so"], + }, + android_arm: { + srcs: ["android/arm/lib/dslvariants.so"], + }, + linux_glibc_x86_64: { + enabled: true, + srcs: ["linux_glibc/x86_64/lib/dslvariants.so"], + }, + linux_glibc_x86: { + enabled: true, + srcs: ["linux_glibc/x86/lib/dslvariants.so"], + }, + }, +} + sdk_snapshot { name: "mysdk@current", visibility: ["//visibility:public"], host_supported: true, - native_shared_libs: ["mysdk_sslvariants@current"], + native_shared_libs: [ + "mysdk_sslvariants@current", + "mysdk_dslvariants@current", + ], target: { host: { enabled: false, diff --git a/sdk/update.go b/sdk/update.go index 3f613399a..b146b62c8 100644 --- a/sdk/update.go +++ b/sdk/update.go @@ -156,6 +156,11 @@ func (s *sdk) collectMembers(ctx android.ModuleContext) { if memberTag, ok := tag.(android.SdkMemberTypeDependencyTag); ok { memberType := memberTag.SdkMemberType(child) + // If a nil SdkMemberType was returned then this module should not be added to the sdk. + if memberType == nil { + return false + } + // Make sure that the resolved module is allowed in the member list property. if !memberType.IsInstance(child) { ctx.ModuleErrorf("module %q is not valid in property %s", ctx.OtherModuleName(child), memberType.SdkPropertyName()) diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh index 8c8dc8208..125868490 100755 --- a/tests/bootstrap_test.sh +++ b/tests/bootstrap_test.sh @@ -486,6 +486,39 @@ function test_null_build_after_docs { fi } +function test_write_to_source_tree { + setup + mkdir -p a + cat > a/Android.bp <<EOF +genrule { + name: "write_to_source_tree", + out: ["write_to_source_tree"], + cmd: "touch file_in_source_tree && touch \$(out)", +} +EOF + readonly EXPECTED_OUT=out/soong/.intermediates/a/write_to_source_tree/gen/write_to_source_tree + readonly ERROR_LOG=${MOCK_TOP}/out/error.log + readonly ERROR_MSG="Read-only file system" + readonly ERROR_HINT_PATTERN="BUILD_BROKEN_SRC_DIR" + # Test in ReadOnly source tree + run_ninja BUILD_BROKEN_SRC_DIR_IS_WRITABLE=false ${EXPECTED_OUT} &> /dev/null && \ + fail "Write to source tree should not work in a ReadOnly source tree" + + if grep -q "${ERROR_MSG}" ${ERROR_LOG} && grep -q "${ERROR_HINT_PATTERN}" ${ERROR_LOG} ; then + echo Error message and error hint found in logs >/dev/null + else + fail "Did not find Read-only error AND error hint in error.log" + fi + + # Test in ReadWrite source tree + run_ninja BUILD_BROKEN_SRC_DIR_IS_WRITABLE=true ${EXPECTED_OUT} &> /dev/null || \ + fail "Write to source tree did not succeed in a ReadWrite source tree" + + if grep -q "${ERROR_MSG}\|${ERROR_HINT_PATTERN}" ${ERROR_LOG} ; then + fail "Found read-only error OR error hint in error.log" + fi +} + function test_bp2build_smoke { setup GENERATE_BAZEL_FILES=1 run_soong @@ -692,6 +725,7 @@ test_add_file_to_soong_build test_glob_during_bootstrapping test_soong_build_rerun_iff_environment_changes test_dump_json_module_graph +test_write_to_source_tree test_bp2build_smoke test_bp2build_generates_fake_ninja_file test_bp2build_null_build diff --git a/tests/lib.sh b/tests/lib.sh index 35ccea973..f1e1efa09 100644 --- a/tests/lib.sh +++ b/tests/lib.sh @@ -126,6 +126,10 @@ run_bp2build() { GENERATE_BAZEL_FILES=true build/soong/soong_ui.bash --make-mode --skip-ninja --skip-make --skip-soong-tests nothing } +run_ninja() { + build/soong/soong_ui.bash --make-mode --skip-make --skip-soong-tests "$@" +} + info "Starting Soong integration test suite $(basename $0)" info "Mock top: $MOCK_TOP" diff --git a/ui/build/build.go b/ui/build/build.go index 1187aa2cf..1ed901401 100644 --- a/ui/build/build.go +++ b/ui/build/build.go @@ -28,16 +28,20 @@ import ( func SetupOutDir(ctx Context, config Config) { ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "Android.mk")) ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "CleanSpec.mk")) - if !config.SkipKati() { - // Run soong_build with Kati for a hybrid build, e.g. running the - // AndroidMk singleton and postinstall commands. Communicate this to - // soong_build by writing an empty .soong.kati_enabled marker file in the - // soong_build output directory for the soong_build primary builder to - // know if the user wants to run Kati after. - // - // This does not preclude running Kati for *product configuration purposes*. - ensureEmptyFileExists(ctx, filepath.Join(config.SoongOutDir(), ".soong.kati_enabled")) + + // Potentially write a marker file for whether kati is enabled. This is used by soong_build to + // potentially run the AndroidMk singleton and postinstall commands. + // Note that the absence of the file does not not preclude running Kati for product + // configuration purposes. + katiEnabledMarker := filepath.Join(config.SoongOutDir(), ".soong.kati_enabled") + if config.SkipKatiNinja() { + os.Remove(katiEnabledMarker) + // Note that we can not remove the file for SkipKati builds yet -- some continuous builds + // --skip-make builds rely on kati targets being defined. + } else if !config.SkipKati() { + ensureEmptyFileExists(ctx, katiEnabledMarker) } + // The ninja_build file is used by our buildbots to understand that the output // can be parsed as ninja output. ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "ninja_build")) diff --git a/ui/build/config.go b/ui/build/config.go index 480672171..737062732 100644 --- a/ui/build/config.go +++ b/ui/build/config.go @@ -161,6 +161,10 @@ func NewConfig(ctx Context, args ...string) Config { ret.distDir = filepath.Join(ret.OutDir(), "dist") } + if srcDirIsWritable, ok := ret.environ.Get("BUILD_BROKEN_SRC_DIR_IS_WRITABLE"); ok { + ret.sandboxConfig.SetSrcDirIsRO(srcDirIsWritable == "false") + } + ret.environ.Unset( // We're already using it "USE_SOONG_UI", diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go index b0a674864..5b2046e54 100644 --- a/ui/build/sandbox_linux.go +++ b/ui/build/sandbox_linux.go @@ -97,8 +97,11 @@ func (c *Cmd) sandboxSupported() bool { "-u", "nobody", "-g", sandboxConfig.group, "-R", "/", - "-B", sandboxConfig.srcDir, + // Mount tmp before srcDir + // srcDir is /tmp/.* in integration tests, which is a child dir of /tmp + // nsjail throws an error if a child dir is mounted before its parent "-B", "/tmp", + "-B", sandboxConfig.srcDir, "-B", sandboxConfig.outDir, } diff --git a/ui/status/ninja.go b/ui/status/ninja.go index 765679fb2..2445972fb 100644 --- a/ui/status/ninja.go +++ b/ui/status/ninja.go @@ -19,6 +19,8 @@ import ( "fmt" "io" "os" + "regexp" + "strings" "syscall" "time" @@ -158,9 +160,10 @@ func (n *NinjaReader) run() { err = fmt.Errorf("exited with code: %d", exitCode) } + outputWithErrorHint := errorHintGenerator.GetOutputWithErrorHint(msg.EdgeFinished.GetOutput(), exitCode) n.status.FinishAction(ActionResult{ Action: started, - Output: msg.EdgeFinished.GetOutput(), + Output: outputWithErrorHint, Error: err, Stats: ActionResultStats{ UserTime: msg.EdgeFinished.GetUserTime(), @@ -219,3 +222,53 @@ func readVarInt(r *bufio.Reader) (int, error) { return ret, nil } + +// key is pattern in stdout/stderr +// value is error hint +var allErrorHints = map[string]string{ + "Read-only file system": `\nWrite to a read-only file system detected. Possible fixes include +1. Generate file directly to out/ which is ReadWrite, #recommend solution +2. BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST := <my/path/1> <my/path/2> #discouraged, subset of source tree will be RW +3. BUILD_BROKEN_SRC_DIR_IS_WRITABLE := true #highly discouraged, entire source tree will be RW +`, +} +var errorHintGenerator = *newErrorHintGenerator(allErrorHints) + +type ErrorHintGenerator struct { + allErrorHints map[string]string + allErrorHintPatternsCompiled *regexp.Regexp +} + +func newErrorHintGenerator(allErrorHints map[string]string) *ErrorHintGenerator { + var allErrorHintPatterns []string + for errorHintPattern, _ := range allErrorHints { + allErrorHintPatterns = append(allErrorHintPatterns, errorHintPattern) + } + allErrorHintPatternsRegex := strings.Join(allErrorHintPatterns[:], "|") + re := regexp.MustCompile(allErrorHintPatternsRegex) + return &ErrorHintGenerator{ + allErrorHints: allErrorHints, + allErrorHintPatternsCompiled: re, + } +} + +func (errorHintGenerator *ErrorHintGenerator) GetOutputWithErrorHint(rawOutput string, buildExitCode int) string { + if buildExitCode == 0 { + return rawOutput + } + errorHint := errorHintGenerator.getErrorHint(rawOutput) + if errorHint == nil { + return rawOutput + } + return rawOutput + *errorHint +} + +// Returns the error hint corresponding to the FIRST match in raw output +func (errorHintGenerator *ErrorHintGenerator) getErrorHint(rawOutput string) *string { + firstMatch := errorHintGenerator.allErrorHintPatternsCompiled.FindString(rawOutput) + if _, found := errorHintGenerator.allErrorHints[firstMatch]; found { + errorHint := errorHintGenerator.allErrorHints[firstMatch] + return &errorHint + } + return nil +} diff --git a/ui/status/ninja_test.go b/ui/status/ninja_test.go index c400c97ee..f3638b323 100644 --- a/ui/status/ninja_test.go +++ b/ui/status/ninja_test.go @@ -43,3 +43,53 @@ func TestNinjaReader_Close(t *testing.T) { t.Errorf("nr.Close timed out, %s > %s", g, w) } } + +// Test that error hint is added to output if available +func TestNinjaReader_CorrectErrorHint(t *testing.T) { + errorPattern1 := "pattern-1 in input" + errorHint1 := "\n Fix by doing task 1" + errorPattern2 := "pattern-2 in input" + errorHint2 := "\n Fix by doing task 2" + mockErrorHints := make(map[string]string) + mockErrorHints[errorPattern1] = errorHint1 + mockErrorHints[errorPattern2] = errorHint2 + + errorHintGenerator := *newErrorHintGenerator(mockErrorHints) + testCases := []struct { + rawOutput string + buildExitCode int + expectedFinalOutput string + testCaseErrorMessage string + }{ + { + rawOutput: "ninja build was successful", + buildExitCode: 0, + expectedFinalOutput: "ninja build was successful", + testCaseErrorMessage: "raw output changed when build was successful", + }, + { + rawOutput: "ninja build failed", + buildExitCode: 1, + expectedFinalOutput: "ninja build failed", + testCaseErrorMessage: "raw output changed even when no error hint pattern was found", + }, + { + rawOutput: "ninja build failed: " + errorPattern1 + "some footnotes", + buildExitCode: 1, + expectedFinalOutput: "ninja build failed: " + errorPattern1 + "some footnotes" + errorHint1, + testCaseErrorMessage: "error hint not added despite pattern match", + }, + { + rawOutput: "ninja build failed: " + errorPattern2 + errorPattern1, + buildExitCode: 1, + expectedFinalOutput: "ninja build failed: " + errorPattern2 + errorPattern1 + errorHint2, + testCaseErrorMessage: "error hint should be added for first pattern match in raw output", + }, + } + for _, testCase := range testCases { + actualFinalOutput := errorHintGenerator.GetOutputWithErrorHint(testCase.rawOutput, testCase.buildExitCode) + if actualFinalOutput != testCase.expectedFinalOutput { + t.Errorf(testCase.testCaseErrorMessage+"\nexpected: %s\ngot: %s", testCase.expectedFinalOutput, actualFinalOutput) + } + } +} |