diff options
62 files changed, 2840 insertions, 797 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/prebuilt.go b/android/prebuilt.go index 43b7cbefd..f3493bd5a 100644 --- a/android/prebuilt.go +++ b/android/prebuilt.go @@ -166,6 +166,7 @@ type PrebuiltSrcsSupplier func(ctx BaseModuleContext, prebuilt Module) []string func InitPrebuiltModuleWithSrcSupplier(module PrebuiltInterface, srcsSupplier PrebuiltSrcsSupplier, srcsPropertyName string) { p := module.Prebuilt() module.AddProperties(&p.properties) + module.base().customizableProperties = module.GetProperties() if srcsSupplier == nil { panic(fmt.Errorf("srcsSupplier must not be nil")) diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go index ced37fe64..23524a533 100644 --- a/android/prebuilt_test.go +++ b/android/prebuilt_test.go @@ -259,6 +259,38 @@ var prebuiltsTests = []struct { }`, prebuilt: []OsType{Android, BuildOs}, }, + { + name: "prebuilt properties customizable", + replaceBp: true, + modules: ` + source { + name: "foo", + deps: [":bar"], + } + + soong_config_module_type { + name: "prebuilt_with_config", + module_type: "prebuilt", + config_namespace: "any_namespace", + bool_variables: ["bool_var"], + properties: ["prefer"], + } + + prebuilt_with_config { + name: "bar", + prefer: true, + srcs: ["prebuilt_file"], + soong_config_variables: { + bool_var: { + prefer: false, + conditions_default: { + prefer: true, + }, + }, + }, + }`, + prebuilt: []OsType{Android, BuildOs}, + }, } func TestPrebuilts(t *testing.T) { @@ -394,6 +426,9 @@ func registerTestPrebuiltModules(ctx RegistrationContext) { ctx.RegisterModuleType("prebuilt", newPrebuiltModule) ctx.RegisterModuleType("source", newSourceModule) ctx.RegisterModuleType("override_source", newOverrideSourceModule) + ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory) + ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory) + ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory) } type prebuiltModule struct { diff --git a/android/sdk.go b/android/sdk.go index 36c576d80..e70003144 100644 --- a/android/sdk.go +++ b/android/sdk.go @@ -38,6 +38,36 @@ type RequiredSdks interface { type sdkAwareWithoutModule interface { RequiredSdks + // SdkMemberComponentName will return the name to use for a component of this module based on the + // base name of this module. + // + // The baseName is the name returned by ModuleBase.BaseModuleName(), i.e. the name specified in + // the name property in the .bp file so will not include the prebuilt_ prefix. + // + // The componentNameCreator is a func for creating the name of a component from the base name of + // the module, e.g. it could just append ".component" to the name passed in. + // + // This is intended to be called by prebuilt modules that create component models. It is because + // prebuilt module base names come in a variety of different forms: + // * unversioned - this is the same as the source module. + // * internal to an sdk - this is the unversioned name prefixed by the base name of the sdk + // module. + // * versioned - this is the same as the internal with the addition of an "@<version>" suffix. + // + // While this can be called from a source module in that case it will behave the same way as the + // unversioned name and return the result of calling the componentNameCreator func on the supplied + // base name. + // + // e.g. Assuming the componentNameCreator func simply appends ".component" to the name passed in + // then this will work as follows: + // * An unversioned name of "foo" will return "foo.component". + // * An internal to the sdk name of "sdk_foo" will return "sdk_foo.component". + // * A versioned name of "sdk_foo@current" will return "sdk_foo.component@current". + // + // Note that in the latter case the ".component" suffix is added before the version. Adding it + // after would change the version. + SdkMemberComponentName(baseName string, componentNameCreator func(string) string) string + sdkBase() *SdkBase MakeMemberOf(sdk SdkRef) IsInAnySdk() bool @@ -135,6 +165,18 @@ func (s *SdkBase) sdkBase() *SdkBase { return s } +func (s *SdkBase) SdkMemberComponentName(baseName string, componentNameCreator func(string) string) string { + if s.MemberName() == "" { + return componentNameCreator(baseName) + } else { + index := strings.LastIndex(baseName, "@") + unversionedName := baseName[:index] + unversionedComponentName := componentNameCreator(unversionedName) + versionSuffix := baseName[index:] + return unversionedComponentName + versionSuffix + } +} + // MakeMemberOf sets this module to be a member of a specific SDK func (s *SdkBase) MakeMemberOf(sdk SdkRef) { s.properties.ContainingSdk = &sdk @@ -300,6 +342,22 @@ type BpModule interface { Name() string } +// BpPrintable is a marker interface that must be implemented by any struct that is added as a +// property value. +type BpPrintable interface { + bpPrintable() +} + +// BpPrintableBase must be embedded within any struct that is added as a +// property value. +type BpPrintableBase struct { +} + +func (b BpPrintableBase) bpPrintable() { +} + +var _ BpPrintable = BpPrintableBase{} + // An individual member of the SDK, includes all of the variants that the SDK // requires. type SdkMember interface { @@ -317,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 @@ -643,3 +703,30 @@ type SdkMemberContext interface { // into which to copy the prebuilt files. Name() string } + +// ExportedComponentsInfo contains information about the components that this module exports to an +// sdk snapshot. +// +// A component of a module is a child module that the module creates and which forms an integral +// part of the functionality that the creating module provides. A component module is essentially +// owned by its creator and is tightly coupled to the creator and other components. +// +// e.g. the child modules created by prebuilt_apis are not components because they are not tightly +// coupled to the prebuilt_apis module. Once they are created the prebuilt_apis ignores them. The +// child impl and stub library created by java_sdk_library (and corresponding import) are components +// because the creating module depends upon them in order to provide some of its own functionality. +// +// A component is exported if it is part of an sdk snapshot. e.g. The xml and impl child modules are +// components but they are not exported as they are not part of an sdk snapshot. +// +// This information is used by the sdk snapshot generation code to ensure that it does not create +// an sdk snapshot that contains a declaration of the component module and the module that creates +// it as that would result in duplicate modules when attempting to use the snapshot. e.g. a snapshot +// that included the java_sdk_library_import "foo" and also a java_import "foo.stubs" would fail +// as there would be two modules called "foo.stubs". +type ExportedComponentsInfo struct { + // The names of the exported components. + Components []string +} + +var ExportedComponentsInfoProvider = blueprint.NewProvider(ExportedComponentsInfo{}) 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 88da21bdc..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) { @@ -1695,6 +1798,36 @@ func TestApexMinSdkVersion_SupportsCodeNames(t *testing.T) { expectNoLink("libx", "shared_apex10000", "libz", "shared") } +func TestApexMinSdkVersion_SupportsCodeNames_JavaLibs(t *testing.T) { + testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + java_libs: ["libx"], + min_sdk_version: "S", + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + java_library { + name: "libx", + srcs: ["a.java"], + apex_available: [ "myapex" ], + sdk_version: "current", + min_sdk_version: "S", // should be okay + } + `, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_version_active_codenames = []string{"S"} + variables.Platform_sdk_codename = proptools.StringPtr("S") + }), + ) +} + func TestApexMinSdkVersion_DefaultsToLatest(t *testing.T) { ctx := testApex(t, ` apex { @@ -3338,60 +3471,109 @@ func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName, var } func TestVndkApexCurrent(t *testing.T) { - ctx := testApex(t, ` - apex_vndk { - name: "com.android.vndk.current", - key: "com.android.vndk.current.key", - updatable: false, - } - - apex_key { - name: "com.android.vndk.current.key", - public_key: "testkey.avbpubkey", - private_key: "testkey.pem", - } - - cc_library { - name: "libvndk", - srcs: ["mylib.cpp"], - vendor_available: true, - product_available: true, - vndk: { - enabled: true, - }, - system_shared_libs: [], - stl: "none", - apex_available: [ "com.android.vndk.current" ], - } - - cc_library { - name: "libvndksp", - srcs: ["mylib.cpp"], - vendor_available: true, - product_available: true, - vndk: { - enabled: true, - support_system_process: true, - }, - system_shared_libs: [], - stl: "none", - apex_available: [ "com.android.vndk.current" ], - } - `+vndkLibrariesTxtFiles("current")) - - ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{ - "lib/libvndk.so", - "lib/libvndksp.so", + commonFiles := []string{ "lib/libc++.so", - "lib64/libvndk.so", - "lib64/libvndksp.so", "lib64/libc++.so", "etc/llndk.libraries.29.txt", "etc/vndkcore.libraries.29.txt", "etc/vndksp.libraries.29.txt", "etc/vndkprivate.libraries.29.txt", "etc/vndkproduct.libraries.29.txt", - }) + } + testCases := []struct { + vndkVersion string + expectedFiles []string + }{ + { + vndkVersion: "current", + expectedFiles: append(commonFiles, + "lib/libvndk.so", + "lib/libvndksp.so", + "lib64/libvndk.so", + "lib64/libvndksp.so"), + }, + { + vndkVersion: "", + expectedFiles: append(commonFiles, + // Legacy VNDK APEX contains only VNDK-SP files (of core variant) + "lib/libvndksp.so", + "lib64/libvndksp.so"), + }, + } + for _, tc := range testCases { + t.Run("VNDK.current with DeviceVndkVersion="+tc.vndkVersion, func(t *testing.T) { + ctx := testApex(t, ` + apex_vndk { + name: "com.android.vndk.current", + key: "com.android.vndk.current.key", + updatable: false, + } + + apex_key { + name: "com.android.vndk.current.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "libvndk", + srcs: ["mylib.cpp"], + vendor_available: true, + product_available: true, + vndk: { + enabled: true, + }, + system_shared_libs: [], + stl: "none", + apex_available: [ "com.android.vndk.current" ], + } + + cc_library { + name: "libvndksp", + srcs: ["mylib.cpp"], + vendor_available: true, + product_available: true, + vndk: { + enabled: true, + support_system_process: true, + }, + system_shared_libs: [], + stl: "none", + apex_available: [ "com.android.vndk.current" ], + } + + // VNDK-Ext should not cause any problems + + cc_library { + name: "libvndk.ext", + srcs: ["mylib2.cpp"], + vendor: true, + vndk: { + enabled: true, + extends: "libvndk", + }, + system_shared_libs: [], + stl: "none", + } + + cc_library { + name: "libvndksp.ext", + srcs: ["mylib2.cpp"], + vendor: true, + vndk: { + enabled: true, + support_system_process: true, + extends: "libvndksp", + }, + system_shared_libs: [], + stl: "none", + } + `+vndkLibrariesTxtFiles("current"), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.DeviceVndkVersion = proptools.StringPtr(tc.vndkVersion) + })) + ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", tc.expectedFiles) + }) + } } func TestVndkApexWithPrebuilt(t *testing.T) { @@ -3893,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_" @@ -4442,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/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go index bd4a9d599..279cf54de 100644 --- a/apex/platform_bootclasspath_test.go +++ b/apex/platform_bootclasspath_test.go @@ -481,3 +481,62 @@ func CheckModuleDependencies(t *testing.T, ctx *android.TestContext, name, varia pairs := java.ApexNamePairsFromModules(ctx, modules) android.AssertDeepEquals(t, "module dependencies", expected, pairs) } + +// TestPlatformBootclasspath_IncludesRemainingApexJars verifies that any apex boot jar is present in +// platform_bootclasspath's classpaths.proto config, if the apex does not generate its own config +// by setting generate_classpaths_proto property to false. +func TestPlatformBootclasspath_IncludesRemainingApexJars(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForTestWithPlatformBootclasspath, + prepareForTestWithMyapex, + java.FixtureConfigureUpdatableBootJars("myapex:foo"), + android.FixtureWithRootAndroidBp(` + platform_bootclasspath { + name: "platform-bootclasspath", + fragments: [ + { + apex: "myapex", + module:"foo-fragment", + }, + ], + } + + apex { + name: "myapex", + key: "myapex.key", + bootclasspath_fragments: ["foo-fragment"], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + bootclasspath_fragment { + name: "foo-fragment", + generate_classpaths_proto: false, + contents: ["foo"], + apex_available: ["myapex"], + } + + java_library { + name: "foo", + srcs: ["a.java"], + system_modules: "none", + sdk_version: "none", + compile_dex: true, + apex_available: ["myapex"], + permitted_packages: ["foo"], + } + `), + ).RunTest(t) + + java.CheckClasspathFragmentProtoContentInfoProvider(t, result, + true, // proto should be generated + "myapex:foo", // apex doesn't generate its own config, so must be in platform_bootclasspath + "bootclasspath.pb", + "out/soong/target/product/test_device/system/etc/classpaths", + ) +} 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"], } @@ -832,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 } @@ -1260,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) } @@ -1505,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 { @@ -2647,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. @@ -2919,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/global.go b/cc/config/global.go index 24e10a458..495776795 100644 --- a/cc/config/global.go +++ b/cc/config/global.go @@ -46,6 +46,7 @@ var ( "-O2", "-g", + "-fdebug-default-version=5", "-fdebug-info-for-profiling", "-fno-strict-aliasing", @@ -145,8 +146,8 @@ var ( // prebuilts/clang default settings. ClangDefaultBase = "prebuilts/clang/host" - ClangDefaultVersion = "clang-r416183b" - ClangDefaultShortVersion = "12.0.5" + ClangDefaultVersion = "clang-r416183b1" + ClangDefaultShortVersion = "12.0.7" // Directories with warnings from Android.bp files. WarningAllowedProjects = []string{ diff --git a/cc/library.go b/cc/library.go index c0d1345d7..4fd7c7475 100644 --- a/cc/library.go +++ b/cc/library.go @@ -185,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 { 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/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.go b/cc/sanitize.go index 513730ae9..b0eb0c6ab 100644 --- a/cc/sanitize.go +++ b/cc/sanitize.go @@ -485,6 +485,11 @@ func (sanitize *sanitize) begin(ctx BaseModuleContext) { if Bool(s.Hwaddress) { s.Address = nil s.Thread = nil + // Disable ubsan diagnosic as a workaround for a compiler bug. + // TODO(b/191808836): re-enable. + s.Diag.Undefined = nil + s.Diag.Integer_overflow = nil + s.Diag.Misc_undefined = nil } // TODO(b/131771163): CFI transiently depends on LTO, and thus Fuzzer is 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/cc/vndk.go b/cc/vndk.go index dd1c3e14b..0b4007605 100644 --- a/cc/vndk.go +++ b/cc/vndk.go @@ -371,7 +371,7 @@ func IsForVndkApex(mctx android.BottomUpMutatorContext, m *Module) bool { if mctx.ModuleName() == "libz" { return false } - return m.ImageVariation().Variation == android.CoreVariation && lib.shared() && m.IsVndkSp() + return m.ImageVariation().Variation == android.CoreVariation && lib.shared() && m.IsVndkSp() && !m.IsVndkExt() } useCoreVariant := m.VndkVersion() == mctx.DeviceConfig().PlatformVndkVersion() && 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 a7cc58e33..6b8119619 100644 --- a/java/base.go +++ b/java/base.go @@ -260,8 +260,8 @@ type embeddableInModuleAndImport struct { EmbeddableSdkLibraryComponent } -func (e *embeddableInModuleAndImport) initModuleAndImport(moduleBase *android.ModuleBase) { - e.initSdkLibraryComponent(moduleBase) +func (e *embeddableInModuleAndImport) initModuleAndImport(module android.Module) { + e.initSdkLibraryComponent(module) } // Module/Import's DepIsInSameApex(...) delegates to this method. @@ -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() { @@ -1513,12 +1515,8 @@ func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, if sdkSpec.Kind == android.SdkCore { return nil } - ver, err := sdkSpec.EffectiveVersion(ctx) - if err != nil { - return err - } - if ver.GreaterThan(sdkVersion) { - return fmt.Errorf("newer SDK(%v)", ver) + if sdkSpec.ApiLevel.GreaterThan(sdkVersion) { + return fmt.Errorf("newer SDK(%v)", sdkSpec.ApiLevel) } return nil } diff --git a/java/bootclasspath.go b/java/bootclasspath.go index eddcc838b..4108770cd 100644 --- a/java/bootclasspath.go +++ b/java/bootclasspath.go @@ -144,6 +144,8 @@ func gatherApexModulePairDepsWithTag(ctx android.BaseModuleContext, tag blueprin // ApexVariantReference specifies a particular apex variant of a module. type ApexVariantReference struct { + android.BpPrintableBase + // The name of the module apex variant, i.e. the apex containing the module variant. // // If this is not specified then it defaults to "platform" which will cause a dependency to be @@ -225,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 4d23820fc..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 @@ -496,13 +515,14 @@ func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleC // generateClasspathProtoBuildActions generates all required build actions for classpath.proto config func (b *BootclasspathFragmentModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) { var classpathJars []classpathJar + configuredJars := b.configuredJars(ctx) if "art" == proptools.String(b.properties.Image_name) { // ART and platform boot jars must have a corresponding entry in DEX2OATBOOTCLASSPATH - classpathJars = configuredJarListToClasspathJars(ctx, b.configuredJars(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH) + classpathJars = configuredJarListToClasspathJars(ctx, configuredJars, BOOTCLASSPATH, DEX2OATBOOTCLASSPATH) } else { - classpathJars = configuredJarListToClasspathJars(ctx, b.configuredJars(ctx), b.classpathType) + classpathJars = configuredJarListToClasspathJars(ctx, configuredJars, b.classpathType) } - b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars) + b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars) } func (b *BootclasspathFragmentModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList { @@ -594,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 @@ -638,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 } @@ -748,6 +768,9 @@ type bootclasspathFragmentSdkMemberProperties struct { Stub_libs []string Core_platform_stub_libs []string + // Fragment properties + Fragments []ApexVariantReference + // Flag files by *hiddenAPIFlagFileCategory Flag_files_by_category FlagFilesByCategory @@ -788,6 +811,9 @@ func (b *bootclasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx andro // Copy stub_libs properties. b.Stub_libs = module.properties.Api.Stub_libs b.Core_platform_stub_libs = module.properties.Core_platform_api.Stub_libs + + // Copy fragment properties. + b.Fragments = module.properties.Fragments } func (b *bootclasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) { @@ -810,6 +836,9 @@ func (b *bootclasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android. corePlatformApiPropertySet := propertySet.AddPropertySet("core_platform_api") corePlatformApiPropertySet.AddPropertyWithTag("stub_libs", b.Core_platform_stub_libs, requiredMemberDependency) } + if len(b.Fragments) > 0 { + propertySet.AddProperty("fragments", b.Fragments) + } hiddenAPISet := propertySet.AddPropertySet("hidden_api") hiddenAPIDir := "hiddenapi" 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/classpath_fragment.go b/java/classpath_fragment.go index 3d7258086..f7a200ad3 100644 --- a/java/classpath_fragment.go +++ b/java/classpath_fragment.go @@ -106,7 +106,7 @@ func configuredJarListToClasspathJars(ctx android.ModuleContext, configuredJars return jars } -func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, jars []classpathJar) { +func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, configuredJars android.ConfiguredJarList, jars []classpathJar) { generateProto := proptools.BoolDefault(c.properties.Generate_classpaths_proto, true) if generateProto { outputFilename := strings.ToLower(c.classpathType.String()) + ".pb" @@ -129,6 +129,7 @@ func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.M classpathProtoInfo := ClasspathFragmentProtoContentInfo{ ClasspathFragmentProtoGenerated: generateProto, + ClasspathFragmentProtoContents: configuredJars, ClasspathFragmentProtoInstallDir: c.installDirPath, ClasspathFragmentProtoOutput: c.outputFilepath, } @@ -177,6 +178,9 @@ type ClasspathFragmentProtoContentInfo struct { // Whether the classpaths.proto config is generated for the fragment. ClasspathFragmentProtoGenerated bool + // ClasspathFragmentProtoContents contains a list of jars that are part of this classpath fragment. + ClasspathFragmentProtoContents android.ConfiguredJarList + // ClasspathFragmentProtoOutput is an output path for the generated classpaths.proto config of this module. // // The file should be copied to a relevant place on device, see ClasspathFragmentProtoInstallDir diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index bb857845a..03769fab7 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -884,8 +884,9 @@ func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) { ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+sfx, variant.installs.String()) ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+sfx, variant.unstrippedInstalls.String()) } - imageLocationsOnHost, _ := current.getAnyAndroidVariant().imageLocations() - ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_"+current.name, strings.Join(imageLocationsOnHost, ":")) + imageLocationsOnHost, imageLocationsOnDevice := current.getAnyAndroidVariant().imageLocations() + ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_HOST"+current.name, strings.Join(imageLocationsOnHost, ":")) + ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICE"+current.name, strings.Join(imageLocationsOnDevice, ":")) ctx.Strict("DEXPREOPT_IMAGE_ZIP_"+current.name, current.zip.String()) } ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(imageNames, " ")) 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/java.go b/java/java.go index ae8adf277..be1ad874f 100644 --- a/java/java.go +++ b/java/java.go @@ -643,7 +643,7 @@ func LibraryFactory() android.Module { module.addHostAndDeviceProperties() - module.initModuleAndImport(&module.ModuleBase) + module.initModuleAndImport(module) android.InitApexModule(module) android.InitSdkAwareModule(module) @@ -1416,12 +1416,8 @@ func (j *Import) ShouldSupportSdkVersion(ctx android.BaseModuleContext, if sdkSpec.Kind == android.SdkCore { return nil } - ver, err := sdkSpec.EffectiveVersion(ctx) - if err != nil { - return err - } - if ver.GreaterThan(sdkVersion) { - return fmt.Errorf("newer SDK(%v)", ver) + if sdkSpec.ApiLevel.GreaterThan(sdkVersion) { + return fmt.Errorf("newer SDK(%v)", sdkSpec.ApiLevel) } return nil } @@ -1496,7 +1492,7 @@ func ImportFactory() android.Module { &module.dexer.dexProperties, ) - module.initModuleAndImport(&module.ModuleBase) + module.initModuleAndImport(module) module.dexProperties.Optimize.EnabledByDefault = false 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 2a6a83e74..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) { @@ -198,13 +198,29 @@ func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.Mo // Generate classpaths.proto config func (b *platformBootclasspathModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) { + configuredJars := b.configuredJars(ctx) // ART and platform boot jars must have a corresponding entry in DEX2OATBOOTCLASSPATH - classpathJars := configuredJarListToClasspathJars(ctx, b.configuredJars(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH) - b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars) + classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, BOOTCLASSPATH, DEX2OATBOOTCLASSPATH) + b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars) } func (b *platformBootclasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList { - return b.getImageConfig(ctx).modules + // Include all non APEX jars + jars := b.getImageConfig(ctx).modules + + // Include jars from APEXes that don't populate their classpath proto config. + remainingJars := dexpreopt.GetGlobalConfig(ctx).UpdatableBootJars + for _, fragment := range b.fragments { + info := ctx.OtherModuleProvider(fragment, ClasspathFragmentProtoContentInfoProvider).(ClasspathFragmentProtoContentInfo) + if info.ClasspathFragmentProtoGenerated { + remainingJars = remainingJars.RemoveList(info.ClasspathFragmentProtoContents) + } + } + for i := 0; i < remainingJars.Len(); i++ { + jars = jars.Append(remainingJars.Apex(i), remainingJars.Jar(i)) + } + + return jars } // checkNonUpdatableModules ensures that the non-updatable modules supplied are not part of an @@ -287,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 @@ -299,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.go b/java/sdk_library.go index dae31b9a3..567e29219 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -631,9 +631,17 @@ type commonToSdkLibraryAndImportProperties struct { Doctag_files []string `android:"path"` } +// commonSdkLibraryAndImportModule defines the interface that must be provided by a module that +// embeds the commonToSdkLibraryAndImport struct. +type commonSdkLibraryAndImportModule interface { + android.SdkAware + + BaseModuleName() string +} + // Common code between sdk library and sdk library import type commonToSdkLibraryAndImport struct { - moduleBase *android.ModuleBase + module commonSdkLibraryAndImportModule scopePaths map[*apiScope]*scopePaths @@ -648,13 +656,13 @@ type commonToSdkLibraryAndImport struct { EmbeddableSdkLibraryComponent } -func (c *commonToSdkLibraryAndImport) initCommon(moduleBase *android.ModuleBase) { - c.moduleBase = moduleBase +func (c *commonToSdkLibraryAndImport) initCommon(module commonSdkLibraryAndImportModule) { + c.module = module - moduleBase.AddProperties(&c.commonSdkLibraryProperties) + module.AddProperties(&c.commonSdkLibraryProperties) // Initialize this as an sdk library component. - c.initSdkLibraryComponent(moduleBase) + c.initSdkLibraryComponent(module) } func (c *commonToSdkLibraryAndImport) initCommonAfterDefaultsApplied(ctx android.DefaultableHookContext) bool { @@ -670,35 +678,50 @@ func (c *commonToSdkLibraryAndImport) initCommonAfterDefaultsApplied(ctx android // Only track this sdk library if this can be used as a shared library. if c.sharedLibrary() { // Use the name specified in the module definition as the owner. - c.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.moduleBase.BaseModuleName()) + c.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.module.BaseModuleName()) } return true } +// uniqueApexVariations provides common implementation of the ApexModule.UniqueApexVariations +// method. +func (c *commonToSdkLibraryAndImport) uniqueApexVariations() bool { + // A java_sdk_library that is a shared library produces an XML file that makes the shared library + // usable from an AndroidManifest.xml's <uses-library> entry. That XML file contains the name of + // the APEX and so it needs a unique variation per APEX. + return c.sharedLibrary() +} + func (c *commonToSdkLibraryAndImport) generateCommonBuildActions(ctx android.ModuleContext) { c.doctagPaths = android.PathsForModuleSrc(ctx, c.commonSdkLibraryProperties.Doctag_files) } // Module name of the runtime implementation library func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string { - return c.moduleBase.BaseModuleName() + ".impl" + return c.module.BaseModuleName() + ".impl" } // Module name of the XML file for the lib func (c *commonToSdkLibraryAndImport) xmlPermissionsModuleName() string { - return c.moduleBase.BaseModuleName() + sdkXmlFileSuffix + return c.module.BaseModuleName() + sdkXmlFileSuffix } // Name of the java_library module that compiles the stubs source. func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string { - return c.namingScheme.stubsLibraryModuleName(apiScope, c.moduleBase.BaseModuleName()) + baseName := c.module.BaseModuleName() + return c.module.SdkMemberComponentName(baseName, func(name string) string { + return c.namingScheme.stubsLibraryModuleName(apiScope, name) + }) } // Name of the droidstubs module that generates the stubs source and may also // generate/check the API. func (c *commonToSdkLibraryAndImport) stubsSourceModuleName(apiScope *apiScope) string { - return c.namingScheme.stubsSourceModuleName(apiScope, c.moduleBase.BaseModuleName()) + baseName := c.module.BaseModuleName() + return c.module.SdkMemberComponentName(baseName, func(name string) string { + return c.namingScheme.stubsSourceModuleName(apiScope, name) + }) } // The component names for different outputs of the java_sdk_library. @@ -747,7 +770,7 @@ func (c *commonToSdkLibraryAndImport) commonOutputFiles(tag string) (android.Pat if scope, ok := scopeByName[scopeName]; ok { paths := c.findScopePaths(scope) if paths == nil { - return nil, fmt.Errorf("%q does not provide api scope %s", c.moduleBase.BaseModuleName(), scopeName) + return nil, fmt.Errorf("%q does not provide api scope %s", c.module.BaseModuleName(), scopeName) } switch component { @@ -778,7 +801,7 @@ func (c *commonToSdkLibraryAndImport) commonOutputFiles(tag string) (android.Pat if c.doctagPaths != nil { return c.doctagPaths, nil } else { - return nil, fmt.Errorf("no doctag_files specified on %s", c.moduleBase.BaseModuleName()) + return nil, fmt.Errorf("no doctag_files specified on %s", c.module.BaseModuleName()) } } return nil, nil @@ -824,7 +847,7 @@ func (c *commonToSdkLibraryAndImport) selectHeaderJarsForSdkVersion(ctx android. // If a specific numeric version has been requested then use prebuilt versions of the sdk. if !sdkVersion.ApiLevel.IsPreview() { - return PrebuiltJars(ctx, c.moduleBase.BaseModuleName(), sdkVersion) + return PrebuiltJars(ctx, c.module.BaseModuleName(), sdkVersion) } paths := c.selectScopePaths(ctx, sdkVersion.Kind) @@ -851,7 +874,7 @@ func (c *commonToSdkLibraryAndImport) selectScopePaths(ctx android.BaseModuleCon scopes = append(scopes, s.name) } } - ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.moduleBase.BaseModuleName(), scopes) + ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.module.BaseModuleName(), scopes) return nil } @@ -907,7 +930,7 @@ func (c *commonToSdkLibraryAndImport) sdkComponentPropertiesForChildLibrary() in // any app that includes code which depends (directly or indirectly) on the stubs // library will have the appropriate <uses-library> invocation inserted into its // manifest if necessary. - componentProps.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.moduleBase.BaseModuleName()) + componentProps.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.module.BaseModuleName()) } return componentProps @@ -939,8 +962,8 @@ type EmbeddableSdkLibraryComponent struct { sdkLibraryComponentProperties SdkLibraryComponentProperties } -func (e *EmbeddableSdkLibraryComponent) initSdkLibraryComponent(moduleBase *android.ModuleBase) { - moduleBase.AddProperties(&e.sdkLibraryComponentProperties) +func (e *EmbeddableSdkLibraryComponent) initSdkLibraryComponent(module android.Module) { + module.AddProperties(&e.sdkLibraryComponentProperties) } // to satisfy SdkLibraryComponentDependency @@ -1162,6 +1185,10 @@ func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) module.Library.GenerateAndroidBuildActions(ctx) } + // Collate the components exported by this module. All scope specific modules are exported but + // the impl and xml component modules are not. + exportedComponents := map[string]struct{}{} + // Record the paths to the header jars of the library (stubs and impl). // When this java_sdk_library is depended upon from others via "libs" property, // the recorded paths will be returned depending on the link type of the caller. @@ -1176,8 +1203,14 @@ func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) // Extract information from the dependency. The exact information extracted // is determined by the nature of the dependency which is determined by the tag. scopeTag.extractDepInfo(ctx, to, scopePaths) + + exportedComponents[ctx.OtherModuleName(to)] = struct{}{} } }) + + // Make the set of components exported by this module available for use elsewhere. + exportedComponentInfo := android.ExportedComponentsInfo{Components: android.SortedStringKeys(exportedComponents)} + ctx.SetProvider(android.ExportedComponentsInfoProvider, exportedComponentInfo) } func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries { @@ -1504,6 +1537,7 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC mctx.CreateModule(DroidstubsFactory, &props) } +// Implements android.ApexModule func (module *SdkLibrary) DepIsInSameApex(mctx android.BaseModuleContext, dep android.Module) bool { depTag := mctx.OtherModuleDependencyTag(dep) if depTag == xmlPermissionsFileTag { @@ -1512,6 +1546,11 @@ func (module *SdkLibrary) DepIsInSameApex(mctx android.BaseModuleContext, dep an return module.Library.DepIsInSameApex(mctx, dep) } +// Implements android.ApexModule +func (module *SdkLibrary) UniqueApexVariations() bool { + return module.uniqueApexVariations() +} + // Creates the xml file that publicizes the runtime library func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) { props := struct { @@ -1707,7 +1746,7 @@ func (module *SdkLibrary) InitSdkLibraryProperties() { module.addHostAndDeviceProperties() module.AddProperties(&module.sdkLibraryProperties) - module.initSdkLibraryComponent(&module.ModuleBase) + module.initSdkLibraryComponent(module) module.properties.Installable = proptools.BoolPtr(true) module.deviceProperties.IsSDKLibrary = true @@ -1772,7 +1811,7 @@ func SdkLibraryFactory() android.Module { module := &SdkLibrary{} // Initialize information common between source and prebuilt. - module.initCommon(&module.ModuleBase) + module.initCommon(module) module.InitSdkLibraryProperties() android.InitApexModule(module) @@ -1920,7 +1959,7 @@ func sdkLibraryImportFactory() android.Module { module.AddProperties(&module.properties, allScopeProperties) // Initialize information common between source and prebuilt. - module.initCommon(&module.ModuleBase) + module.initCommon(module) android.InitPrebuiltModule(module, &[]string{""}) android.InitApexModule(module) @@ -2066,6 +2105,11 @@ func (module *SdkLibraryImport) ShouldSupportSdkVersion(ctx android.BaseModuleCo return nil } +// Implements android.ApexModule +func (module *SdkLibraryImport) UniqueApexVariations() bool { + return module.uniqueApexVariations() +} + func (module *SdkLibraryImport) OutputFiles(tag string) (android.Paths, error) { return module.commonOutputFiles(tag) } diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go index 2520dde6e..8e0618e31 100644 --- a/java/sdk_library_test.go +++ b/java/sdk_library_test.go @@ -110,7 +110,7 @@ func TestJavaSdkLibrary(t *testing.T) { `) // check the existence of the internal modules - result.ModuleForTests("foo", "android_common") + foo := result.ModuleForTests("foo", "android_common") result.ModuleForTests(apiScopePublic.stubsLibraryModuleName("foo"), "android_common") result.ModuleForTests(apiScopeSystem.stubsLibraryModuleName("foo"), "android_common") result.ModuleForTests(apiScopeTest.stubsLibraryModuleName("foo"), "android_common") @@ -122,6 +122,17 @@ func TestJavaSdkLibrary(t *testing.T) { result.ModuleForTests("foo.api.system.28", "") result.ModuleForTests("foo.api.test.28", "") + exportedComponentsInfo := result.ModuleProvider(foo.Module(), android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo) + expectedFooExportedComponents := []string{ + "foo.stubs", + "foo.stubs.source", + "foo.stubs.source.system", + "foo.stubs.source.test", + "foo.stubs.system", + "foo.stubs.test", + } + android.AssertArrayString(t, "foo exported components", expectedFooExportedComponents, exportedComponentsInfo.Components) + bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac") // tests if baz is actually linked to the stubs lib android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "foo.stubs.system.jar") diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go index a0decb7e9..252c6151f 100644 --- a/java/systemserver_classpath_fragment.go +++ b/java/systemserver_classpath_fragment.go @@ -48,13 +48,14 @@ func (p *platformSystemServerClasspathModule) AndroidMkEntries() (entries []andr } func (p *platformSystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { - classpathJars := configuredJarListToClasspathJars(ctx, p.configuredJars(ctx), p.classpathType) - p.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars) + configuredJars := p.configuredJars(ctx) + classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, p.classpathType) + p.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars) } func (p *platformSystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList { - global := dexpreopt.GetGlobalConfig(ctx) - return global.SystemServerJars + // TODO(satayev): include any apex jars that don't populate their classpath proto config. + return dexpreopt.GetGlobalConfig(ctx).SystemServerJars } type SystemServerClasspathModule struct { @@ -94,8 +95,9 @@ func (s *SystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.Mo ctx.PropertyErrorf("contents", "empty contents are not allowed") } - classpathJars := configuredJarListToClasspathJars(ctx, s.configuredJars(ctx), s.classpathType) - s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars) + configuredJars := s.configuredJars(ctx) + classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, s.classpathType) + s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars) // Collect the module directory for IDE info in java/jdeps.go. s.modulePaths = append(s.modulePaths, ctx.ModuleDir()) diff --git a/java/testing.go b/java/testing.go index 7b452f762..c3803c8d4 100644 --- a/java/testing.go +++ b/java/testing.go @@ -363,6 +363,17 @@ func CheckPlatformBootclasspathModules(t *testing.T, result *android.TestResult, android.AssertDeepEquals(t, fmt.Sprintf("%s modules", "platform-bootclasspath"), expected, pairs) } +func CheckClasspathFragmentProtoContentInfoProvider(t *testing.T, result *android.TestResult, generated bool, contents, outputFilename, installDir string) { + t.Helper() + p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule) + info := result.ModuleProvider(p, ClasspathFragmentProtoContentInfoProvider).(ClasspathFragmentProtoContentInfo) + + android.AssertBoolEquals(t, "classpath proto generated", generated, info.ClasspathFragmentProtoGenerated) + android.AssertStringEquals(t, "classpath proto contents", contents, info.ClasspathFragmentProtoContents.String()) + android.AssertStringEquals(t, "output filepath", outputFilename, info.ClasspathFragmentProtoOutput.Base()) + android.AssertPathRelativeToTopEquals(t, "install filepath", installDir, info.ClasspathFragmentProtoInstallDir) +} + // ApexNamePairsFromModules returns the apex:module pair for the supplied modules. func ApexNamePairsFromModules(ctx *android.TestContext, modules []android.Module) []string { pairs := []string{} 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/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go index 378f7cfaa..a458cba7d 100644 --- a/sdk/bootclasspath_fragment_sdk_test.go +++ b/sdk/bootclasspath_fragment_sdk_test.go @@ -273,7 +273,7 @@ func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) { name: "myothersdklibrary", apex_available: ["myapex"], srcs: ["Test.java"], - shared_library: false, + compile_dex: true, public: {enabled: true}, min_sdk_version: "2", permitted_packages: ["myothersdklibrary"], @@ -283,7 +283,7 @@ func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) { name: "mycoreplatform", apex_available: ["myapex"], srcs: ["Test.java"], - shared_library: false, + compile_dex: true, public: {enabled: true}, min_sdk_version: "2", } @@ -334,7 +334,8 @@ java_sdk_library_import { prefer: false, visibility: ["//visibility:public"], apex_available: ["myapex"], - shared_library: false, + shared_library: true, + compile_dex: true, public: { jars: ["sdk_library/public/myothersdklibrary-stubs.jar"], stub_srcs: ["sdk_library/public/myothersdklibrary_stub_sources"], @@ -364,7 +365,8 @@ java_sdk_library_import { prefer: false, visibility: ["//visibility:public"], apex_available: ["myapex"], - shared_library: false, + shared_library: true, + compile_dex: true, public: { jars: ["sdk_library/public/mycoreplatform-stubs.jar"], stub_srcs: ["sdk_library/public/mycoreplatform_stub_sources"], @@ -414,7 +416,8 @@ java_sdk_library_import { sdk_member_name: "myothersdklibrary", visibility: ["//visibility:public"], apex_available: ["myapex"], - shared_library: false, + shared_library: true, + compile_dex: true, public: { jars: ["sdk_library/public/myothersdklibrary-stubs.jar"], stub_srcs: ["sdk_library/public/myothersdklibrary_stub_sources"], @@ -444,7 +447,8 @@ java_sdk_library_import { sdk_member_name: "mycoreplatform", visibility: ["//visibility:public"], apex_available: ["myapex"], - shared_library: false, + shared_library: true, + compile_dex: true, public: { jars: ["sdk_library/public/mycoreplatform-stubs.jar"], stub_srcs: ["sdk_library/public/mycoreplatform_stub_sources"], @@ -511,6 +515,127 @@ sdk_snapshot { ) } +// TestSnapshotWithBootClasspathFragment_Fragments makes sure that the fragments property of a +// bootclasspath_fragment is correctly output to the sdk snapshot. +func TestSnapshotWithBootClasspathFragment_Fragments(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForSdkTestWithJava, + java.PrepareForTestWithJavaDefaultModules, + java.PrepareForTestWithJavaSdkLibraryFiles, + java.FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary"), + prepareForSdkTestWithApex, + + // Some additional files needed for the myotherapex. + android.FixtureMergeMockFs(android.MockFS{ + "system/sepolicy/apex/myotherapex-file_contexts": nil, + "myotherapex/apex_manifest.json": nil, + "myotherapex/Test.java": nil, + }), + + android.FixtureAddTextFile("myotherapex/Android.bp", ` + apex { + name: "myotherapex", + key: "myapex.key", + min_sdk_version: "2", + bootclasspath_fragments: ["myotherbootclasspathfragment"], + } + + bootclasspath_fragment { + name: "myotherbootclasspathfragment", + apex_available: ["myotherapex"], + contents: [ + "myotherlib", + ], + } + + java_library { + name: "myotherlib", + apex_available: ["myotherapex"], + srcs: ["Test.java"], + min_sdk_version: "2", + permitted_packages: ["myothersdklibrary"], + compile_dex: true, + } + `), + + android.FixtureWithRootAndroidBp(` + sdk { + name: "mysdk", + bootclasspath_fragments: ["mybootclasspathfragment"], + } + + bootclasspath_fragment { + name: "mybootclasspathfragment", + contents: [ + "mysdklibrary", + ], + fragments: [ + { + apex: "myotherapex", + module: "myotherbootclasspathfragment" + }, + ], + } + + java_sdk_library { + name: "mysdklibrary", + srcs: ["Test.java"], + shared_library: false, + public: {enabled: true}, + min_sdk_version: "2", + } + `), + ).RunTest(t) + + // A preparer to update the test fixture used when processing an unpackage snapshot. + preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment") + + CheckSnapshot(t, result, "mysdk", "", + checkUnversionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. + +prebuilt_bootclasspath_fragment { + name: "mybootclasspathfragment", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + contents: ["mysdklibrary"], + fragments: [ + { + apex: "myotherapex", + module: "myotherbootclasspathfragment", + }, + ], + hidden_api: { + stub_flags: "hiddenapi/stub-flags.csv", + annotation_flags: "hiddenapi/annotation-flags.csv", + metadata: "hiddenapi/metadata.csv", + index: "hiddenapi/index.csv", + all_flags: "hiddenapi/all-flags.csv", + }, +} + +java_sdk_library_import { + name: "mysdklibrary", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + shared_library: false, + public: { + jars: ["sdk_library/public/mysdklibrary-stubs.jar"], + stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"], + current_api: "sdk_library/public/mysdklibrary.txt", + removed_api: "sdk_library/public/mysdklibrary-removed.txt", + sdk_version: "current", + }, +} + `), + snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot), + snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot), + snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot), + ) +} + // Test that bootclasspath_fragment works with sdk. func TestBasicSdkWithBootclasspathFragment(t *testing.T) { android.GroupFixturePreparers( diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go index ad2bd7599..a2cfe6d16 100644 --- a/sdk/java_sdk_test.go +++ b/sdk/java_sdk_test.go @@ -722,14 +722,6 @@ java_import { jars: ["java/system-module.jar"], } -java_import { - name: "mysdk_myjavalib.stubs", - prefer: false, - visibility: ["//visibility:private"], - apex_available: ["//apex_available:platform"], - jars: ["java/myjavalib.stubs.jar"], -} - java_sdk_library_import { name: "myjavalib", prefer: false, @@ -752,7 +744,7 @@ java_system_modules_import { libs: [ "mysdk_system-module", "exported-system-module", - "mysdk_myjavalib.stubs", + "myjavalib.stubs", ], } `), @@ -775,14 +767,6 @@ java_import { jars: ["java/system-module.jar"], } -java_import { - name: "mysdk_myjavalib.stubs@current", - sdk_member_name: "myjavalib.stubs", - visibility: ["//visibility:private"], - apex_available: ["//apex_available:platform"], - jars: ["java/myjavalib.stubs.jar"], -} - java_sdk_library_import { name: "mysdk_myjavalib@current", sdk_member_name: "myjavalib", @@ -820,7 +804,6 @@ sdk_snapshot { checkAllCopyRules(` .intermediates/exported-system-module/android_common/turbine-combined/exported-system-module.jar -> java/exported-system-module.jar .intermediates/system-module/android_common/turbine-combined/system-module.jar -> java/system-module.jar -.intermediates/myjavalib.stubs/android_common/turbine-combined/myjavalib.stubs.jar -> java/myjavalib.stubs.jar .intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar .intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt .intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt @@ -1153,10 +1136,10 @@ sdk_snapshot { ".intermediates/mysdk/common_os/tmp/sdk_library/test/myjavalib_stub_sources.zip", ), snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) { - // Show that the existing behavior is incorrect as the suffix for the child modules is added - // to the version not before it. - result.Module("mysdk_myjavalib@current.stubs", "android_common") - result.Module("mysdk_myjavalib@current.stubs.source", "android_common") + // Make sure that the name of the child modules created by a versioned java_sdk_library_import + // module is correct, i.e. the suffix is added before the version and not after. + result.Module("mysdk_myjavalib.stubs@current", "android_common") + result.Module("mysdk_myjavalib.stubs.source@current", "android_common") }), ) } diff --git a/sdk/update.go b/sdk/update.go index 36b564fe6..b146b62c8 100644 --- a/sdk/update.go +++ b/sdk/update.go @@ -22,7 +22,6 @@ import ( "android/soong/apex" "android/soong/cc" - "github.com/google/blueprint" "github.com/google/blueprint/proptools" @@ -114,8 +113,16 @@ func (gc *generatedContents) Dedent() { gc.indentLevel-- } -func (gc *generatedContents) Printfln(format string, args ...interface{}) { - fmt.Fprintf(&(gc.content), strings.Repeat(" ", gc.indentLevel)+format+"\n", args...) +// IndentedPrintf will add spaces to indent the line to the appropriate level before printing the +// arguments. +func (gc *generatedContents) IndentedPrintf(format string, args ...interface{}) { + fmt.Fprintf(&(gc.content), strings.Repeat(" ", gc.indentLevel)+format, args...) +} + +// UnindentedPrintf does not add spaces to indent the line to the appropriate level before printing +// the arguments. +func (gc *generatedContents) UnindentedPrintf(format string, args ...interface{}) { + fmt.Fprintf(&(gc.content), format, args...) } func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderContext, implicits android.Paths) { @@ -140,8 +147,8 @@ func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderC // Collect all the members. // -// Updates the sdk module with a list of sdkMemberVariantDeps and details as to which multilibs -// (32/64/both) are used by this sdk variant. +// Updates the sdk module with a list of sdkMemberVariantDep instances and details as to which +// multilibs (32/64/both) are used by this sdk variant. func (s *sdk) collectMembers(ctx android.ModuleContext) { s.multilibUsages = multilibNone ctx.WalkDeps(func(child android.Module, parent android.Module) bool { @@ -149,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()) @@ -157,8 +169,15 @@ func (s *sdk) collectMembers(ctx android.ModuleContext) { // Keep track of which multilib variants are used by the sdk. s.multilibUsages = s.multilibUsages.addArchType(child.Target().Arch.ArchType) + var exportedComponentsInfo android.ExportedComponentsInfo + if ctx.OtherModuleHasProvider(child, android.ExportedComponentsInfoProvider) { + exportedComponentsInfo = ctx.OtherModuleProvider(child, android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo) + } + export := memberTag.ExportMember() - s.memberVariantDeps = append(s.memberVariantDeps, sdkMemberVariantDep{s, memberType, child.(android.SdkAware), export}) + s.memberVariantDeps = append(s.memberVariantDeps, sdkMemberVariantDep{ + s, memberType, child.(android.SdkAware), export, exportedComponentsInfo, + }) // Recurse down into the member's dependencies as it may have dependencies that need to be // automatically added to the sdk. @@ -245,26 +264,41 @@ func versionedSdkMemberName(ctx android.ModuleContext, memberName string, versio // the contents (header files, stub libraries, etc) into the zip file. func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) android.OutputPath { - allMembersByName := make(map[string]struct{}) - exportedMembersByName := make(map[string]struct{}) + // Aggregate all the sdkMemberVariantDep instances from all the sdk variants. hasLicenses := false var memberVariantDeps []sdkMemberVariantDep for _, sdkVariant := range sdkVariants { memberVariantDeps = append(memberVariantDeps, sdkVariant.memberVariantDeps...) + } - // Record the names of all the members, both explicitly specified and implicitly - // included. - for _, memberVariantDep := range sdkVariant.memberVariantDeps { - name := memberVariantDep.variant.Name() - allMembersByName[name] = struct{}{} + // Filter out any sdkMemberVariantDep that is a component of another. + memberVariantDeps = filterOutComponents(ctx, memberVariantDeps) - if memberVariantDep.export { - exportedMembersByName[name] = struct{}{} - } + // Record the names of all the members, both explicitly specified and implicitly + // included. + allMembersByName := make(map[string]struct{}) + exportedMembersByName := make(map[string]struct{}) - if memberVariantDep.memberType == android.LicenseModuleSdkMemberType { - hasLicenses = true - } + addMember := func(name string, export bool) { + allMembersByName[name] = struct{}{} + if export { + exportedMembersByName[name] = struct{}{} + } + } + + for _, memberVariantDep := range memberVariantDeps { + name := memberVariantDep.variant.Name() + export := memberVariantDep.export + + addMember(name, export) + + // Add any components provided by the module. + for _, component := range memberVariantDep.exportedComponentsInfo.Components { + addMember(component, export) + } + + if memberVariantDep.memberType == android.LicenseModuleSdkMemberType { + hasLicenses = true } } @@ -423,6 +457,47 @@ be unnecessary as every module in the sdk already has its own licenses property. return outputZipFile } +// filterOutComponents removes any item from the deps list that is a component of another item in +// the deps list, e.g. if the deps list contains "foo" and "foo.stubs" which is component of "foo" +// then it will remove "foo.stubs" from the deps. +func filterOutComponents(ctx android.ModuleContext, deps []sdkMemberVariantDep) []sdkMemberVariantDep { + // Collate the set of components that all the modules added to the sdk provide. + components := map[string]*sdkMemberVariantDep{} + for i, _ := range deps { + dep := &deps[i] + for _, c := range dep.exportedComponentsInfo.Components { + components[c] = dep + } + } + + // If no module provides components then return the input deps unfiltered. + if len(components) == 0 { + return deps + } + + filtered := make([]sdkMemberVariantDep, 0, len(deps)) + for _, dep := range deps { + name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(dep.variant)) + if owner, ok := components[name]; ok { + // This is a component of another module that is a member of the sdk. + + // If the component is exported but the owning module is not then the configuration is not + // supported. + if dep.export && !owner.export { + ctx.ModuleErrorf("Module %s is internal to the SDK but provides component %s which is used outside the SDK") + continue + } + + // This module must not be added to the list of members of the sdk as that would result in a + // duplicate module in the sdk snapshot. + continue + } + + filtered = append(filtered, dep) + } + return filtered +} + // addSnapshotModule adds the sdk_snapshot/module_exports_snapshot module to the builder. func (s *sdk) addSnapshotModule(ctx android.ModuleContext, builder *snapshotBuilder, sdkVariants []*sdk, memberVariantDeps []sdkMemberVariantDep) { bpFile := builder.bpFile @@ -742,13 +817,13 @@ func generateBpContents(contents *generatedContents, bpFile *bpFile) { } func generateFilteredBpContents(contents *generatedContents, bpFile *bpFile, moduleFilter func(module *bpModule) bool) { - contents.Printfln("// This is auto-generated. DO NOT EDIT.") + contents.IndentedPrintf("// This is auto-generated. DO NOT EDIT.\n") for _, bpModule := range bpFile.order { if moduleFilter(bpModule) { - contents.Printfln("") - contents.Printfln("%s {", bpModule.moduleType) + contents.IndentedPrintf("\n") + contents.IndentedPrintf("%s {\n", bpModule.moduleType) outputPropertySet(contents, bpModule.bpPropertySet) - contents.Printfln("}") + contents.IndentedPrintf("}\n") } } } @@ -759,7 +834,7 @@ func outputPropertySet(contents *generatedContents, set *bpPropertySet) { addComment := func(name string) { if text, ok := set.comments[name]; ok { for _, line := range strings.Split(text, "\n") { - contents.Printfln("// %s", line) + contents.IndentedPrintf("// %s\n", line) } } } @@ -776,29 +851,8 @@ func outputPropertySet(contents *generatedContents, set *bpPropertySet) { } addComment(name) - switch v := value.(type) { - case []string: - length := len(v) - if length > 1 { - contents.Printfln("%s: [", name) - contents.Indent() - for i := 0; i < length; i = i + 1 { - contents.Printfln("%q,", v[i]) - } - contents.Dedent() - contents.Printfln("],") - } else if length == 0 { - contents.Printfln("%s: [],", name) - } else { - contents.Printfln("%s: [%q],", name, v[0]) - } - - case bool: - contents.Printfln("%s: %t,", name, v) - - default: - contents.Printfln("%s: %q,", name, value) - } + reflectValue := reflect.ValueOf(value) + outputNamedValue(contents, name, reflectValue) } for _, name := range set.order { @@ -808,15 +862,94 @@ func outputPropertySet(contents *generatedContents, set *bpPropertySet) { switch v := value.(type) { case *bpPropertySet: addComment(name) - contents.Printfln("%s: {", name) + contents.IndentedPrintf("%s: {\n", name) outputPropertySet(contents, v) - contents.Printfln("},") + contents.IndentedPrintf("},\n") } } contents.Dedent() } +// outputNamedValue outputs a value that has an associated name. The name will be indented, followed +// by the value and then followed by a , and a newline. +func outputNamedValue(contents *generatedContents, name string, value reflect.Value) { + contents.IndentedPrintf("%s: ", name) + outputUnnamedValue(contents, value) + contents.UnindentedPrintf(",\n") +} + +// outputUnnamedValue outputs a single value. The value is not indented and is not followed by +// either a , or a newline. With multi-line values, e.g. slices, all but the first line will be +// indented and all but the last line will end with a newline. +func outputUnnamedValue(contents *generatedContents, value reflect.Value) { + valueType := value.Type() + switch valueType.Kind() { + case reflect.Bool: + contents.UnindentedPrintf("%t", value.Bool()) + + case reflect.String: + contents.UnindentedPrintf("%q", value) + + case reflect.Ptr: + outputUnnamedValue(contents, value.Elem()) + + case reflect.Slice: + length := value.Len() + if length == 0 { + contents.UnindentedPrintf("[]") + } else { + firstValue := value.Index(0) + if length == 1 && !multiLineValue(firstValue) { + contents.UnindentedPrintf("[") + outputUnnamedValue(contents, firstValue) + contents.UnindentedPrintf("]") + } else { + contents.UnindentedPrintf("[\n") + contents.Indent() + for i := 0; i < length; i++ { + itemValue := value.Index(i) + contents.IndentedPrintf("") + outputUnnamedValue(contents, itemValue) + contents.UnindentedPrintf(",\n") + } + contents.Dedent() + contents.IndentedPrintf("]") + } + } + + case reflect.Struct: + // Avoid unlimited recursion by requiring every structure to implement android.BpPrintable. + v := value.Interface() + if _, ok := v.(android.BpPrintable); !ok { + panic(fmt.Errorf("property value %#v of type %T does not implement android.BpPrintable", v, v)) + } + contents.UnindentedPrintf("{\n") + contents.Indent() + for f := 0; f < valueType.NumField(); f++ { + fieldType := valueType.Field(f) + if fieldType.Anonymous { + continue + } + fieldValue := value.Field(f) + fieldName := fieldType.Name + propertyName := proptools.PropertyNameForField(fieldName) + outputNamedValue(contents, propertyName, fieldValue) + } + contents.Dedent() + contents.IndentedPrintf("}") + + default: + panic(fmt.Errorf("Unknown type: %T of value %#v", value, value)) + } +} + +// multiLineValue returns true if the supplied value may require multiple lines in the output. +func multiLineValue(value reflect.Value) bool { + kind := value.Kind() + return kind == reflect.Slice || kind == reflect.Struct +} + func (s *sdk) GetAndroidBpContentsForTests() string { contents := &generatedContents{} generateBpContents(contents, s.builderForTests.bpFile) @@ -1086,9 +1219,18 @@ func addSdkMemberPropertiesToSet(ctx *memberContext, memberProperties android.Sd type sdkMemberVariantDep struct { // The sdk variant that depends (possibly indirectly) on the member variant. sdkVariant *sdk + + // The type of sdk member the variant is to be treated as. memberType android.SdkMemberType - variant android.SdkAware - export bool + + // The variant that is added to the sdk. + variant android.SdkAware + + // True if the member should be exported, i.e. accessible, from outside the sdk. + export bool + + // The names of additional component modules provided by the variant. + exportedComponentsInfo android.ExportedComponentsInfo } var _ android.SdkMember = (*sdkMember)(nil) diff --git a/ui/build/build.go b/ui/build/build.go index 8f050d9be..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")) @@ -274,6 +278,11 @@ func Build(ctx Context, config Config) { // Return early, if we're using Soong as solely the generator of BUILD files. return } + + if config.bazelBuildMode() == generateJsonModuleGraph { + // Return early, if we're using Soong as solely the generator of the JSON module graph + return + } } if what&RunKati != 0 { diff --git a/ui/build/config.go b/ui/build/config.go index b9aaaf84e..480672171 100644 --- a/ui/build/config.go +++ b/ui/build/config.go @@ -108,6 +108,9 @@ const ( // Only generate build files (in a subdirectory of the out directory) and exit. generateBuildFiles + // Only generate the Soong json module graph for use with jq, and exit. + generateJsonModuleGraph + // Generate synthetic build files and incorporate these files into a build which // partially uses Bazel. Build metadata may come from Android.bp or BUILD files. mixedBuild @@ -936,6 +939,8 @@ func (c *configImpl) bazelBuildMode() bazelBuildMode { return mixedBuild } else if c.Environment().IsEnvTrue("GENERATE_BAZEL_FILES") { return generateBuildFiles + } else if v, ok := c.Environment().Get("SOONG_DUMP_JSON_MODULE_GRAPH"); ok && v != "" { + return generateJsonModuleGraph } else { return noBazel } diff --git a/ui/build/soong.go b/ui/build/soong.go index 19a47ae7c..a40457fc6 100644 --- a/ui/build/soong.go +++ b/ui/build/soong.go @@ -333,8 +333,9 @@ func runSoong(ctx Context, config Config) { } func shouldCollectBuildSoongMetrics(config Config) bool { - // Do not collect metrics protobuf if the soong_build binary ran as the bp2build converter. - return config.bazelBuildMode() != generateBuildFiles + // Do not collect metrics protobuf if the soong_build binary ran as the + // bp2build converter or the JSON graph dump. + return config.bazelBuildMode() != generateBuildFiles && config.bazelBuildMode() != generateJsonModuleGraph } func loadSoongBuildMetrics(ctx Context, config Config) *soong_metrics_proto.SoongBuildMetrics { |