diff options
214 files changed, 6819 insertions, 9133 deletions
diff --git a/Android.bp b/Android.bp index 432c7fc4f..ab8e4a086 100644 --- a/Android.bp +++ b/Android.bp @@ -1,5 +1,9 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], + default_visibility: [ + "//build/soong:__subpackages__", + ], + default_team: "trendy_team_build", } subdirs = [ @@ -23,6 +27,8 @@ bootstrap_go_package { srcs: [ "doc.go", ], + // Used by plugins, though probably shouldn't be. + visibility: ["//visibility:public"], } // @@ -40,6 +46,7 @@ cc_defaults { enabled: true, }, }, + defaults_visibility: ["//visibility:public"], } // @@ -51,6 +58,7 @@ kernel_headers { vendor: true, recovery_available: true, min_sdk_version: "apex_inherit", + visibility: ["//visibility:public"], } cc_genrule { @@ -75,6 +83,7 @@ cc_genrule { cmd: "$(location) -s $(out) $(in)", srcs: [":linker"], out: ["linker.s"], + visibility: ["//bionic/libc"], } cc_genrule { @@ -99,11 +108,18 @@ cc_genrule { cmd: "$(location) -T $(out) $(in)", srcs: [":linker"], out: ["linker.script"], + visibility: ["//visibility:public"], } // Instantiate the dex_bootjars singleton module. dex_bootjars { name: "dex_bootjars", + visibility: ["//visibility:public"], +} + +art_boot_images { + name: "art_boot_images", + visibility: ["//art:__subpackages__"], } // Pseudo-test that's run on checkbuilds to ensure that get_clang_version can @@ -123,11 +139,16 @@ dexpreopt_systemserver_check { // container for apex_contributions selected using build flags all_apex_contributions { name: "all_apex_contributions", + visibility: ["//visibility:public"], } +// TODO(b/365670526): remove the cuttlefish visibility once it is fully removed product_config { name: "product_config", - visibility: ["//device/google/cuttlefish/system_image"], + visibility: [ + "//build/make/target/product/generic", + "//device/google/cuttlefish/system_image", + ], } build_prop { @@ -136,6 +157,7 @@ build_prop { product_config: ":product_config", // Currently, only microdroid and cf system image can refer to system-build.prop visibility: [ + "//build/make/target/product/generic", "//device/google/cuttlefish/system_image", "//packages/modules/Virtualization/build/microdroid", ], diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go index 250fd56d1..d9a862c02 100644 --- a/aconfig/aconfig_declarations.go +++ b/aconfig/aconfig_declarations.go @@ -88,6 +88,13 @@ func (module *DeclarationsModule) DepsMutator(ctx android.BottomUpMutatorContext ctx.PropertyErrorf("container", "missing container property") } + // treating system_ext as system partition as we are combining them as one container + // TODO remove this logic once we start enforcing that system_ext cannot be specified as + // container in the container field. + if module.properties.Container == "system_ext" { + module.properties.Container = "system" + } + // Add a dependency on the aconfig_value_sets defined in // RELEASE_ACONFIG_VALUE_SETS, and add any aconfig_values that // match our package. diff --git a/aconfig/aconfig_value_set.go b/aconfig/aconfig_value_set.go index 7ba76c044..d72ec48ff 100644 --- a/aconfig/aconfig_value_set.go +++ b/aconfig/aconfig_value_set.go @@ -16,6 +16,9 @@ package aconfig import ( "android/soong/android" + "fmt" + "strings" + "github.com/google/blueprint" ) @@ -27,6 +30,9 @@ type ValueSetModule struct { properties struct { // aconfig_values modules Values []string + + // Paths to the Android.bp files where the aconfig_values modules are defined. + Srcs []string } } @@ -56,7 +62,35 @@ type valueSetProviderData struct { var valueSetProviderKey = blueprint.NewProvider[valueSetProviderData]() +func (module *ValueSetModule) FindAconfigValuesFromSrc(ctx android.BottomUpMutatorContext) map[string]android.Path { + moduleDir := ctx.ModuleDir() + srcs := android.PathsForModuleSrcExcludes(ctx, module.properties.Srcs, []string{ctx.BlueprintsFile()}) + + aconfigValuesPrefix := strings.Replace(module.Name(), "aconfig_value_set", "aconfig-values", 1) + moduleNamesSrcMap := make(map[string]android.Path) + for _, src := range srcs { + subDir := strings.TrimPrefix(src.String(), moduleDir+"/") + packageName, _, found := strings.Cut(subDir, "/") + if found { + moduleName := fmt.Sprintf("%s-%s-all", aconfigValuesPrefix, packageName) + moduleNamesSrcMap[moduleName] = src + } + } + return moduleNamesSrcMap +} + func (module *ValueSetModule) DepsMutator(ctx android.BottomUpMutatorContext) { + + // TODO: b/366285733 - Replace the file path based solution with more robust solution. + aconfigValuesMap := module.FindAconfigValuesFromSrc(ctx) + for _, moduleName := range android.SortedKeys(aconfigValuesMap) { + if ctx.OtherModuleExists(moduleName) { + ctx.AddDependency(ctx.Module(), valueSetTag, moduleName) + } else { + ctx.ModuleErrorf("module %q not found. Rename the aconfig_values module defined in %q to %q", moduleName, aconfigValuesMap[moduleName], moduleName) + } + } + deps := ctx.AddDependency(ctx.Module(), valueSetTag, module.properties.Values...) for _, dep := range deps { _, ok := dep.(*ValuesModule) diff --git a/aconfig/aconfig_value_set_test.go b/aconfig/aconfig_value_set_test.go index 32c31cb32..3b7281ec9 100644 --- a/aconfig/aconfig_value_set_test.go +++ b/aconfig/aconfig_value_set_test.go @@ -18,6 +18,8 @@ import ( "testing" "android/soong/android" + + "github.com/google/blueprint" ) func TestAconfigValueSet(t *testing.T) { @@ -41,3 +43,112 @@ func TestAconfigValueSet(t *testing.T) { depData, _ := android.OtherModuleProvider(result, module, valueSetProviderKey) android.AssertStringEquals(t, "AvailablePackages", "blah.aconfig_values", depData.AvailablePackages["foo.package"][0].String()) } + +func TestAconfigValueSetBpGlob(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithAconfigBuildComponents, + android.FixtureMergeMockFs( + map[string][]byte{ + // .../some_release/android.foo/ + "some_release/android.foo/Android.bp": []byte(` + aconfig_values { + name: "aconfig-values-platform_build_release-some_release-android.foo-all", + package: "android.foo", + srcs: [ + "*.textproto", + ], + } + `), + "some_release/android.foo/flag.textproto": nil, + + // .../some_release/android.bar/ + "some_release/android.bar/Android.bp": []byte(` + aconfig_values { + name: "aconfig-values-platform_build_release-some_release-android.bar-all", + package: "android.bar", + srcs: [ + "*.textproto", + ], + } + `), + "some_release/android.bar/flag.textproto": nil, + + // .../some_release/ + "some_release/Android.bp": []byte(` + aconfig_value_set { + name: "aconfig_value_set-platform_build_release-some_release", + srcs: [ + "*/Android.bp", + ], + } + `), + }, + ), + ).RunTest(t) + + checkModuleHasDependency := func(name, variant, dep string) bool { + t.Helper() + module := result.ModuleForTests(name, variant).Module() + depFound := false + result.VisitDirectDeps(module, func(m blueprint.Module) { + if m.Name() == dep { + depFound = true + } + }) + return depFound + } + android.AssertBoolEquals(t, + "aconfig_value_set expected to depend on aconfig_value via srcs", + true, + checkModuleHasDependency( + "aconfig_value_set-platform_build_release-some_release", + "", + "aconfig-values-platform_build_release-some_release-android.foo-all", + ), + ) + android.AssertBoolEquals(t, + "aconfig_value_set expected to depend on aconfig_value via srcs", + true, + checkModuleHasDependency( + "aconfig_value_set-platform_build_release-some_release", + "", + "aconfig-values-platform_build_release-some_release-android.bar-all", + ), + ) +} + +func TestAconfigValueSetBpGlobError(t *testing.T) { + android.GroupFixturePreparers( + PrepareForTestWithAconfigBuildComponents, + android.FixtureMergeMockFs( + map[string][]byte{ + // .../some_release/android.bar/ + "some_release/android.bar/Android.bp": []byte(` + aconfig_values { + name: "aconfig-values-platform_build_release-some_release-android_bar-all", + package: "android.bar", + srcs: [ + "*.textproto", + ], + } + `), + "some_release/android.bar/flag.textproto": nil, + + // .../some_release/ + "some_release/Android.bp": []byte(` + aconfig_value_set { + name: "aconfig_value_set-platform_build_release-some_release", + srcs: [ + "*/Android.bp", + ], + } + `), + }, + ), + ).ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern( + `module "aconfig_value_set-platform_build_release-some_release": module ` + + `"aconfig-values-platform_build_release-some_release-android.bar-all" not found. ` + + `Rename the aconfig_values module defined in "some_release/android.bar/Android.bp" ` + + `to "aconfig-values-platform_build_release-some_release-android.bar-all"`), + ).RunTest(t) +} diff --git a/aconfig/codegen/cc_aconfig_library.go b/aconfig/codegen/cc_aconfig_library.go index b5cf68798..8c4bfe696 100644 --- a/aconfig/codegen/cc_aconfig_library.go +++ b/aconfig/codegen/cc_aconfig_library.go @@ -63,7 +63,7 @@ func CcAconfigLibraryFactory() android.Module { callbacks := &CcAconfigLibraryCallbacks{ properties: &CcAconfigLibraryProperties{}, } - return cc.GeneratedCcLibraryModuleFactory("cc_aconfig_library", callbacks) + return cc.GeneratedCcLibraryModuleFactory(callbacks) } func (this *CcAconfigLibraryCallbacks) GeneratorInit(ctx cc.BaseModuleContext) { diff --git a/aconfig/codegen/java_aconfig_library_test.go b/aconfig/codegen/java_aconfig_library_test.go index 87b54a47f..d8372f3c9 100644 --- a/aconfig/codegen/java_aconfig_library_test.go +++ b/aconfig/codegen/java_aconfig_library_test.go @@ -260,7 +260,7 @@ func TestMkEntriesMatchedContainer(t *testing.T) { aconfig_declarations { name: "my_aconfig_declarations_bar", package: "com.example.package.bar", - container: "system_ext", + container: "vendor", srcs: ["bar.aconfig"], } diff --git a/aidl_library/Android.bp b/aidl_library/Android.bp index ec2150427..07472a492 100644 --- a/aidl_library/Android.bp +++ b/aidl_library/Android.bp @@ -29,4 +29,5 @@ bootstrap_go_package { "aidl_library_test.go", ], pluginFor: ["soong_build"], + visibility: ["//visibility:public"], } diff --git a/android/Android.bp b/android/Android.bp index 16a34b7de..c2bef0bfe 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -88,6 +88,7 @@ bootstrap_go_package { "prebuilt.go", "prebuilt_build_tool.go", "product_config.go", + "product_config_to_bp.go", "proto.go", "provider.go", "raw_files.go", @@ -105,7 +106,6 @@ bootstrap_go_package { "test_asserts.go", "test_suites.go", "testing.go", - "updatable_modules.go", "util.go", "variable.go", "vintf_fragment.go", @@ -154,4 +154,6 @@ bootstrap_go_package { "vintf_fragment_test.go", "visibility_test.go", ], + // Used by plugins + visibility: ["//visibility:public"], } diff --git a/android/aconfig_providers.go b/android/aconfig_providers.go index 6bfbf3776..b902f8be5 100644 --- a/android/aconfig_providers.go +++ b/android/aconfig_providers.go @@ -107,7 +107,7 @@ func aconfigUpdateAndroidBuildActions(ctx ModuleContext) { mergedAconfigFiles := make(map[string]Paths) mergedModeInfos := make(map[string]ModeInfo) - ctx.VisitDirectDepsIgnoreBlueprint(func(module Module) { + ctx.VisitDirectDeps(func(module Module) { if aconfig_dep, ok := OtherModuleProvider(ctx, module, CodegenInfoProvider); ok && len(aconfig_dep.ModeInfos) > 0 { maps.Copy(mergedModeInfos, aconfig_dep.ModeInfos) } @@ -187,6 +187,20 @@ func aconfigUpdateAndroidMkEntries(ctx fillInEntriesContext, mod Module, entries } } +func aconfigUpdateAndroidMkInfos(ctx fillInEntriesContext, mod Module, infos *AndroidMkProviderInfo) { + info, ok := OtherModuleProvider(ctx, mod, AconfigPropagatingProviderKey) + if !ok || len(info.AconfigFiles) == 0 { + return + } + // All of the files in the module potentially depend on the aconfig flag values. + infos.PrimaryInfo.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles)) + if len(infos.ExtraInfo) > 0 { + for _, ei := range (*infos).ExtraInfo { + ei.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles)) + } + } +} + func mergeAconfigFiles(ctx ModuleContext, container string, inputs Paths, generateRule bool) Paths { inputs = SortedUniquePaths(inputs) if len(inputs) == 1 { @@ -219,7 +233,8 @@ func getAconfigFilePaths(m *ModuleBase, aconfigFiles map[string]Paths) (paths Pa } else if m.ProductSpecific() { container = "product" } else if m.SystemExtSpecific() { - container = "system_ext" + // system_ext and system partitions should be treated as one container + container = "system" } paths = append(paths, aconfigFiles[container]...) diff --git a/android/androidmk.go b/android/androidmk.go index 6426835af..cac2cfe47 100644 --- a/android/androidmk.go +++ b/android/androidmk.go @@ -34,7 +34,6 @@ import ( "strings" "github.com/google/blueprint" - "github.com/google/blueprint/bootstrap" "github.com/google/blueprint/pathtools" "github.com/google/blueprint/proptools" ) @@ -502,6 +501,7 @@ type fillInEntriesContext interface { otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) ModuleType(module blueprint.Module) string OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{}) + HasMutatorFinished(mutatorName string) bool } func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) { @@ -806,15 +806,19 @@ func translateAndroidMkModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs // Additional cases here require review for correct license propagation to make. var err error - switch x := mod.(type) { - case AndroidMkDataProvider: - err = translateAndroidModule(ctx, w, moduleInfoJSONs, mod, x) - case bootstrap.GoBinaryTool: - err = translateGoBinaryModule(ctx, w, mod, x) - case AndroidMkEntriesProvider: - err = translateAndroidMkEntriesModule(ctx, w, moduleInfoJSONs, mod, x) - default: - // Not exported to make so no make variables to set. + + if info, ok := ctx.otherModuleProvider(mod, AndroidMkInfoProvider); ok { + androidMkEntriesInfos := info.(*AndroidMkProviderInfo) + err = translateAndroidMkEntriesInfoModule(ctx, w, moduleInfoJSONs, mod, androidMkEntriesInfos) + } else { + switch x := mod.(type) { + case AndroidMkDataProvider: + err = translateAndroidModule(ctx, w, moduleInfoJSONs, mod, x) + case AndroidMkEntriesProvider: + err = translateAndroidMkEntriesModule(ctx, w, moduleInfoJSONs, mod, x) + default: + // Not exported to make so no make variables to set. + } } if err != nil { @@ -824,23 +828,6 @@ func translateAndroidMkModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs return err } -// A simple, special Android.mk entry output func to make it possible to build blueprint tools using -// m by making them phony targets. -func translateGoBinaryModule(ctx SingletonContext, w io.Writer, mod blueprint.Module, - goBinary bootstrap.GoBinaryTool) error { - - name := ctx.ModuleName(mod) - fmt.Fprintln(w, ".PHONY:", name) - fmt.Fprintln(w, name+":", goBinary.InstallPath()) - fmt.Fprintln(w, "") - // Assuming no rules in make include go binaries in distributables. - // If the assumption is wrong, make will fail to build without the necessary .meta_lic and .meta_module files. - // In that case, add the targets and rules here to build a .meta_lic file for `name` and a .meta_module for - // `goBinary.InstallPath()` pointing to the `name`.meta_lic file. - - return nil -} - func (data *AndroidMkData) fillInData(ctx fillInEntriesContext, mod blueprint.Module) { // Get the preamble content through AndroidMkEntries logic. data.Entries = AndroidMkEntries{ @@ -913,6 +900,7 @@ func translateAndroidModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs * case "*android_sdk.sdkRepoHost": // doesn't go through base_rules case "*apex.apexBundle": // license properties written case "*bpf.bpf": // license properties written (both for module and objs) + case "*libbpf_prog.libbpfProg": // license properties written (both for module and objs) case "*genrule.Module": // writes non-custom before adding .phony case "*java.SystemModules": // doesn't go through base_rules case "*java.systemModulesImport": // doesn't go through base_rules @@ -982,11 +970,11 @@ func translateAndroidMkEntriesModule(ctx SingletonContext, w io.Writer, moduleIn return nil } -func ShouldSkipAndroidMkProcessing(ctx ConfigAndErrorContext, module Module) bool { +func ShouldSkipAndroidMkProcessing(ctx ConfigurableEvaluatorContext, module Module) bool { return shouldSkipAndroidMkProcessing(ctx, module.base()) } -func shouldSkipAndroidMkProcessing(ctx ConfigAndErrorContext, module *ModuleBase) bool { +func shouldSkipAndroidMkProcessing(ctx ConfigurableEvaluatorContext, module *ModuleBase) bool { if !module.commonProperties.NamespaceExportedToMake { // TODO(jeffrygaston) do we want to validate that there are no modules being // exported to Kati that depend on this module? @@ -1062,3 +1050,564 @@ func AndroidMkEmitAssignList(w io.Writer, varName string, lists ...[]string) { } fmt.Fprintln(w) } + +type AndroidMkProviderInfo struct { + PrimaryInfo AndroidMkInfo + ExtraInfo []AndroidMkInfo +} + +type AndroidMkInfo struct { + // Android.mk class string, e.g. EXECUTABLES, JAVA_LIBRARIES, ETC + Class string + // Optional suffix to append to the module name. Useful when a module wants to return multiple + // AndroidMkEntries objects. For example, when a java_library returns an additional entry for + // its hostdex sub-module, this SubName field is set to "-hostdex" so that it can have a + // different name than the parent's. + SubName string + // If set, this value overrides the base module name. SubName is still appended. + OverrideName string + // Dist files to output + DistFiles TaggedDistFiles + // The output file for Kati to process and/or install. If absent, the module is skipped. + OutputFile OptionalPath + // If true, the module is skipped and does not appear on the final Android-<product name>.mk + // file. Useful when a module needs to be skipped conditionally. + Disabled bool + // The postprocessing mk file to include, e.g. $(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk + // If not set, $(BUILD_SYSTEM)/prebuilt.mk is used. + Include string + // Required modules that need to be built and included in the final build output when building + // this module. + Required []string + // Required host modules that need to be built and included in the final build output when + // building this module. + Host_required []string + // Required device modules that need to be built and included in the final build output when + // building this module. + Target_required []string + + HeaderStrings []string + FooterStrings []string + + // A map that holds the up-to-date Make variable values. Can be accessed from tests. + EntryMap map[string][]string + // A list of EntryMap keys in insertion order. This serves a few purposes: + // 1. Prevents churns. Golang map doesn't provide consistent iteration order, so without this, + // the outputted Android-*.mk file may change even though there have been no content changes. + // 2. Allows modules to refer to other variables, like LOCAL_BAR_VAR := $(LOCAL_FOO_VAR), + // without worrying about the variables being mixed up in the actual mk file. + // 3. Makes troubleshooting and spotting errors easier. + EntryOrder []string +} + +// TODO: rename it to AndroidMkEntriesProvider after AndroidMkEntriesProvider interface is gone. +var AndroidMkInfoProvider = blueprint.NewProvider[*AndroidMkProviderInfo]() + +func translateAndroidMkEntriesInfoModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON, + mod blueprint.Module, providerInfo *AndroidMkProviderInfo) error { + if shouldSkipAndroidMkProcessing(ctx, mod.(Module).base()) { + return nil + } + + // Deep copy the provider info since we need to modify the info later + info := deepCopyAndroidMkProviderInfo(providerInfo) + + aconfigUpdateAndroidMkInfos(ctx, mod.(Module), &info) + + // Any new or special cases here need review to verify correct propagation of license information. + info.PrimaryInfo.fillInEntries(ctx, mod) + info.PrimaryInfo.write(w) + if len(info.ExtraInfo) > 0 { + for _, ei := range info.ExtraInfo { + ei.fillInEntries(ctx, mod) + ei.write(w) + } + } + + if !info.PrimaryInfo.disabled() { + if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok { + *moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON) + } + } + + return nil +} + +// Utility funcs to manipulate Android.mk variable entries. + +// SetString sets a Make variable with the given name to the given value. +func (a *AndroidMkInfo) SetString(name, value string) { + if _, ok := a.EntryMap[name]; !ok { + a.EntryOrder = append(a.EntryOrder, name) + } + a.EntryMap[name] = []string{value} +} + +// SetPath sets a Make variable with the given name to the given path string. +func (a *AndroidMkInfo) SetPath(name string, path Path) { + if _, ok := a.EntryMap[name]; !ok { + a.EntryOrder = append(a.EntryOrder, name) + } + a.EntryMap[name] = []string{path.String()} +} + +// SetOptionalPath sets a Make variable with the given name to the given path string if it is valid. +// It is a no-op if the given path is invalid. +func (a *AndroidMkInfo) SetOptionalPath(name string, path OptionalPath) { + if path.Valid() { + a.SetPath(name, path.Path()) + } +} + +// AddPath appends the given path string to a Make variable with the given name. +func (a *AndroidMkInfo) AddPath(name string, path Path) { + if _, ok := a.EntryMap[name]; !ok { + a.EntryOrder = append(a.EntryOrder, name) + } + a.EntryMap[name] = append(a.EntryMap[name], path.String()) +} + +// AddOptionalPath appends the given path string to a Make variable with the given name if it is +// valid. It is a no-op if the given path is invalid. +func (a *AndroidMkInfo) AddOptionalPath(name string, path OptionalPath) { + if path.Valid() { + a.AddPath(name, path.Path()) + } +} + +// SetPaths sets a Make variable with the given name to a slice of the given path strings. +func (a *AndroidMkInfo) SetPaths(name string, paths Paths) { + if _, ok := a.EntryMap[name]; !ok { + a.EntryOrder = append(a.EntryOrder, name) + } + a.EntryMap[name] = paths.Strings() +} + +// SetOptionalPaths sets a Make variable with the given name to a slice of the given path strings +// only if there are a non-zero amount of paths. +func (a *AndroidMkInfo) SetOptionalPaths(name string, paths Paths) { + if len(paths) > 0 { + a.SetPaths(name, paths) + } +} + +// AddPaths appends the given path strings to a Make variable with the given name. +func (a *AndroidMkInfo) AddPaths(name string, paths Paths) { + if _, ok := a.EntryMap[name]; !ok { + a.EntryOrder = append(a.EntryOrder, name) + } + a.EntryMap[name] = append(a.EntryMap[name], paths.Strings()...) +} + +// SetBoolIfTrue sets a Make variable with the given name to true if the given flag is true. +// It is a no-op if the given flag is false. +func (a *AndroidMkInfo) SetBoolIfTrue(name string, flag bool) { + if flag { + if _, ok := a.EntryMap[name]; !ok { + a.EntryOrder = append(a.EntryOrder, name) + } + a.EntryMap[name] = []string{"true"} + } +} + +// SetBool sets a Make variable with the given name to if the given bool flag value. +func (a *AndroidMkInfo) SetBool(name string, flag bool) { + if _, ok := a.EntryMap[name]; !ok { + a.EntryOrder = append(a.EntryOrder, name) + } + if flag { + a.EntryMap[name] = []string{"true"} + } else { + a.EntryMap[name] = []string{"false"} + } +} + +// AddStrings appends the given strings to a Make variable with the given name. +func (a *AndroidMkInfo) AddStrings(name string, value ...string) { + if len(value) == 0 { + return + } + if _, ok := a.EntryMap[name]; !ok { + a.EntryOrder = append(a.EntryOrder, name) + } + a.EntryMap[name] = append(a.EntryMap[name], value...) +} + +// AddCompatibilityTestSuites adds the supplied test suites to the EntryMap, with special handling +// for partial MTS and MCTS test suites. +func (a *AndroidMkInfo) AddCompatibilityTestSuites(suites ...string) { + // M(C)TS supports a full test suite and partial per-module MTS test suites, with naming mts-${MODULE}. + // To reduce repetition, if we find a partial M(C)TS test suite without an full M(C)TS test suite, + // we add the full test suite to our list. + if PrefixInList(suites, "mts-") && !InList("mts", suites) { + suites = append(suites, "mts") + } + if PrefixInList(suites, "mcts-") && !InList("mcts", suites) { + suites = append(suites, "mcts") + } + a.AddStrings("LOCAL_COMPATIBILITY_SUITE", suites...) +} + +func (a *AndroidMkInfo) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) { + helperInfo := AndroidMkInfo{ + EntryMap: make(map[string][]string), + } + + amod := mod.(Module) + base := amod.base() + name := base.BaseModuleName() + if a.OverrideName != "" { + name = a.OverrideName + } + + if a.Include == "" { + a.Include = "$(BUILD_PREBUILT)" + } + a.Required = append(a.Required, amod.RequiredModuleNames(ctx)...) + a.Required = append(a.Required, amod.VintfFragmentModuleNames(ctx)...) + a.Host_required = append(a.Host_required, amod.HostRequiredModuleNames()...) + a.Target_required = append(a.Target_required, amod.TargetRequiredModuleNames()...) + + for _, distString := range a.GetDistForGoals(ctx, mod) { + a.HeaderStrings = append(a.HeaderStrings, distString) + } + + a.HeaderStrings = append(a.HeaderStrings, fmt.Sprintf("\ninclude $(CLEAR_VARS) # type: %s, name: %s, variant: %s\n", ctx.ModuleType(mod), base.BaseModuleName(), ctx.ModuleSubDir(mod))) + + // Collect make variable assignment entries. + helperInfo.SetString("LOCAL_PATH", ctx.ModuleDir(mod)) + helperInfo.SetString("LOCAL_MODULE", name+a.SubName) + helperInfo.SetString("LOCAL_MODULE_CLASS", a.Class) + helperInfo.SetString("LOCAL_PREBUILT_MODULE_FILE", a.OutputFile.String()) + helperInfo.AddStrings("LOCAL_REQUIRED_MODULES", a.Required...) + helperInfo.AddStrings("LOCAL_HOST_REQUIRED_MODULES", a.Host_required...) + helperInfo.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", a.Target_required...) + helperInfo.AddStrings("LOCAL_SOONG_MODULE_TYPE", ctx.ModuleType(amod)) + + // If the install rule was generated by Soong tell Make about it. + info := OtherModuleProviderOrDefault(ctx, mod, InstallFilesProvider) + if len(info.KatiInstalls) > 0 { + // Assume the primary install file is last since it probably needs to depend on any other + // installed files. If that is not the case we can add a method to specify the primary + // installed file. + helperInfo.SetPath("LOCAL_SOONG_INSTALLED_MODULE", info.KatiInstalls[len(info.KatiInstalls)-1].to) + helperInfo.SetString("LOCAL_SOONG_INSTALL_PAIRS", info.KatiInstalls.BuiltInstalled()) + helperInfo.SetPaths("LOCAL_SOONG_INSTALL_SYMLINKS", info.KatiSymlinks.InstallPaths().Paths()) + } else { + // Soong may not have generated the install rule also when `no_full_install: true`. + // Mark this module as uninstallable in order to prevent Make from creating an + // install rule there. + helperInfo.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", proptools.Bool(base.commonProperties.No_full_install)) + } + + if len(info.TestData) > 0 { + helperInfo.AddStrings("LOCAL_TEST_DATA", androidMkDataPaths(info.TestData)...) + } + + if am, ok := mod.(ApexModule); ok { + helperInfo.SetBoolIfTrue("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", am.NotAvailableForPlatform()) + } + + archStr := base.Arch().ArchType.String() + host := false + switch base.Os().Class { + case Host: + if base.Target().HostCross { + // Make cannot identify LOCAL_MODULE_HOST_CROSS_ARCH:= common. + if base.Arch().ArchType != Common { + helperInfo.SetString("LOCAL_MODULE_HOST_CROSS_ARCH", archStr) + } + } else { + // Make cannot identify LOCAL_MODULE_HOST_ARCH:= common. + if base.Arch().ArchType != Common { + helperInfo.SetString("LOCAL_MODULE_HOST_ARCH", archStr) + } + } + host = true + case Device: + // Make cannot identify LOCAL_MODULE_TARGET_ARCH:= common. + if base.Arch().ArchType != Common { + if base.Target().NativeBridge { + hostArchStr := base.Target().NativeBridgeHostArchName + if hostArchStr != "" { + helperInfo.SetString("LOCAL_MODULE_TARGET_ARCH", hostArchStr) + } + } else { + helperInfo.SetString("LOCAL_MODULE_TARGET_ARCH", archStr) + } + } + + if !base.InVendorRamdisk() { + helperInfo.AddPaths("LOCAL_FULL_INIT_RC", info.InitRcPaths) + } + if len(info.VintfFragmentsPaths) > 0 { + helperInfo.AddPaths("LOCAL_FULL_VINTF_FRAGMENTS", info.VintfFragmentsPaths) + } + helperInfo.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(base.commonProperties.Proprietary)) + if Bool(base.commonProperties.Vendor) || Bool(base.commonProperties.Soc_specific) { + helperInfo.SetString("LOCAL_VENDOR_MODULE", "true") + } + helperInfo.SetBoolIfTrue("LOCAL_ODM_MODULE", Bool(base.commonProperties.Device_specific)) + helperInfo.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", Bool(base.commonProperties.Product_specific)) + helperInfo.SetBoolIfTrue("LOCAL_SYSTEM_EXT_MODULE", Bool(base.commonProperties.System_ext_specific)) + if base.commonProperties.Owner != nil { + helperInfo.SetString("LOCAL_MODULE_OWNER", *base.commonProperties.Owner) + } + } + + if host { + makeOs := base.Os().String() + if base.Os() == Linux || base.Os() == LinuxBionic || base.Os() == LinuxMusl { + makeOs = "linux" + } + helperInfo.SetString("LOCAL_MODULE_HOST_OS", makeOs) + helperInfo.SetString("LOCAL_IS_HOST_MODULE", "true") + } + + prefix := "" + if base.ArchSpecific() { + switch base.Os().Class { + case Host: + if base.Target().HostCross { + prefix = "HOST_CROSS_" + } else { + prefix = "HOST_" + } + case Device: + prefix = "TARGET_" + + } + + if base.Arch().ArchType != ctx.Config().Targets[base.Os()][0].Arch.ArchType { + prefix = "2ND_" + prefix + } + } + + if licenseMetadata, ok := OtherModuleProvider(ctx, mod, LicenseMetadataProvider); ok { + helperInfo.SetPath("LOCAL_SOONG_LICENSE_METADATA", licenseMetadata.LicenseMetadataPath) + } + + if _, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok { + helperInfo.SetBool("LOCAL_SOONG_MODULE_INFO_JSON", true) + } + + a.mergeEntries(&helperInfo) + + // Write to footer. + a.FooterStrings = append([]string{"include " + a.Include}, a.FooterStrings...) +} + +// This method merges the entries to helperInfo, then replaces a's EntryMap and +// EntryOrder with helperInfo's +func (a *AndroidMkInfo) mergeEntries(helperInfo *AndroidMkInfo) { + for _, extraEntry := range a.EntryOrder { + if v, ok := helperInfo.EntryMap[extraEntry]; ok { + v = append(v, a.EntryMap[extraEntry]...) + } else { + helperInfo.EntryMap[extraEntry] = a.EntryMap[extraEntry] + helperInfo.EntryOrder = append(helperInfo.EntryOrder, extraEntry) + } + } + a.EntryOrder = helperInfo.EntryOrder + a.EntryMap = helperInfo.EntryMap +} + +func (a *AndroidMkInfo) disabled() bool { + return a.Disabled || !a.OutputFile.Valid() +} + +// write flushes the AndroidMkEntries's in-struct data populated by AndroidMkEntries into the +// given Writer object. +func (a *AndroidMkInfo) write(w io.Writer) { + if a.disabled() { + return + } + + combinedHeaderString := strings.Join(a.HeaderStrings, "\n") + combinedFooterString := strings.Join(a.FooterStrings, "\n") + w.Write([]byte(combinedHeaderString)) + for _, name := range a.EntryOrder { + AndroidMkEmitAssignList(w, name, a.EntryMap[name]) + } + w.Write([]byte(combinedFooterString)) +} + +// Compute the list of Make strings to declare phony goals and dist-for-goals +// calls from the module's dist and dists properties. +func (a *AndroidMkInfo) GetDistForGoals(ctx fillInEntriesContext, mod blueprint.Module) []string { + distContributions := a.getDistContributions(ctx, mod) + if distContributions == nil { + return nil + } + + return generateDistContributionsForMake(distContributions) +} + +// Compute the contributions that the module makes to the dist. +func (a *AndroidMkInfo) getDistContributions(ctx fillInEntriesContext, mod blueprint.Module) *distContributions { + amod := mod.(Module).base() + name := amod.BaseModuleName() + + // Collate the set of associated tag/paths available for copying to the dist. + // Start with an empty (nil) set. + var availableTaggedDists TaggedDistFiles + + // Then merge in any that are provided explicitly by the module. + if a.DistFiles != nil { + // Merge the DistFiles into the set. + availableTaggedDists = availableTaggedDists.merge(a.DistFiles) + } + + // If no paths have been provided for the DefaultDistTag and the output file is + // valid then add that as the default dist path. + if _, ok := availableTaggedDists[DefaultDistTag]; !ok && a.OutputFile.Valid() { + availableTaggedDists = availableTaggedDists.addPathsForTag(DefaultDistTag, a.OutputFile.Path()) + } + + info := OtherModuleProviderOrDefault(ctx, mod, InstallFilesProvider) + // If the distFiles created by GenerateTaggedDistFiles contains paths for the + // DefaultDistTag then that takes priority so delete any existing paths. + if _, ok := info.DistFiles[DefaultDistTag]; ok { + delete(availableTaggedDists, DefaultDistTag) + } + + // Finally, merge the distFiles created by GenerateTaggedDistFiles. + availableTaggedDists = availableTaggedDists.merge(info.DistFiles) + + if len(availableTaggedDists) == 0 { + // Nothing dist-able for this module. + return nil + } + + // Collate the contributions this module makes to the dist. + distContributions := &distContributions{} + + if !exemptFromRequiredApplicableLicensesProperty(mod.(Module)) { + distContributions.licenseMetadataFile = info.LicenseMetadataFile + } + + // Iterate over this module's dist structs, merged from the dist and dists properties. + for _, dist := range amod.Dists() { + // Get the list of goals this dist should be enabled for. e.g. sdk, droidcore + goals := strings.Join(dist.Targets, " ") + + // Get the tag representing the output files to be dist'd. e.g. ".jar", ".proguard_map" + var tag string + if dist.Tag == nil { + // If the dist struct does not specify a tag, use the default output files tag. + tag = DefaultDistTag + } else { + tag = *dist.Tag + } + + // Get the paths of the output files to be dist'd, represented by the tag. + // Can be an empty list. + tagPaths := availableTaggedDists[tag] + if len(tagPaths) == 0 { + // Nothing to dist for this tag, continue to the next dist. + continue + } + + if len(tagPaths) > 1 && (dist.Dest != nil || dist.Suffix != nil) { + errorMessage := "%s: Cannot apply dest/suffix for more than one dist " + + "file for %q goals tag %q in module %s. The list of dist files, " + + "which should have a single element, is:\n%s" + panic(fmt.Errorf(errorMessage, mod, goals, tag, name, tagPaths)) + } + + copiesForGoals := distContributions.getCopiesForGoals(goals) + + // Iterate over each path adding a copy instruction to copiesForGoals + for _, path := range tagPaths { + // It's possible that the Path is nil from errant modules. Be defensive here. + if path == nil { + tagName := "default" // for error message readability + if dist.Tag != nil { + tagName = *dist.Tag + } + panic(fmt.Errorf("Dist file should not be nil for the %s tag in %s", tagName, name)) + } + + dest := filepath.Base(path.String()) + + if dist.Dest != nil { + var err error + if dest, err = validateSafePath(*dist.Dest); err != nil { + // This was checked in ModuleBase.GenerateBuildActions + panic(err) + } + } + + ext := filepath.Ext(dest) + suffix := "" + if dist.Suffix != nil { + suffix = *dist.Suffix + } + + productString := "" + if dist.Append_artifact_with_product != nil && *dist.Append_artifact_with_product { + productString = fmt.Sprintf("_%s", ctx.Config().DeviceProduct()) + } + + if suffix != "" || productString != "" { + dest = strings.TrimSuffix(dest, ext) + suffix + productString + ext + } + + if dist.Dir != nil { + var err error + if dest, err = validateSafePath(*dist.Dir, dest); err != nil { + // This was checked in ModuleBase.GenerateBuildActions + panic(err) + } + } + + copiesForGoals.addCopyInstruction(path, dest) + } + } + + return distContributions +} + +func deepCopyAndroidMkProviderInfo(providerInfo *AndroidMkProviderInfo) AndroidMkProviderInfo { + info := AndroidMkProviderInfo{ + PrimaryInfo: deepCopyAndroidMkInfo(&providerInfo.PrimaryInfo), + } + if len(providerInfo.ExtraInfo) > 0 { + for _, i := range providerInfo.ExtraInfo { + info.ExtraInfo = append(info.ExtraInfo, deepCopyAndroidMkInfo(&i)) + } + } + return info +} + +func deepCopyAndroidMkInfo(mkinfo *AndroidMkInfo) AndroidMkInfo { + info := AndroidMkInfo{ + Class: mkinfo.Class, + SubName: mkinfo.SubName, + OverrideName: mkinfo.OverrideName, + // There is no modification on DistFiles or OutputFile, so no need to + // make their deep copy. + DistFiles: mkinfo.DistFiles, + OutputFile: mkinfo.OutputFile, + Disabled: mkinfo.Disabled, + Include: mkinfo.Include, + Required: deepCopyStringSlice(mkinfo.Required), + Host_required: deepCopyStringSlice(mkinfo.Host_required), + Target_required: deepCopyStringSlice(mkinfo.Target_required), + HeaderStrings: deepCopyStringSlice(mkinfo.HeaderStrings), + FooterStrings: deepCopyStringSlice(mkinfo.FooterStrings), + EntryOrder: deepCopyStringSlice(mkinfo.EntryOrder), + } + info.EntryMap = make(map[string][]string) + for k, v := range mkinfo.EntryMap { + info.EntryMap[k] = deepCopyStringSlice(v) + } + + return info +} + +func deepCopyStringSlice(original []string) []string { + result := make([]string, len(original)) + copy(result, original) + return result +} diff --git a/android/apex.go b/android/apex.go index 114fe2988..79ab13caf 100644 --- a/android/apex.go +++ b/android/apex.go @@ -989,8 +989,8 @@ func (d *ApexBundleDepsInfo) BuildDepsInfoLists(ctx ModuleContext, minSdkVersion // Function called while walking an APEX's payload dependencies. // // Return true if the `to` module should be visited, false otherwise. -type PayloadDepsCallback func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool -type WalkPayloadDepsFunc func(ctx ModuleContext, do PayloadDepsCallback) +type PayloadDepsCallback func(ctx BaseModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool +type WalkPayloadDepsFunc func(ctx BaseModuleContext, do PayloadDepsCallback) // ModuleWithMinSdkVersionCheck represents a module that implements min_sdk_version checks type ModuleWithMinSdkVersionCheck interface { @@ -1017,7 +1017,7 @@ func CheckMinSdkVersion(ctx ModuleContext, minSdkVersion ApiLevel, walk WalkPayl return } - walk(ctx, func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool { + walk(ctx, func(ctx BaseModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool { if externalDep { // external deps are outside the payload boundary, which is "stable" // interface. We don't have to check min_sdk_version for external diff --git a/android/arch.go b/android/arch.go index 6d896e5fc..1ec971d15 100644 --- a/android/arch.go +++ b/android/arch.go @@ -23,7 +23,6 @@ import ( "strings" "github.com/google/blueprint" - "github.com/google/blueprint/bootstrap" "github.com/google/blueprint/proptools" ) @@ -139,6 +138,16 @@ func (a ArchType) String() string { return a.Name } +func (a ArchType) Bitness() string { + if a.Multilib == "lib32" { + return "32" + } + if a.Multilib == "lib64" { + return "64" + } + panic("Bitness is not defined for the common variant") +} + const COMMON_VARIANT = "common" var ( @@ -397,45 +406,21 @@ func (target Target) Variations() []blueprint.Variation { // device_supported and host_supported properties to determine which OsTypes are enabled for this // module, then searches through the Targets to determine which have enabled Targets for this // module. -func osMutator(bpctx blueprint.BottomUpMutatorContext) { - var module Module - var ok bool - if module, ok = bpctx.Module().(Module); !ok { - // The module is not a Soong module, it is a Blueprint module. - if bootstrap.IsBootstrapModule(bpctx.Module()) { - // Bootstrap Go modules are always the build OS or linux bionic. - config := bpctx.Config().(Config) - osNames := []string{config.BuildOSTarget.OsVariation()} - for _, hostCrossTarget := range config.Targets[LinuxBionic] { - if hostCrossTarget.Arch.ArchType == config.BuildOSTarget.Arch.ArchType { - osNames = append(osNames, hostCrossTarget.OsVariation()) - } - } - osNames = FirstUniqueStrings(osNames) - bpctx.CreateVariations(osNames...) - } - return - } +type osTransitionMutator struct{} - // Bootstrap Go module support above requires this mutator to be a - // blueprint.BottomUpMutatorContext because android.BottomUpMutatorContext - // filters out non-Soong modules. Now that we've handled them, create a - // normal android.BottomUpMutatorContext. - mctx := bottomUpMutatorContextFactory(bpctx, module, false) - defer bottomUpMutatorContextPool.Put(mctx) - - base := module.base() +type allOsInfo struct { + Os map[string]OsType + Variations []string +} - // Nothing to do for modules that are not architecture specific (e.g. a genrule). - if !base.ArchSpecific() { - return - } +var allOsProvider = blueprint.NewMutatorProvider[*allOsInfo]("os_propagate") - // Collect a list of OSTypes supported by this module based on the HostOrDevice value - // passed to InitAndroidArchModule and the device_supported and host_supported properties. +// moduleOSList collects a list of OSTypes supported by this module based on the HostOrDevice +// value passed to InitAndroidArchModule and the device_supported and host_supported properties. +func moduleOSList(ctx ConfigContext, base *ModuleBase) []OsType { var moduleOSList []OsType for _, os := range osTypeList { - for _, t := range mctx.Config().Targets[os] { + for _, t := range ctx.Config().Targets[os] { if base.supportsTarget(t) { moduleOSList = append(moduleOSList, os) break @@ -443,53 +428,91 @@ func osMutator(bpctx blueprint.BottomUpMutatorContext) { } } - createCommonOSVariant := base.commonProperties.CreateCommonOSVariant + if base.commonProperties.CreateCommonOSVariant { + // A CommonOS variant was requested so add it to the list of OS variants to + // create. It needs to be added to the end because it needs to depend on the + // the other variants and inter variant dependencies can only be created from a + // later variant in that list to an earlier one. That is because variants are + // always processed in the order in which they are created. + moduleOSList = append(moduleOSList, CommonOS) + } + + return moduleOSList +} + +func (o *osTransitionMutator) Split(ctx BaseModuleContext) []string { + module := ctx.Module() + base := module.base() + + // Nothing to do for modules that are not architecture specific (e.g. a genrule). + if !base.ArchSpecific() { + return []string{""} + } + + moduleOSList := moduleOSList(ctx, base) // If there are no supported OSes then disable the module. - if len(moduleOSList) == 0 && !createCommonOSVariant { + if len(moduleOSList) == 0 { base.Disable() - return + return []string{""} } // Convert the list of supported OsTypes to the variation names. osNames := make([]string, len(moduleOSList)) + osMapping := make(map[string]OsType, len(moduleOSList)) for i, os := range moduleOSList { osNames[i] = os.String() + osMapping[osNames[i]] = os } - if createCommonOSVariant { - // A CommonOS variant was requested so add it to the list of OS variants to - // create. It needs to be added to the end because it needs to depend on the - // the other variants in the list returned by CreateVariations(...) and inter - // variant dependencies can only be created from a later variant in that list to - // an earlier one. That is because variants are always processed in the order in - // which they are returned from CreateVariations(...). - osNames = append(osNames, CommonOS.Name) - moduleOSList = append(moduleOSList, CommonOS) + SetProvider(ctx, allOsProvider, &allOsInfo{ + Os: osMapping, + Variations: osNames, + }) + + return osNames +} + +func (o *osTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string { + return sourceVariation +} + +func (o *osTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string { + module := ctx.Module() + base := module.base() + + if !base.ArchSpecific() { + return "" } - // Create the variations, annotate each one with which OS it was created for, and - // squash the appropriate OS-specific properties into the top level properties. - modules := mctx.CreateVariations(osNames...) - for i, m := range modules { - m.base().commonProperties.CompileOS = moduleOSList[i] - m.base().setOSProperties(mctx) + return incomingVariation +} + +func (o *osTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) { + module := ctx.Module() + base := module.base() + + if variation == "" { + return + } + + allOsInfo, ok := ModuleProvider(ctx, allOsProvider) + if !ok { + panic(fmt.Errorf("missing allOsProvider")) } - if createCommonOSVariant { + // Annotate this variant with which OS it was created for, and + // squash the appropriate OS-specific properties into the top level properties. + base.commonProperties.CompileOS = allOsInfo.Os[variation] + base.setOSProperties(ctx) + + if variation == CommonOS.String() { // A CommonOS variant was requested so add dependencies from it (the last one in // the list) to the OS type specific variants. - last := len(modules) - 1 - commonOSVariant := modules[last] - commonOSVariant.base().commonProperties.CommonOSVariant = true - for _, module := range modules[0:last] { - // Ignore modules that are enabled. Note, this will only avoid adding - // dependencies on OsType variants that are explicitly disabled in their - // properties. The CommonOS variant will still depend on disabled variants - // if they are disabled afterwards, e.g. in archMutator if - if module.Enabled(mctx) { - mctx.AddInterVariantDependency(commonOsToOsSpecificVariantTag, commonOSVariant, module) - } + osList := allOsInfo.Variations[:len(allOsInfo.Variations)-1] + for _, os := range osList { + variation := []blueprint.Variation{{"os", os}} + ctx.AddVariationDependencies(variation, commonOsToOsSpecificVariantTag, ctx.ModuleName()) } } } @@ -522,7 +545,7 @@ func GetOsSpecificVariantsOfCommonOSVariant(mctx BaseModuleContext) []Module { var DarwinUniversalVariantTag = archDepTag{name: "darwin universal binary"} -// archMutator splits a module into a variant for each Target requested by the module. Target selection +// archTransitionMutator splits a module into a variant for each Target requested by the module. Target selection // for a module is in three levels, OsClass, multilib, and then Target. // OsClass selection is determined by: // - The HostOrDeviceSupported value passed in to InitAndroidArchModule by the module type factory, which selects @@ -553,41 +576,32 @@ var DarwinUniversalVariantTag = archDepTag{name: "darwin universal binary"} // // Modules can be initialized with InitAndroidMultiTargetsArchModule, in which case they will be split by OsClass, // but will have a common Target that is expected to handle all other selected Targets via ctx.MultiTargets(). -func archMutator(bpctx blueprint.BottomUpMutatorContext) { - var module Module - var ok bool - if module, ok = bpctx.Module().(Module); !ok { - if bootstrap.IsBootstrapModule(bpctx.Module()) { - // Bootstrap Go modules are always the build architecture. - bpctx.CreateVariations(bpctx.Config().(Config).BuildOSTarget.ArchVariation()) - } - return - } +type archTransitionMutator struct{} + +type allArchInfo struct { + Targets map[string]Target + MultiTargets []Target + Primary string + Multilib string +} - // Bootstrap Go module support above requires this mutator to be a - // blueprint.BottomUpMutatorContext because android.BottomUpMutatorContext - // filters out non-Soong modules. Now that we've handled them, create a - // normal android.BottomUpMutatorContext. - mctx := bottomUpMutatorContextFactory(bpctx, module, false) - defer bottomUpMutatorContextPool.Put(mctx) +var allArchProvider = blueprint.NewMutatorProvider[*allArchInfo]("arch_propagate") +func (a *archTransitionMutator) Split(ctx BaseModuleContext) []string { + module := ctx.Module() base := module.base() if !base.ArchSpecific() { - return + return []string{""} } os := base.commonProperties.CompileOS if os == CommonOS { - // Make sure that the target related properties are initialized for the - // CommonOS variant. - addTargetProperties(module, commonTargetMap[os.Name], nil, true) - // Do not create arch specific variants for the CommonOS variant. - return + return []string{""} } - osTargets := mctx.Config().Targets[os] + osTargets := ctx.Config().Targets[os] image := base.commonProperties.ImageVariation // Filter NativeBridge targets unless they are explicitly supported. @@ -614,19 +628,18 @@ func archMutator(bpctx blueprint.BottomUpMutatorContext) { prefer32 := os == Windows // Determine the multilib selection for this module. - ignorePrefer32OnDevice := mctx.Config().IgnorePrefer32OnDevice() - multilib, extraMultilib := decodeMultilib(base, os, ignorePrefer32OnDevice) + multilib, extraMultilib := decodeMultilib(ctx, base) // Convert the multilib selection into a list of Targets. targets, err := decodeMultilibTargets(multilib, osTargets, prefer32) if err != nil { - mctx.ModuleErrorf("%s", err.Error()) + ctx.ModuleErrorf("%s", err.Error()) } // If there are no supported targets disable the module. if len(targets) == 0 { base.Disable() - return + return []string{""} } // If the module is using extraMultilib, decode the extraMultilib selection into @@ -635,7 +648,7 @@ func archMutator(bpctx blueprint.BottomUpMutatorContext) { if extraMultilib != "" { multiTargets, err = decodeMultilibTargets(extraMultilib, osTargets, prefer32) if err != nil { - mctx.ModuleErrorf("%s", err.Error()) + ctx.ModuleErrorf("%s", err.Error()) } multiTargets = filterHostCross(multiTargets, targets[0].HostCross) } @@ -643,7 +656,7 @@ func archMutator(bpctx blueprint.BottomUpMutatorContext) { // Recovery is always the primary architecture, filter out any other architectures. // Common arch is also allowed if image == RecoveryVariation { - primaryArch := mctx.Config().DevicePrimaryArchType() + primaryArch := ctx.Config().DevicePrimaryArchType() targets = filterToArch(targets, primaryArch, Common) multiTargets = filterToArch(multiTargets, primaryArch, Common) } @@ -651,37 +664,109 @@ func archMutator(bpctx blueprint.BottomUpMutatorContext) { // If there are no supported targets disable the module. if len(targets) == 0 { base.Disable() - return + return []string{""} } // Convert the targets into a list of arch variation names. targetNames := make([]string, len(targets)) + targetMapping := make(map[string]Target, len(targets)) for i, target := range targets { targetNames[i] = target.ArchVariation() + targetMapping[targetNames[i]] = targets[i] } - // Create the variations, annotate each one with which Target it was created for, and - // squash the appropriate arch-specific properties into the top level properties. - modules := mctx.CreateVariations(targetNames...) - for i, m := range modules { - addTargetProperties(m, targets[i], multiTargets, i == 0) - m.base().setArchProperties(mctx) + SetProvider(ctx, allArchProvider, &allArchInfo{ + Targets: targetMapping, + MultiTargets: multiTargets, + Primary: targetNames[0], + Multilib: multilib, + }) + return targetNames +} - // Install support doesn't understand Darwin+Arm64 - if os == Darwin && targets[i].HostCross { - m.base().commonProperties.SkipInstall = true +func (a *archTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string { + return sourceVariation +} + +func (a *archTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string { + module := ctx.Module() + base := module.base() + + if !base.ArchSpecific() { + return "" + } + + os := base.commonProperties.CompileOS + if os == CommonOS { + // Do not create arch specific variants for the CommonOS variant. + return "" + } + + if incomingVariation == "" { + multilib, _ := decodeMultilib(ctx, base) + if multilib == "common" { + return "common" } } + return incomingVariation +} + +func (a *archTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) { + module := ctx.Module() + base := module.base() + os := base.commonProperties.CompileOS + + if os == CommonOS { + // Make sure that the target related properties are initialized for the + // CommonOS variant. + addTargetProperties(module, commonTargetMap[os.Name], nil, true) + return + } + + if variation == "" { + return + } + + if !base.ArchSpecific() { + panic(fmt.Errorf("found variation %q for non arch specifc module", variation)) + } + + allArchInfo, ok := ModuleProvider(ctx, allArchProvider) + if !ok { + return + } + + target, ok := allArchInfo.Targets[variation] + if !ok { + panic(fmt.Errorf("missing Target for %q", variation)) + } + primary := variation == allArchInfo.Primary + multiTargets := allArchInfo.MultiTargets + + // Annotate the new variant with which Target it was created for, and + // squash the appropriate arch-specific properties into the top level properties. + addTargetProperties(ctx.Module(), target, multiTargets, primary) + base.setArchProperties(ctx) + + // Install support doesn't understand Darwin+Arm64 + if os == Darwin && target.HostCross { + base.commonProperties.SkipInstall = true + } // Create a dependency for Darwin Universal binaries from the primary to secondary // architecture. The module itself will be responsible for calling lipo to merge the outputs. if os == Darwin { - if multilib == "darwin_universal" && len(modules) == 2 { - mctx.AddInterVariantDependency(DarwinUniversalVariantTag, modules[1], modules[0]) - } else if multilib == "darwin_universal_common_first" && len(modules) == 3 { - mctx.AddInterVariantDependency(DarwinUniversalVariantTag, modules[2], modules[1]) + isUniversalBinary := (allArchInfo.Multilib == "darwin_universal" && len(allArchInfo.Targets) == 2) || + allArchInfo.Multilib == "darwin_universal_common_first" && len(allArchInfo.Targets) == 3 + isPrimary := variation == ctx.Config().BuildArch.String() + hasSecondaryConfigured := len(ctx.Config().Targets[Darwin]) > 1 + if isUniversalBinary && isPrimary && hasSecondaryConfigured { + secondaryArch := ctx.Config().Targets[Darwin][1].Arch.String() + variation := []blueprint.Variation{{"arch", secondaryArch}} + ctx.AddVariationDependencies(variation, DarwinUniversalVariantTag, ctx.ModuleName()) } } + } // addTargetProperties annotates a variant with the Target is is being compiled for, the list @@ -698,7 +783,9 @@ func addTargetProperties(m Module, target Target, multiTargets []Target, primary // multilib from the factory's call to InitAndroidArchModule if none was set. For modules that // called InitAndroidMultiTargetsArchModule it always returns "common" for multilib, and returns // the actual multilib in extraMultilib. -func decodeMultilib(base *ModuleBase, os OsType, ignorePrefer32OnDevice bool) (multilib, extraMultilib string) { +func decodeMultilib(ctx ConfigContext, base *ModuleBase) (multilib, extraMultilib string) { + os := base.commonProperties.CompileOS + ignorePrefer32OnDevice := ctx.Config().IgnorePrefer32OnDevice() // First check the "android.compile_multilib" or "host.compile_multilib" properties. switch os.Class { case Device: diff --git a/android/arch_list.go b/android/arch_list.go index 2937092af..389f194e8 100644 --- a/android/arch_list.go +++ b/android/arch_list.go @@ -26,8 +26,10 @@ var archVariants = map[ArchType][]string{ "armv8-2a", "armv8-2a-dotprod", "armv9-a", + "armv9-2a", }, X86: { + "alderlake", "amberlake", "atom", "broadwell", @@ -52,6 +54,7 @@ var archVariants = map[ArchType][]string{ "x86_64", }, X86_64: { + "alderlake", "amberlake", "broadwell", "goldmont", @@ -109,9 +112,6 @@ var cpuVariants = map[ArchType][]string{ } var archFeatures = map[ArchType][]string{ - Arm: { - "neon", - }, Arm64: { "dotprod", }, @@ -141,17 +141,6 @@ var archFeatures = map[ArchType][]string{ } var androidArchFeatureMap = map[ArchType]map[string][]string{ - Arm: { - "armv7-a-neon": { - "neon", - }, - "armv8-a": { - "neon", - }, - "armv8-2a": { - "neon", - }, - }, Arm64: { "armv8-2a-dotprod": { "dotprod", @@ -164,6 +153,16 @@ var androidArchFeatureMap = map[ArchType]map[string][]string{ }, }, X86: { + "alderlake": { + "ssse3", + "sse4", + "sse4_1", + "sse4_2", + "avx", + "avx2", + "aes_ni", + "popcnt", + }, "amberlake": { "ssse3", "sse4", @@ -340,6 +339,16 @@ var androidArchFeatureMap = map[ArchType]map[string][]string{ "sse4_2", "popcnt", }, + "alderlake": { + "ssse3", + "sse4", + "sse4_1", + "sse4_2", + "avx", + "avx2", + "aes_ni", + "popcnt", + }, "amberlake": { "ssse3", "sse4", diff --git a/android/arch_test.go b/android/arch_test.go index 6134a065f..57c901032 100644 --- a/android/arch_test.go +++ b/android/arch_test.go @@ -451,7 +451,7 @@ func TestArchMutator(t *testing.T) { for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { - if tt.goOS != runtime.GOOS { + if tt.goOS != "" && tt.goOS != runtime.GOOS { t.Skipf("requries runtime.GOOS %s", tt.goOS) } diff --git a/android/base_module_context.go b/android/base_module_context.go index 550600052..c7d7573f1 100644 --- a/android/base_module_context.go +++ b/android/base_module_context.go @@ -113,31 +113,22 @@ type BaseModuleContext interface { // the first DependencyTag. GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) - // VisitDirectDepsBlueprint calls visit for each direct dependency. If there are multiple - // direct dependencies on the same module visit will be called multiple times on that module - // and OtherModuleDependencyTag will return a different tag for each. - // - // The Module passed to the visit function should not be retained outside of the visit - // function, it may be invalidated by future mutators. - VisitDirectDepsBlueprint(visit func(blueprint.Module)) - - // VisitDirectDepsIgnoreBlueprint calls visit for each direct dependency. If there are multiple + // VisitDirectDeps calls visit for each direct dependency. If there are multiple // direct dependencies on the same module visit will be called multiple times on that module - // and OtherModuleDependencyTag will return a different tag for each. It silently ignores any - // dependencies that are not an android.Module. + // and OtherModuleDependencyTag will return a different tag for each. It raises an error if any of the + // dependencies are disabled. // // The Module passed to the visit function should not be retained outside of the visit // function, it may be invalidated by future mutators. - VisitDirectDepsIgnoreBlueprint(visit func(Module)) + VisitDirectDeps(visit func(Module)) // VisitDirectDeps calls visit for each direct dependency. If there are multiple // direct dependencies on the same module visit will be called multiple times on that module - // and OtherModuleDependencyTag will return a different tag for each. It raises an error if any of the - // dependencies are not an android.Module. + // and OtherModuleDependencyTag will return a different tag for each. // // The Module passed to the visit function should not be retained outside of the visit // function, it may be invalidated by future mutators. - VisitDirectDeps(visit func(Module)) + VisitDirectDepsAllowDisabled(visit func(Module)) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) @@ -164,17 +155,6 @@ type BaseModuleContext interface { // invalidated by future mutators. WalkDeps(visit func(child, parent Module) bool) - // WalkDepsBlueprint calls visit for each transitive dependency, traversing the dependency - // tree in top down order. visit may be called multiple times for the same (child, parent) - // pair if there are multiple direct dependencies between the child and parent with different - // tags. OtherModuleDependencyTag will return the tag for the currently visited - // (child, parent) pair. If visit returns false WalkDeps will not continue recursing down - // to child. - // - // The Modules passed to the visit function should not be retained outside of the visit function, they may be - // invalidated by future mutators. - WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) - // GetWalkPath is supposed to be called in visit function passed in WalkDeps() // and returns a top-down dependency path from a start module to current child module. GetWalkPath() []Module @@ -311,7 +291,7 @@ func (t AlwaysAllowDisabledModuleDependencyTag) AllowDisabledModuleDependency(Mo return true } -func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool, ignoreBlueprint bool) Module { +func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool) Module { aModule, _ := module.(Module) if !strict { @@ -319,10 +299,7 @@ func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag b } if aModule == nil { - if !ignoreBlueprint { - b.ModuleErrorf("module %q (%#v) not an android module", b.OtherModuleName(module), tag) - } - return nil + panic(fmt.Errorf("module %q (%#v) not an android module", b.OtherModuleName(module), tag)) } if !aModule.Enabled(b) { @@ -345,15 +322,8 @@ type dep struct { func (b *baseModuleContext) getDirectDepsInternal(name string, tag blueprint.DependencyTag) []dep { var deps []dep - b.VisitDirectDepsBlueprint(func(module blueprint.Module) { - if aModule, _ := module.(Module); aModule != nil { - if aModule.base().BaseModuleName() == name { - returnedTag := b.bp.OtherModuleDependencyTag(aModule) - if tag == nil || returnedTag == tag { - deps = append(deps, dep{aModule, returnedTag}) - } - } - } else if b.bp.OtherModuleName(module) == name { + b.VisitDirectDeps(func(module Module) { + if module.base().BaseModuleName() == name { returnedTag := b.bp.OtherModuleDependencyTag(module) if tag == nil || returnedTag == tag { deps = append(deps, dep{module, returnedTag}) @@ -396,11 +366,9 @@ func (b *baseModuleContext) getDirectDepFirstTag(name string) (blueprint.Module, func (b *baseModuleContext) GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module { var deps []Module - b.VisitDirectDepsBlueprint(func(module blueprint.Module) { - if aModule, _ := module.(Module); aModule != nil { - if b.bp.OtherModuleDependencyTag(aModule) == tag { - deps = append(deps, aModule) - } + b.VisitDirectDeps(func(module Module) { + if b.bp.OtherModuleDependencyTag(module) == tag { + deps = append(deps, module) } }) return deps @@ -413,30 +381,24 @@ func (b *baseModuleContext) GetDirectDep(name string) (blueprint.Module, bluepri return b.getDirectDepFirstTag(name) } -func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module)) { - b.bp.VisitDirectDeps(visit) -} - func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) { - b.visitDirectDeps(visit, false) -} - -func (b *baseModuleContext) VisitDirectDepsIgnoreBlueprint(visit func(Module)) { - b.visitDirectDeps(visit, true) -} - -func (b *baseModuleContext) visitDirectDeps(visit func(Module), ignoreBlueprint bool) { b.bp.VisitDirectDeps(func(module blueprint.Module) { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, ignoreBlueprint); aModule != nil { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { visit(aModule) } }) } +func (b *baseModuleContext) VisitDirectDepsAllowDisabled(visit func(Module)) { + b.bp.VisitDirectDeps(func(module blueprint.Module) { + visit(module.(Module)) + }) +} + func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) { b.bp.VisitDirectDeps(func(module blueprint.Module) { if b.bp.OtherModuleDependencyTag(module) == tag { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { visit(aModule) } } @@ -447,7 +409,7 @@ func (b *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func b.bp.VisitDirectDepsIf( // pred func(module blueprint.Module) bool { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { return pred(aModule) } else { return false @@ -461,7 +423,7 @@ func (b *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func func (b *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) { b.bp.VisitDepsDepthFirst(func(module blueprint.Module) { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { visit(aModule) } }) @@ -471,7 +433,7 @@ func (b *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, visit b.bp.VisitDepsDepthFirstIf( // pred func(module blueprint.Module) bool { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps, false); aModule != nil { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { return pred(aModule) } else { return false @@ -483,10 +445,6 @@ func (b *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, visit }) } -func (b *baseModuleContext) WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) { - b.bp.WalkDeps(visit) -} - func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) { b.walkPath = []Module{b.Module()} b.tagPath = []blueprint.DependencyTag{} diff --git a/android/config.go b/android/config.go index b682c2e93..6dcdeef45 100644 --- a/android/config.go +++ b/android/config.go @@ -238,6 +238,11 @@ func (c Config) ReleaseAconfigFlagDefaultPermission() string { return c.config.productVariables.ReleaseAconfigFlagDefaultPermission } +// Enable object size sanitizer +func (c Config) ReleaseBuildObjectSizeSanitizer() bool { + return c.config.productVariables.GetBuildFlagBool("RELEASE_BUILD_OBJECT_SIZE_SANITIZER") +} + // The flag indicating behavior for the tree wrt building modules or using prebuilts // derived from RELEASE_DEFAULT_MODULE_BUILD_FROM_SOURCE func (c Config) ReleaseDefaultModuleBuildFromSource() bool { @@ -245,6 +250,13 @@ func (c Config) ReleaseDefaultModuleBuildFromSource() bool { Bool(c.config.productVariables.ReleaseDefaultModuleBuildFromSource) } +func (c Config) ReleaseDefaultUpdatableModuleVersion() string { + if val, exists := c.GetBuildFlag("RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION"); exists { + return val + } + panic("RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION is missing from build flags.") +} + func (c Config) ReleaseDisableVerifyOverlaps() bool { return c.config.productVariables.GetBuildFlagBool("RELEASE_DISABLE_VERIFY_OVERLAPS_CHECK") } @@ -1247,7 +1259,7 @@ func (c *config) TidyChecks() string { } func (c *config) LibartImgHostBaseAddress() string { - return "0x60000000" + return "0x70000000" } func (c *config) LibartImgDeviceBaseAddress() string { @@ -1273,6 +1285,7 @@ func (c *config) EnforceRROForModule(name string) bool { } return false } + func (c *config) EnforceRROExcludedOverlay(path string) bool { excluded := c.productVariables.EnforceRROExcludedOverlays if len(excluded) > 0 { @@ -1281,6 +1294,11 @@ func (c *config) EnforceRROExcludedOverlay(path string) bool { return false } +func (c *config) EnforceRROGlobally() bool { + enforceList := c.productVariables.EnforceRROTargets + return InList("*", enforceList) +} + func (c *config) ExportedNamespaces() []string { return append([]string(nil), c.productVariables.NamespacesToExport...) } @@ -1663,6 +1681,17 @@ func (c *config) ApexTrimEnabled() bool { return Bool(c.productVariables.TrimmedApex) } +func (c *config) UseSoongSystemImage() bool { + return Bool(c.productVariables.UseSoongSystemImage) +} + +func (c *config) SoongDefinedSystemImage() string { + if c.UseSoongSystemImage() { + return String(c.productVariables.ProductSoongDefinedSystemImage) + } + return "" +} + func (c *config) EnforceSystemCertificate() bool { return Bool(c.productVariables.EnforceSystemCertificate) } @@ -1675,14 +1704,6 @@ func (c *config) EnforceProductPartitionInterface() bool { return Bool(c.productVariables.EnforceProductPartitionInterface) } -func (c *config) EnforceInterPartitionJavaSdkLibrary() bool { - return Bool(c.productVariables.EnforceInterPartitionJavaSdkLibrary) -} - -func (c *config) InterPartitionJavaLibraryAllowList() []string { - return c.productVariables.InterPartitionJavaLibraryAllowList -} - func (c *config) ProductHiddenAPIStubs() []string { return c.productVariables.ProductHiddenAPIStubs } @@ -1954,6 +1975,10 @@ func (c *config) GetBuildFlag(name string) (string, bool) { return val, ok } +func (c *config) UseOptimizedResourceShrinkingByDefault() bool { + return c.productVariables.GetBuildFlagBool("RELEASE_USE_OPTIMIZED_RESOURCE_SHRINKING_BY_DEFAULT") +} + func (c *config) UseResourceProcessorByDefault() bool { return c.productVariables.GetBuildFlagBool("RELEASE_USE_RESOURCE_PROCESSOR_BY_DEFAULT") } @@ -2061,6 +2086,10 @@ func (c *config) OdmPropFiles(ctx PathContext) Paths { return PathsForSource(ctx, c.productVariables.OdmPropFiles) } +func (c *config) ExtraAllowedDepsTxt() string { + return String(c.productVariables.ExtraAllowedDepsTxt) +} + func (c *config) EnableUffdGc() string { return String(c.productVariables.EnableUffdGc) } diff --git a/android/container.go b/android/container.go index c048d6c73..2a3777b30 100644 --- a/android/container.go +++ b/android/container.go @@ -479,7 +479,7 @@ func setContainerInfo(ctx ModuleContext) { func checkContainerViolations(ctx ModuleContext) { if _, ok := ctx.Module().(InstallableModule); ok { containersInfo, _ := getContainerModuleInfo(ctx, ctx.Module()) - ctx.VisitDirectDepsIgnoreBlueprint(func(dep Module) { + ctx.VisitDirectDeps(func(dep Module) { if !dep.Enabled(ctx) { return } diff --git a/android/deapexer.go b/android/deapexer.go index dcae3e46d..4049d2b2a 100644 --- a/android/deapexer.go +++ b/android/deapexer.go @@ -15,7 +15,6 @@ package android import ( - "fmt" "strings" "github.com/google/blueprint" @@ -109,10 +108,6 @@ func (i DeapexerInfo) GetExportedModuleNames() []string { return i.exportedModuleNames } -// Provider that can be used from within the `GenerateAndroidBuildActions` of a module that depends -// on a `deapexer` module to retrieve its `DeapexerInfo`. -var DeapexerProvider = blueprint.NewProvider[DeapexerInfo]() - // NewDeapexerInfo creates and initializes a DeapexerInfo that is suitable // for use with a prebuilt_apex module. // @@ -169,45 +164,6 @@ type RequiresFilesFromPrebuiltApexTag interface { RequiresFilesFromPrebuiltApex() } -// FindDeapexerProviderForModule searches through the direct dependencies of the current context -// module for a DeapexerTag dependency and returns its DeapexerInfo. If a single nonambiguous -// deapexer module isn't found then it returns it an error -// clients should check the value of error and call ctx.ModuleErrof if a non nil error is received -func FindDeapexerProviderForModule(ctx ModuleContext) (*DeapexerInfo, error) { - var di *DeapexerInfo - var err error - ctx.VisitDirectDepsWithTag(DeapexerTag, func(m Module) { - if err != nil { - // An err has been found. Do not visit further. - return - } - c, ok := OtherModuleProvider(ctx, m, DeapexerProvider) - if !ok { - ctx.ModuleErrorf("Expected all deps with DeapexerTag to have a DeapexerProvider, but module %q did not", m.Name()) - return - } - p := &c - if di != nil { - // If two DeapexerInfo providers have been found then check if they are - // equivalent. If they are then use the selected one, otherwise fail. - if selected := equivalentDeapexerInfoProviders(di, p); selected != nil { - di = selected - return - } - err = fmt.Errorf("Multiple installable prebuilt APEXes provide ambiguous deapexers: %s and %s", di.ApexModuleName(), p.ApexModuleName()) - } - di = p - }) - if err != nil { - return nil, err - } - if di != nil { - return di, nil - } - ai, _ := ModuleProvider(ctx, ApexInfoProvider) - return nil, fmt.Errorf("No prebuilt APEX provides a deapexer module for APEX variant %s", ai.ApexVariationName) -} - // removeCompressedApexSuffix removes the _compressed suffix from the name if present. func removeCompressedApexSuffix(name string) string { return strings.TrimSuffix(name, "_compressed") diff --git a/android/defaults.go b/android/defaults.go index 0d51d9d7c..3d06c69c9 100644 --- a/android/defaults.go +++ b/android/defaults.go @@ -69,7 +69,7 @@ type Defaultable interface { // Apply defaults from the supplied Defaults to the property structures supplied to // setProperties(...). - applyDefaults(TopDownMutatorContext, []Defaults) + applyDefaults(BottomUpMutatorContext, []Defaults) // Set the hook to be called after any defaults have been applied. // @@ -101,6 +101,7 @@ func InitDefaultableModule(module DefaultableModule) { // A restricted subset of context methods, similar to LoadHookContext. type DefaultableHookContext interface { EarlyModuleContext + OtherModuleProviderContext CreateModule(ModuleFactory, ...interface{}) Module AddMissingDependencies(missingDeps []string) @@ -209,7 +210,7 @@ func InitDefaultsModule(module DefaultsModule) { var _ Defaults = (*DefaultsModuleBase)(nil) -func (defaultable *DefaultableModuleBase) applyDefaults(ctx TopDownMutatorContext, +func (defaultable *DefaultableModuleBase) applyDefaults(ctx BottomUpMutatorContext, defaultsList []Defaults) { for _, defaults := range defaultsList { @@ -226,7 +227,7 @@ func (defaultable *DefaultableModuleBase) applyDefaults(ctx TopDownMutatorContex // Product variable properties need special handling, the type of the filtered product variable // property struct may not be identical between the defaults module and the defaultable module. // Use PrependMatchingProperties to apply whichever properties match. -func (defaultable *DefaultableModuleBase) applyDefaultVariableProperties(ctx TopDownMutatorContext, +func (defaultable *DefaultableModuleBase) applyDefaultVariableProperties(ctx BottomUpMutatorContext, defaults Defaults, defaultableProp interface{}) { if defaultableProp == nil { return @@ -254,7 +255,7 @@ func (defaultable *DefaultableModuleBase) applyDefaultVariableProperties(ctx Top } } -func (defaultable *DefaultableModuleBase) applyDefaultProperties(ctx TopDownMutatorContext, +func (defaultable *DefaultableModuleBase) applyDefaultProperties(ctx BottomUpMutatorContext, defaults Defaults, defaultableProp interface{}) { for _, def := range defaults.properties() { @@ -273,7 +274,7 @@ func (defaultable *DefaultableModuleBase) applyDefaultProperties(ctx TopDownMuta func RegisterDefaultsPreArchMutators(ctx RegisterMutatorsContext) { ctx.BottomUp("defaults_deps", defaultsDepsMutator).Parallel() - ctx.TopDown("defaults", defaultsMutator).Parallel() + ctx.BottomUp("defaults", defaultsMutator).Parallel() } func defaultsDepsMutator(ctx BottomUpMutatorContext) { @@ -282,8 +283,12 @@ func defaultsDepsMutator(ctx BottomUpMutatorContext) { } } -func defaultsMutator(ctx TopDownMutatorContext) { +func defaultsMutator(ctx BottomUpMutatorContext) { if defaultable, ok := ctx.Module().(Defaultable); ok { + if _, isDefaultsModule := ctx.Module().(Defaults); isDefaultsModule { + // Don't squash transitive defaults into defaults modules + return + } defaults := defaultable.defaults().Defaults if len(defaults) > 0 { var defaultsList []Defaults diff --git a/android/early_module_context.go b/android/early_module_context.go index 23f4c90a2..11de77146 100644 --- a/android/early_module_context.go +++ b/android/early_module_context.go @@ -93,6 +93,10 @@ type EarlyModuleContext interface { // Namespace returns the Namespace object provided by the NameInterface set by Context.SetNameInterface, or the // default SimpleNameInterface if Context.SetNameInterface was not called. Namespace() *Namespace + + // HasMutatorFinished returns true if the given mutator has finished running. + // It will panic if given an invalid mutator name. + HasMutatorFinished(mutatorName string) bool } // Deprecated: use EarlyModuleContext instead @@ -175,3 +179,7 @@ func (e *earlyModuleContext) Namespace() *Namespace { func (e *earlyModuleContext) OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{}) { e.EarlyModuleContext.OtherModulePropertyErrorf(module, property, fmt, args...) } + +func (e *earlyModuleContext) HasMutatorFinished(mutatorName string) bool { + return e.EarlyModuleContext.HasMutatorFinished(mutatorName) +} diff --git a/android/hooks.go b/android/hooks.go index 2ad3b5fa5..bd2fa5e75 100644 --- a/android/hooks.go +++ b/android/hooks.go @@ -95,10 +95,17 @@ func (l *loadHookContext) createModule(factory blueprint.ModuleFactory, name str type createModuleContext interface { Module() Module + HasMutatorFinished(mutatorName string) bool createModule(blueprint.ModuleFactory, string, ...interface{}) blueprint.Module } func createModule(ctx createModuleContext, factory ModuleFactory, ext string, props ...interface{}) Module { + if ctx.HasMutatorFinished("defaults") { + // Creating modules late is oftentimes problematic, because they don't have earlier + // mutators run on them. Prevent making modules after the defaults mutator has run. + panic("Cannot create a module after the defaults mutator has finished") + } + inherited := []interface{}{&ctx.Module().base().commonProperties} var typeName string diff --git a/android/init.go b/android/init.go index d5b486b10..b46229282 100644 --- a/android/init.go +++ b/android/init.go @@ -18,5 +18,6 @@ import "encoding/gob" func init() { gob.Register(ModuleOutPath{}) + gob.Register(PhonyPath{}) gob.Register(unstableInfo{}) } diff --git a/android/license_metadata.go b/android/license_metadata.go index 0ac975fa2..f92563862 100644 --- a/android/license_metadata.go +++ b/android/license_metadata.go @@ -63,11 +63,7 @@ func buildLicenseMetadata(ctx *moduleContext, licenseMetadataFile WritablePath) var allDepOutputFiles Paths var allDepMetadataDepSets []*DepSet[Path] - ctx.VisitDirectDepsBlueprint(func(bpdep blueprint.Module) { - dep, _ := bpdep.(Module) - if dep == nil { - return - } + ctx.VisitDirectDeps(func(dep Module) { if !dep.Enabled(ctx) { return } diff --git a/android/module.go b/android/module.go index 009b0dfb6..20caae2d8 100644 --- a/android/module.go +++ b/android/module.go @@ -58,7 +58,7 @@ type Module interface { base() *ModuleBase Disable() - Enabled(ctx ConfigAndErrorContext) bool + Enabled(ctx ConfigurableEvaluatorContext) bool Target() Target MultiTargets() []Target @@ -109,12 +109,12 @@ type Module interface { // Get information about the properties that can contain visibility rules. visibilityProperties() []visibilityProperty - RequiredModuleNames(ctx ConfigAndErrorContext) []string + RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string HostRequiredModuleNames() []string TargetRequiredModuleNames() []string - VintfFragmentModuleNames(ctx ConfigAndErrorContext) []string + VintfFragmentModuleNames(ctx ConfigurableEvaluatorContext) []string - ConfigurableEvaluator(ctx ConfigAndErrorContext) proptools.ConfigurableEvaluator + ConfigurableEvaluator(ctx ConfigurableEvaluatorContext) proptools.ConfigurableEvaluator } // Qualified id for a module @@ -381,7 +381,7 @@ type commonProperties struct { Native_bridge_supported *bool `android:"arch_variant"` // init.rc files to be installed if this module is installed - Init_rc []string `android:"arch_variant,path"` + Init_rc proptools.Configurable[[]string] `android:"arch_variant,path"` // VINTF manifest fragments to be installed if this module is installed Vintf_fragments proptools.Configurable[[]string] `android:"path"` @@ -443,12 +443,6 @@ type commonProperties struct { // Set at module initialization time by calling InitCommonOSAndroidMultiTargetsArchModule CreateCommonOSVariant bool `blueprint:"mutated"` - // If set to true then this variant is the CommonOS variant that has dependencies on its - // OsType specific variants. - // - // Set by osMutator. - CommonOSVariant bool `blueprint:"mutated"` - // When set to true, this module is not installed to the full install path (ex: under // out/target/product/<name>/<partition>). It can be installed only to the packaging // modules like android_filesystem. @@ -1011,6 +1005,14 @@ func addRequiredDeps(ctx BottomUpMutatorContext) { return } + // Do not create a dependency from common variant to arch variant for `common_first` modules + if multilib, _ := decodeMultilib(ctx, ctx.Module().base()); multilib == string(MultilibCommonFirst) { + commonVariant := ctx.Arch().ArchType.Multilib == "" + if bothInAndroid && commonVariant && InList(target.Arch.ArchType.Multilib, []string{"lib32", "lib64"}) { + return + } + } + variation := target.Variations() if ctx.OtherModuleFarDependencyVariantExists(variation, depName) { ctx.AddFarVariationDependencies(variation, RequiredDepTag, depName) @@ -1063,8 +1065,29 @@ var vintfDepTag = struct { }{} func addVintfFragmentDeps(ctx BottomUpMutatorContext) { + // Vintf manifests in the recovery partition will be ignored. + if !ctx.Device() || ctx.Module().InstallInRecovery() { + return + } + + deviceConfig := ctx.DeviceConfig() + mod := ctx.Module() - ctx.AddDependency(mod, vintfDepTag, mod.VintfFragmentModuleNames(ctx)...) + vintfModules := ctx.AddDependency(mod, vintfDepTag, mod.VintfFragmentModuleNames(ctx)...) + + modPartition := mod.PartitionTag(deviceConfig) + for _, vintf := range vintfModules { + if vintfModule, ok := vintf.(*vintfFragmentModule); ok { + vintfPartition := vintfModule.PartitionTag(deviceConfig) + if modPartition != vintfPartition { + ctx.ModuleErrorf("Module %q(%q) and Vintf_fragment %q(%q) are installed to different partitions.", + mod.Name(), modPartition, + vintfModule.Name(), vintfPartition) + } + } else { + ctx.ModuleErrorf("Only vintf_fragment type module should be listed in vintf_fragment_modules : %q", vintf.Name()) + } + } } // AddProperties "registers" the provided props @@ -1221,7 +1244,7 @@ func (m *ModuleBase) ArchSpecific() bool { // True if the current variant is a CommonOS variant, false otherwise. func (m *ModuleBase) IsCommonOSVariant() bool { - return m.commonProperties.CommonOSVariant + return m.commonProperties.CompileOS == CommonOS } // supportsTarget returns true if the given Target is supported by the current module. @@ -1339,13 +1362,21 @@ func (m *ModuleBase) PartitionTag(config DeviceConfig) string { return partition } -func (m *ModuleBase) Enabled(ctx ConfigAndErrorContext) bool { +func (m *ModuleBase) Enabled(ctx ConfigurableEvaluatorContext) bool { if m.commonProperties.ForcedDisabled { return false } return m.commonProperties.Enabled.GetOrDefault(m.ConfigurableEvaluator(ctx), !m.Os().DefaultDisabled) } +// Returns a copy of the enabled property, useful for passing it on to sub-modules +func (m *ModuleBase) EnabledProperty() proptools.Configurable[bool] { + if m.commonProperties.ForcedDisabled { + return proptools.NewSimpleConfigurable(false) + } + return m.commonProperties.Enabled.Clone() +} + func (m *ModuleBase) Disable() { m.commonProperties.ForcedDisabled = true } @@ -1536,7 +1567,7 @@ func (m *ModuleBase) InRecovery() bool { return m.base().commonProperties.ImageVariation == RecoveryVariation } -func (m *ModuleBase) RequiredModuleNames(ctx ConfigAndErrorContext) []string { +func (m *ModuleBase) RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string { return m.base().commonProperties.Required.GetOrDefault(m.ConfigurableEvaluator(ctx), nil) } @@ -1548,7 +1579,7 @@ func (m *ModuleBase) TargetRequiredModuleNames() []string { return m.base().commonProperties.Target_required } -func (m *ModuleBase) VintfFragmentModuleNames(ctx ConfigAndErrorContext) []string { +func (m *ModuleBase) VintfFragmentModuleNames(ctx ConfigurableEvaluatorContext) []string { return m.base().commonProperties.Vintf_fragment_modules.GetOrDefault(m.ConfigurableEvaluator(ctx), nil) } @@ -1838,10 +1869,8 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) if m.Enabled(ctx) { // ensure all direct android.Module deps are enabled - ctx.VisitDirectDepsBlueprint(func(bm blueprint.Module) { - if m, ok := bm.(Module); ok { - ctx.validateAndroidModule(bm, ctx.OtherModuleDependencyTag(m), ctx.baseModuleContext.strictVisitDeps, false) - } + ctx.VisitDirectDeps(func(m Module) { + ctx.validateAndroidModule(m, ctx.OtherModuleDependencyTag(m), ctx.baseModuleContext.strictVisitDeps) }) if m.Device() { @@ -1853,7 +1882,7 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) // so only a single rule is created for each init.rc or vintf fragment file. if !m.InVendorRamdisk() { - ctx.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc) + ctx.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc.GetOrDefault(ctx, nil)) rcDir := PathForModuleInstall(ctx, "etc", "init") for _, src := range ctx.initRcPaths { installedInitRc := rcDir.Join(ctx, src.Base()) @@ -2204,17 +2233,23 @@ func (m *ModuleBase) IsNativeBridgeSupported() bool { return proptools.Bool(m.commonProperties.Native_bridge_supported) } -type ConfigAndErrorContext interface { +type ConfigContext interface { + Config() Config +} + +type ConfigurableEvaluatorContext interface { + OtherModuleProviderContext Config() Config OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{}) + HasMutatorFinished(mutatorName string) bool } type configurationEvalutor struct { - ctx ConfigAndErrorContext + ctx ConfigurableEvaluatorContext m Module } -func (m *ModuleBase) ConfigurableEvaluator(ctx ConfigAndErrorContext) proptools.ConfigurableEvaluator { +func (m *ModuleBase) ConfigurableEvaluator(ctx ConfigurableEvaluatorContext) proptools.ConfigurableEvaluator { return configurationEvalutor{ ctx: ctx, m: m.module, @@ -2228,6 +2263,12 @@ func (e configurationEvalutor) PropertyErrorf(property string, fmt string, args func (e configurationEvalutor) EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue { ctx := e.ctx m := e.m + + if !ctx.HasMutatorFinished("defaults") { + ctx.OtherModulePropertyErrorf(m, property, "Cannot evaluate configurable property before the defaults mutator has run") + return proptools.ConfigurableValueUndefined() + } + switch condition.FunctionName() { case "release_flag": if condition.NumArgs() != 1 { @@ -2255,6 +2296,8 @@ func (e configurationEvalutor) EvaluateConfiguration(condition proptools.Configu } variable := condition.Arg(0) switch variable { + case "build_from_text_stub": + return proptools.ConfigurableValueBool(ctx.Config().BuildFromTextStub()) case "debuggable": return proptools.ConfigurableValueBool(ctx.Config().Debuggable()) case "use_debug_art": diff --git a/android/module_context.go b/android/module_context.go index 3889e40e8..2bf2a8f00 100644 --- a/android/module_context.go +++ b/android/module_context.go @@ -85,7 +85,9 @@ type ModuleBuildParams BuildParams type ModuleContext interface { BaseModuleContext - blueprintModuleContext() blueprint.ModuleContext + // BlueprintModuleContext returns the blueprint.ModuleContext that the ModuleContext wraps. It may only be + // used by the golang module types that need to call into the bootstrap module types. + BlueprintModuleContext() blueprint.ModuleContext // Deprecated: use ModuleContext.Build instead. ModuleBuild(pctx PackageContext, params ModuleBuildParams) @@ -194,7 +196,7 @@ type ModuleContext interface { InstallInVendor() bool InstallForceOS() (*OsType, *ArchType) - RequiredModuleNames(ctx ConfigAndErrorContext) []string + RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string HostRequiredModuleNames() []string TargetRequiredModuleNames() []string @@ -779,7 +781,7 @@ func (m *moduleContext) UncheckedModule() { m.uncheckedModule = true } -func (m *moduleContext) blueprintModuleContext() blueprint.ModuleContext { +func (m *moduleContext) BlueprintModuleContext() blueprint.ModuleContext { return m.bp } @@ -855,7 +857,7 @@ func (m *moduleContext) ExpandOptionalSource(srcFile *string, _ string) Optional return OptionalPath{} } -func (m *moduleContext) RequiredModuleNames(ctx ConfigAndErrorContext) []string { +func (m *moduleContext) RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string { return m.module.RequiredModuleNames(ctx) } diff --git a/android/module_test.go b/android/module_test.go index d64e3a591..d76d9b329 100644 --- a/android/module_test.go +++ b/android/module_test.go @@ -1080,3 +1080,29 @@ func TestOutputFileForModule(t *testing.T) { }) } } + +func TestVintfFragmentModulesChecksPartition(t *testing.T) { + bp := ` + vintf_fragment { + name: "vintfModA", + src: "test_vintf_file", + vendor: true, + } + deps { + name: "modA", + vintf_fragment_modules: [ + "vintfModA", + ] + } + ` + + testPreparer := GroupFixturePreparers( + PrepareForTestWithAndroidBuildComponents, + prepareForModuleTests, + ) + + testPreparer. + ExtendWithErrorHandler(FixtureExpectsOneErrorPattern( + "Module .+ and Vintf_fragment .+ are installed to different partitions.")). + RunTestWithBp(t, bp) +} diff --git a/android/mutator.go b/android/mutator.go index 38fb857dd..a8b5c7db6 100644 --- a/android/mutator.go +++ b/android/mutator.go @@ -26,7 +26,7 @@ import ( // run Pre-deps mutators // run depsMutator // run PostDeps mutators -// run FinalDeps mutators (CreateVariations disallowed in this phase) +// run FinalDeps mutators (TransitionMutators disallowed in this phase) // continue on to GenerateAndroidBuildActions // collateGloballyRegisteredMutators constructs the list of mutators that have been registered @@ -148,9 +148,9 @@ var preArch = []RegisterMutatorFunc{ } func registerArchMutator(ctx RegisterMutatorsContext) { - ctx.BottomUpBlueprint("os", osMutator).Parallel() + ctx.Transition("os", &osTransitionMutator{}) ctx.Transition("image", &imageTransitionMutator{}) - ctx.BottomUpBlueprint("arch", archMutator).Parallel() + ctx.Transition("arch", &archTransitionMutator{}) } var preDeps = []RegisterMutatorFunc{ @@ -193,16 +193,16 @@ type BaseMutatorContext interface { // Rename all variants of a module. The new name is not visible to calls to ModuleName, // AddDependency or OtherModuleName until after this mutator pass is complete. Rename(name string) + + // CreateModule creates a new module by calling the factory method for the specified moduleType, and applies + // the specified property structs to it as if the properties were set in a blueprint file. + CreateModule(ModuleFactory, ...interface{}) Module } type TopDownMutator func(TopDownMutatorContext) type TopDownMutatorContext interface { BaseMutatorContext - - // CreateModule creates a new module by calling the factory method for the specified moduleType, and applies - // the specified property structs to it as if the properties were set in a blueprint file. - CreateModule(ModuleFactory, ...interface{}) Module } type topDownMutatorContext struct { @@ -231,36 +231,6 @@ type BottomUpMutatorContext interface { // module's dependency list. AddReverseDependency(module blueprint.Module, tag blueprint.DependencyTag, name string) - // CreateVariations splits a module into multiple variants, one for each name in the variationNames - // parameter. It returns a list of new modules in the same order as the variationNames - // list. - // - // If any of the dependencies of the module being operated on were already split - // by calling CreateVariations with the same name, the dependency will automatically - // be updated to point the matching variant. - // - // If a module is split, and then a module depending on the first module is not split - // when the Mutator is later called on it, the dependency of the depending module will - // automatically be updated to point to the first variant. - CreateVariations(...string) []Module - - // CreateLocationVariations splits a module into multiple variants, one for each name in the variantNames - // parameter. It returns a list of new modules in the same order as the variantNames - // list. - // - // Local variations do not affect automatic dependency resolution - dependencies added - // to the split module via deps or DynamicDependerModule must exactly match a variant - // that contains all the non-local variations. - CreateLocalVariations(...string) []Module - - // SetDependencyVariation sets all dangling dependencies on the current module to point to the variation - // with given name. This function ignores the default variation set by SetDefaultDependencyVariation. - SetDependencyVariation(string) - - // SetDefaultDependencyVariation sets the default variation when a dangling reference is detected - // during the subsequent calls on Create*Variations* functions. To reset, set it to nil. - SetDefaultDependencyVariation(*string) - // AddVariationDependencies adds deps as dependencies of the current module, but uses the variations // argument to select which variant of the dependency to use. It returns a slice of modules for // each dependency (some entries may be nil). A variant of the dependency must exist that matches @@ -272,6 +242,16 @@ type BottomUpMutatorContext interface { // be ordered correctly for all future mutator passes. AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag, names ...string) []blueprint.Module + // AddReverseVariationDependencies adds a dependency from the named module to the current + // module. The given variations will be added to the current module's varations, and then the + // result will be used to find the correct variation of the depending module, which must exist. + // + // Does not affect the ordering of the current mutator pass, but will be ordered + // correctly for all future mutator passes. All reverse dependencies for a destination module are + // collected until the end of the mutator pass, sorted by name, and then appended to the destination + // module's dependency list. + AddReverseVariationDependency([]blueprint.Variation, blueprint.DependencyTag, string) + // AddFarVariationDependencies adds deps as dependencies of the current module, but uses the // variations argument to select which variant of the dependency to use. It returns a slice of // modules for each dependency (some entries may be nil). A variant of the dependency must @@ -287,12 +267,6 @@ type BottomUpMutatorContext interface { // be ordered correctly for all future mutator passes. AddFarVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string) []blueprint.Module - // AddInterVariantDependency adds a dependency between two variants of the same module. Variants are always - // ordered in the same orderas they were listed in CreateVariations, and AddInterVariantDependency does not change - // that ordering, but it associates a DependencyTag with the dependency and makes it visible to VisitDirectDeps, - // WalkDeps, etc. - AddInterVariantDependency(tag blueprint.DependencyTag, from, to blueprint.Module) - // ReplaceDependencies finds all the variants of the module with the specified name, then // replaces all dependencies onto those variants with the current variant of this module. // Replacements don't take effect until after the mutator pass is finished. @@ -303,30 +277,6 @@ type BottomUpMutatorContext interface { // as long as the supplied predicate returns true. // Replacements don't take effect until after the mutator pass is finished. ReplaceDependenciesIf(string, blueprint.ReplaceDependencyPredicate) - - // AliasVariation takes a variationName that was passed to CreateVariations for this module, - // and creates an alias from the current variant (before the mutator has run) to the new - // variant. The alias will be valid until the next time a mutator calls CreateVariations or - // CreateLocalVariations on this module without also calling AliasVariation. The alias can - // be used to add dependencies on the newly created variant using the variant map from - // before CreateVariations was run. - AliasVariation(variationName string) - - // CreateAliasVariation takes a toVariationName that was passed to CreateVariations for this - // module, and creates an alias from a new fromVariationName variant the toVariationName - // variant. The alias will be valid until the next time a mutator calls CreateVariations or - // CreateLocalVariations on this module without also calling AliasVariation. The alias can - // be used to add dependencies on the toVariationName variant using the fromVariationName - // variant. - CreateAliasVariation(fromVariationName, toVariationName string) - - // SetVariationProvider sets the value for a provider for the given newly created variant of - // the current module, i.e. one of the Modules returned by CreateVariations.. It panics if - // not called during the appropriate mutator or GenerateBuildActions pass for the provider, - // if the value is not of the appropriate type, or if the module is not a newly created - // variant of the current module. The value should not be modified after being passed to - // SetVariationProvider. - SetVariationProvider(module blueprint.Module, provider blueprint.AnyProviderKey, value interface{}) } // An outgoingTransitionContextImpl and incomingTransitionContextImpl is created for every dependency of every module @@ -516,6 +466,9 @@ type androidTransitionMutator struct { } func (a *androidTransitionMutator) Split(ctx blueprint.BaseModuleContext) []string { + if a.finalPhase { + panic("TransitionMutator not allowed in FinalDepsMutators") + } if m, ok := ctx.Module().(Module); ok { moduleContext := m.base().baseModuleContextFactory(ctx) return a.mutator.Split(&moduleContext) @@ -739,6 +692,14 @@ func (b *bottomUpMutatorContext) Rename(name string) { b.Module().base().commonProperties.DebugName = name } +func (b *bottomUpMutatorContext) createModule(factory blueprint.ModuleFactory, name string, props ...interface{}) blueprint.Module { + return b.bp.CreateModule(factory, name, props...) +} + +func (b *bottomUpMutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) Module { + return createModule(b, factory, "_bottomUpMutatorModule", props...) +} + func (b *bottomUpMutatorContext) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) []blueprint.Module { if b.baseModuleContext.checkedMissingDeps() { panic("Adding deps not allowed after checking for missing deps") @@ -753,48 +714,11 @@ func (b *bottomUpMutatorContext) AddReverseDependency(module blueprint.Module, t b.bp.AddReverseDependency(module, tag, name) } -func (b *bottomUpMutatorContext) CreateVariations(variations ...string) []Module { - if b.finalPhase { - panic("CreateVariations not allowed in FinalDepsMutators") - } - - modules := b.bp.CreateVariations(variations...) - - aModules := make([]Module, len(modules)) - for i := range variations { - aModules[i] = modules[i].(Module) - base := aModules[i].base() - base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, b.MutatorName()) - base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variations[i]) - } - - return aModules -} - -func (b *bottomUpMutatorContext) CreateLocalVariations(variations ...string) []Module { - if b.finalPhase { - panic("CreateLocalVariations not allowed in FinalDepsMutators") - } - - modules := b.bp.CreateLocalVariations(variations...) - - aModules := make([]Module, len(modules)) - for i := range variations { - aModules[i] = modules[i].(Module) - base := aModules[i].base() - base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, b.MutatorName()) - base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variations[i]) +func (b *bottomUpMutatorContext) AddReverseVariationDependency(variations []blueprint.Variation, tag blueprint.DependencyTag, name string) { + if b.baseModuleContext.checkedMissingDeps() { + panic("Adding deps not allowed after checking for missing deps") } - - return aModules -} - -func (b *bottomUpMutatorContext) SetDependencyVariation(variation string) { - b.bp.SetDependencyVariation(variation) -} - -func (b *bottomUpMutatorContext) SetDefaultDependencyVariation(variation *string) { - b.bp.SetDefaultDependencyVariation(variation) + b.bp.AddReverseVariationDependency(variations, tag, name) } func (b *bottomUpMutatorContext) AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag, @@ -814,10 +738,6 @@ func (b *bottomUpMutatorContext) AddFarVariationDependencies(variations []bluepr return b.bp.AddFarVariationDependencies(variations, tag, names...) } -func (b *bottomUpMutatorContext) AddInterVariantDependency(tag blueprint.DependencyTag, from, to blueprint.Module) { - b.bp.AddInterVariantDependency(tag, from, to) -} - func (b *bottomUpMutatorContext) ReplaceDependencies(name string) { if b.baseModuleContext.checkedMissingDeps() { panic("Adding deps not allowed after checking for missing deps") @@ -831,15 +751,3 @@ func (b *bottomUpMutatorContext) ReplaceDependenciesIf(name string, predicate bl } b.bp.ReplaceDependenciesIf(name, predicate) } - -func (b *bottomUpMutatorContext) AliasVariation(variationName string) { - b.bp.AliasVariation(variationName) -} - -func (b *bottomUpMutatorContext) CreateAliasVariation(fromVariationName, toVariationName string) { - b.bp.CreateAliasVariation(fromVariationName, toVariationName) -} - -func (b *bottomUpMutatorContext) SetVariationProvider(module blueprint.Module, provider blueprint.AnyProviderKey, value interface{}) { - b.bp.SetVariationProvider(module, provider, value) -} diff --git a/android/mutator_test.go b/android/mutator_test.go index 21eebd2c8..5d4074a54 100644 --- a/android/mutator_test.go +++ b/android/mutator_test.go @@ -81,6 +81,40 @@ func TestMutatorAddMissingDependencies(t *testing.T) { AssertDeepEquals(t, "foo missing deps", []string{"added_missing_dep", "regular_missing_dep"}, foo.missingDeps) } +type testTransitionMutator struct { + split func(ctx BaseModuleContext) []string + outgoingTransition func(ctx OutgoingTransitionContext, sourceVariation string) string + incomingTransition func(ctx IncomingTransitionContext, incomingVariation string) string + mutate func(ctx BottomUpMutatorContext, variation string) +} + +func (t *testTransitionMutator) Split(ctx BaseModuleContext) []string { + if t.split != nil { + return t.split(ctx) + } + return []string{""} +} + +func (t *testTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string { + if t.outgoingTransition != nil { + return t.outgoingTransition(ctx, sourceVariation) + } + return sourceVariation +} + +func (t *testTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string { + if t.incomingTransition != nil { + return t.incomingTransition(ctx, incomingVariation) + } + return incomingVariation +} + +func (t *testTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) { + if t.mutate != nil { + t.mutate(ctx, variation) + } +} + func TestModuleString(t *testing.T) { bp := ` test { @@ -94,9 +128,11 @@ func TestModuleString(t *testing.T) { FixtureRegisterWithContext(func(ctx RegistrationContext) { ctx.PreArchMutators(func(ctx RegisterMutatorsContext) { - ctx.BottomUp("pre_arch", func(ctx BottomUpMutatorContext) { - moduleStrings = append(moduleStrings, ctx.Module().String()) - ctx.CreateVariations("a", "b") + ctx.Transition("pre_arch", &testTransitionMutator{ + split: func(ctx BaseModuleContext) []string { + moduleStrings = append(moduleStrings, ctx.Module().String()) + return []string{"a", "b"} + }, }) ctx.TopDown("rename_top_down", func(ctx TopDownMutatorContext) { moduleStrings = append(moduleStrings, ctx.Module().String()) @@ -105,16 +141,23 @@ func TestModuleString(t *testing.T) { }) ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.BottomUp("pre_deps", func(ctx BottomUpMutatorContext) { - moduleStrings = append(moduleStrings, ctx.Module().String()) - ctx.CreateVariations("c", "d") + ctx.Transition("pre_deps", &testTransitionMutator{ + split: func(ctx BaseModuleContext) []string { + moduleStrings = append(moduleStrings, ctx.Module().String()) + return []string{"c", "d"} + }, }) }) ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.BottomUp("post_deps", func(ctx BottomUpMutatorContext) { - moduleStrings = append(moduleStrings, ctx.Module().String()) - ctx.CreateLocalVariations("e", "f") + ctx.Transition("post_deps", &testTransitionMutator{ + split: func(ctx BaseModuleContext) []string { + moduleStrings = append(moduleStrings, ctx.Module().String()) + return []string{"e", "f"} + }, + outgoingTransition: func(ctx OutgoingTransitionContext, sourceVariation string) string { + return "" + }, }) ctx.BottomUp("rename_bottom_up", func(ctx BottomUpMutatorContext) { moduleStrings = append(moduleStrings, ctx.Module().String()) @@ -138,15 +181,15 @@ func TestModuleString(t *testing.T) { "foo{pre_arch:b}", "foo{pre_arch:a}", - // After rename_top_down. - "foo_renamed1{pre_arch:a}", + // After rename_top_down (reversed because pre_deps TransitionMutator.Split is TopDown). "foo_renamed1{pre_arch:b}", + "foo_renamed1{pre_arch:a}", - // After pre_deps. - "foo_renamed1{pre_arch:a,pre_deps:c}", - "foo_renamed1{pre_arch:a,pre_deps:d}", - "foo_renamed1{pre_arch:b,pre_deps:c}", + // After pre_deps (reversed because post_deps TransitionMutator.Split is TopDown). "foo_renamed1{pre_arch:b,pre_deps:d}", + "foo_renamed1{pre_arch:b,pre_deps:c}", + "foo_renamed1{pre_arch:a,pre_deps:d}", + "foo_renamed1{pre_arch:a,pre_deps:c}", // After post_deps. "foo_renamed1{pre_arch:a,pre_deps:c,post_deps:e}", @@ -202,8 +245,10 @@ func TestFinalDepsPhase(t *testing.T) { ctx.AddFarVariationDependencies([]blueprint.Variation{}, dep1Tag, "common_dep_1") } }) - ctx.BottomUp("variant", func(ctx BottomUpMutatorContext) { - ctx.CreateLocalVariations("a", "b") + ctx.Transition("variant", &testTransitionMutator{ + split: func(ctx BaseModuleContext) []string { + return []string{"a", "b"} + }, }) }) @@ -242,28 +287,21 @@ func TestFinalDepsPhase(t *testing.T) { AssertDeepEquals(t, "final", finalWant, finalGot) } -func TestNoCreateVariationsInFinalDeps(t *testing.T) { - checkErr := func() { - if err := recover(); err == nil || !strings.Contains(fmt.Sprintf("%s", err), "not allowed in FinalDepsMutators") { - panic("Expected FinalDepsMutators consistency check to fail") - } - } - +func TestTransitionMutatorInFinalDeps(t *testing.T) { GroupFixturePreparers( FixtureRegisterWithContext(func(ctx RegistrationContext) { ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.BottomUp("vars", func(ctx BottomUpMutatorContext) { - defer checkErr() - ctx.CreateVariations("a", "b") - }) - ctx.BottomUp("local_vars", func(ctx BottomUpMutatorContext) { - defer checkErr() - ctx.CreateLocalVariations("a", "b") + ctx.Transition("vars", &testTransitionMutator{ + split: func(ctx BaseModuleContext) []string { + return []string{"a", "b"} + }, }) }) ctx.RegisterModuleType("test", mutatorTestModuleFactory) }), FixtureWithRootAndroidBp(`test {name: "foo"}`), - ).RunTest(t) + ). + ExtendWithErrorHandler(FixtureExpectsOneErrorPattern("not allowed in FinalDepsMutators")). + RunTest(t) } diff --git a/android/neverallow.go b/android/neverallow.go index b89d150cb..e135f5780 100644 --- a/android/neverallow.go +++ b/android/neverallow.go @@ -58,7 +58,6 @@ func init() { AddNeverAllowRules(createInitFirstStageRules()...) AddNeverAllowRules(createProhibitFrameworkAccessRules()...) AddNeverAllowRules(createCcStubsRule()) - AddNeverAllowRules(createJavaExcludeStaticLibsRule()) AddNeverAllowRules(createProhibitHeaderOnlyRule()) AddNeverAllowRules(createLimitNdkExportRule()...) } @@ -253,14 +252,6 @@ func createProhibitFrameworkAccessRules() []Rule { } } -func createJavaExcludeStaticLibsRule() Rule { - return NeverAllow(). - NotIn("build/soong", "libcore", "frameworks/base/api"). - ModuleType("java_library"). - WithMatcher("exclude_static_libs", isSetMatcherInstance). - Because("exclude_static_libs property is only allowed for java modules defined in build/soong, libcore, and frameworks/base/api") -} - func createProhibitHeaderOnlyRule() Rule { return NeverAllow(). Without("name", "framework-minus-apex-headers"). diff --git a/android/neverallow_test.go b/android/neverallow_test.go index b2620ef92..192c92409 100644 --- a/android/neverallow_test.go +++ b/android/neverallow_test.go @@ -344,23 +344,6 @@ var neverallowTests = []struct { `module "outside_allowed_list": violates neverallow`, }, }, - // Test for the rule restricting use of exclude_static_libs - { - name: `"exclude_static_libs" outside allowed directory`, - fs: map[string][]byte{ - "a/b/Android.bp": []byte(` - java_library { - name: "baz", - exclude_static_libs: [ - "bar", - ], - } - `), - }, - expectedErrors: []string{ - `exclude_static_libs property is only allowed for java modules defined in build/soong, libcore, and frameworks/base/api`, - }, - }, // Test for only allowing headers_only for framework-minus-apex-headers { name: `"headers_only" outside framework-minus-apex-headers modules`, diff --git a/android/packaging_test.go b/android/packaging_test.go index 0f7bb39a1..f5b1020fc 100644 --- a/android/packaging_test.go +++ b/android/packaging_test.go @@ -118,6 +118,7 @@ func runPackagingTest(t *testing.T, config testConfig, bp string, expected []str } result := GroupFixturePreparers( + PrepareForTestWithDefaults, PrepareForTestWithArchMutator, FixtureRegisterWithContext(func(ctx RegistrationContext) { ctx.RegisterModuleType("component", componentTestModuleFactory) diff --git a/android/paths.go b/android/paths.go index e45795989..1c8258ede 100644 --- a/android/paths.go +++ b/android/paths.go @@ -27,7 +27,6 @@ import ( "strings" "github.com/google/blueprint" - "github.com/google/blueprint/bootstrap" "github.com/google/blueprint/pathtools" ) @@ -92,8 +91,10 @@ func GlobFiles(ctx EarlyModulePathContext, globPattern string, excludes []string // the Path methods that rely on module dependencies having been resolved. type ModuleWithDepsPathContext interface { EarlyModulePathContext - VisitDirectDepsBlueprint(visit func(blueprint.Module)) + OtherModuleProviderContext + VisitDirectDeps(visit func(Module)) OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag + HasMutatorFinished(mutatorName string) bool } // ModuleMissingDepsPathContext is a subset of *ModuleContext methods required by @@ -554,13 +555,6 @@ func (p OutputPaths) Strings() []string { return ret } -// PathForGoBinary returns the path to the installed location of a bootstrap_go_binary module. -func PathForGoBinary(ctx PathContext, goBinary bootstrap.GoBinaryTool) Path { - goBinaryInstallDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "bin") - rel := Rel(ctx, goBinaryInstallDir.String(), goBinary.InstallPath()) - return goBinaryInstallDir.Join(ctx, rel) -} - // Expands Paths to a SourceFileProducer or OutputFileProducer module dependency referenced via ":name" or ":name{.tag}" syntax. // If the dependency is not found, a missingErrorDependency is returned. // If the module dependency is not a SourceFileProducer or OutputFileProducer, appropriate errors will be returned. @@ -572,10 +566,6 @@ func getPathsFromModuleDep(ctx ModuleWithDepsPathContext, path, moduleName, tag if aModule, ok := module.(Module); ok && !aModule.Enabled(ctx) { return nil, missingDependencyError{[]string{moduleName}} } - if goBinary, ok := module.(bootstrap.GoBinaryTool); ok && tag == "" { - goBinaryPath := PathForGoBinary(ctx, goBinary) - return Paths{goBinaryPath}, nil - } outputFiles, err := outputFilesForModule(ctx, module, tag) if outputFiles != nil && err == nil { return outputFiles, nil @@ -608,7 +598,7 @@ func GetModuleFromPathDep(ctx ModuleWithDepsPathContext, moduleName, tag string) // create the tag here as was supplied to create the tag when the dependency was added so that // this finds the matching dependency module. expectedTag := sourceOrOutputDepTag(moduleName, tag) - ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) { + ctx.VisitDirectDeps(func(module Module) { depTag := ctx.OtherModuleDependencyTag(module) if depTag == expectedTag { found = module diff --git a/android/prebuilt.go b/android/prebuilt.go index fd5a6eaee..4f04d057b 100644 --- a/android/prebuilt.go +++ b/android/prebuilt.go @@ -359,8 +359,8 @@ func GetEmbeddedPrebuilt(module Module) *Prebuilt { // // This function is for use on dependencies after PrebuiltPostDepsMutator has // run - any dependency that is registered before that will already reference -// the right module. This function is only safe to call after all mutators that -// may call CreateVariations, e.g. in GenerateAndroidBuildActions. +// the right module. This function is only safe to call after all TransitionMutators +// have run, e.g. in GenerateAndroidBuildActions. func PrebuiltGetPreferred(ctx BaseModuleContext, module Module) Module { if !module.IsReplacedByPrebuilt() { return module diff --git a/android/product_config.go b/android/product_config.go index 04180bf2b..ce3acc9f2 100644 --- a/android/product_config.go +++ b/android/product_config.go @@ -14,7 +14,9 @@ package android -import "github.com/google/blueprint/proptools" +import ( + "github.com/google/blueprint/proptools" +) func init() { ctx := InitRegistrationContext diff --git a/android/product_config_to_bp.go b/android/product_config_to_bp.go new file mode 100644 index 000000000..680328f67 --- /dev/null +++ b/android/product_config_to_bp.go @@ -0,0 +1,35 @@ +// Copyright 2024 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package android + +func init() { + ctx := InitRegistrationContext + ctx.RegisterParallelSingletonType("product_config_to_bp_singleton", productConfigToBpSingletonFactory) +} + +type productConfigToBpSingleton struct{} + +func (s *productConfigToBpSingleton) GenerateBuildActions(ctx SingletonContext) { + // TODO: update content from make-based product config + var content string + generatedBp := PathForOutput(ctx, "soong_generated_product_config.bp") + WriteFileRule(ctx, generatedBp, content) + ctx.Phony("product_config_to_bp", generatedBp) +} + +// productConfigToBpSingleton generates a bp file from make-based product config +func productConfigToBpSingletonFactory() Singleton { + return &productConfigToBpSingleton{} +} diff --git a/android/rule_builder.go b/android/rule_builder.go index 18bbcab5c..56de9cd00 100644 --- a/android/rule_builder.go +++ b/android/rule_builder.go @@ -38,6 +38,9 @@ const sboxOutSubDir = "out" const sboxToolsSubDir = "tools" const sboxOutDir = sboxSandboxBaseDir + "/" + sboxOutSubDir +const nsjailToolsSubDir = "tools" +const nsjailOutDir = "out" + // RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build // graph. type RuleBuilder struct { @@ -59,6 +62,9 @@ type RuleBuilder struct { sboxManifestPath WritablePath missingDeps []string args map[string]string + nsjail bool + nsjailBasePath WritablePath + nsjailImplicits Paths } // NewRuleBuilder returns a newly created RuleBuilder. @@ -165,12 +171,43 @@ func (r *RuleBuilder) Sbox(outputDir WritablePath, manifestPath WritablePath) *R if len(r.commands) > 0 { panic("Sbox() may not be called after Command()") } + if r.nsjail { + panic("Sbox() may not be called after Nsjail()") + } r.sbox = true r.outDir = outputDir r.sboxManifestPath = manifestPath return r } +// Nsjail marks the rule as needing to be wrapped by nsjail. The outputDir should point to the +// output directory that nsjail will mount to out/. It should not be written to by any other rule. +// baseDir should point to a location where nsjail will mount to /nsjail_build_sandbox, which will +// be the working directory of the command. +func (r *RuleBuilder) Nsjail(outputDir WritablePath, baseDir WritablePath) *RuleBuilder { + if len(r.commands) > 0 { + panic("Nsjail() may not be called after Command()") + } + if r.sbox { + panic("Nsjail() may not be called after Sbox()") + } + r.nsjail = true + r.outDir = outputDir + r.nsjailBasePath = baseDir + return r +} + +// NsjailImplicits adds implicit inputs that are not directly mounted. This is useful when +// the rule mounts directories, as files within those directories can be globbed and +// tracked as dependencies with NsjailImplicits(). +func (r *RuleBuilder) NsjailImplicits(inputs Paths) *RuleBuilder { + if !r.nsjail { + panic("NsjailImplicits() must be called after Nsjail()") + } + r.nsjailImplicits = append(r.nsjailImplicits, inputs...) + return r +} + // SandboxTools enables tool sandboxing for the rule by copying any referenced tools into the // sandbox. func (r *RuleBuilder) SandboxTools() *RuleBuilder { @@ -514,7 +551,73 @@ func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString b commandString := strings.Join(commands, " && ") - if r.sbox { + if !r.sbox { + // If not using sbox the rule will run the command directly, put the hash of the + // list of input files in a comment at the end of the command line to ensure ninja + // reruns the rule when the list of input files changes. + commandString += " # hash of input list: " + hashSrcFiles(inputs) + } + + if r.nsjail { + var nsjailCmd strings.Builder + nsjailPath := r.ctx.Config().PrebuiltBuildTool(r.ctx, "nsjail") + nsjailCmd.WriteString("mkdir -p ") + nsjailCmd.WriteString(r.nsjailBasePath.String()) + nsjailCmd.WriteString(" && ") + nsjailCmd.WriteString(nsjailPath.String()) + nsjailCmd.WriteRune(' ') + nsjailCmd.WriteString("-B $PWD/") + nsjailCmd.WriteString(r.nsjailBasePath.String()) + nsjailCmd.WriteString(":nsjail_build_sandbox") + + // out is mounted to $(genDir). + nsjailCmd.WriteString(" -B $PWD/") + nsjailCmd.WriteString(r.outDir.String()) + nsjailCmd.WriteString(":nsjail_build_sandbox/out") + + for _, input := range inputs { + nsjailCmd.WriteString(" -R $PWD/") + nsjailCmd.WriteString(input.String()) + nsjailCmd.WriteString(":nsjail_build_sandbox/") + nsjailCmd.WriteString(r.nsjailPathForInputRel(input)) + } + for _, tool := range tools { + nsjailCmd.WriteString(" -R $PWD/") + nsjailCmd.WriteString(tool.String()) + nsjailCmd.WriteString(":nsjail_build_sandbox/") + nsjailCmd.WriteString(nsjailPathForToolRel(r.ctx, tool)) + } + inputs = append(inputs, tools...) + for _, c := range r.commands { + for _, tool := range c.packagedTools { + nsjailCmd.WriteString(" -R $PWD/") + nsjailCmd.WriteString(tool.srcPath.String()) + nsjailCmd.WriteString(":nsjail_build_sandbox/") + nsjailCmd.WriteString(nsjailPathForPackagedToolRel(tool)) + inputs = append(inputs, tool.srcPath) + } + } + + // These five directories are necessary to run native host tools like /bin/bash and py3-cmd. + nsjailCmd.WriteString(" -R /bin") + nsjailCmd.WriteString(" -R /lib") + nsjailCmd.WriteString(" -R /lib64") + nsjailCmd.WriteString(" -R /dev") + nsjailCmd.WriteString(" -R /usr") + + nsjailCmd.WriteString(" -m none:/tmp:tmpfs:size=1073741824") // 1GB, should be enough + nsjailCmd.WriteString(" -D nsjail_build_sandbox") + nsjailCmd.WriteString(" --disable_rlimits") + nsjailCmd.WriteString(" -q") + nsjailCmd.WriteString(" -- ") + nsjailCmd.WriteString("/bin/bash -c ") + nsjailCmd.WriteString(proptools.ShellEscape(commandString)) + + commandString = nsjailCmd.String() + + inputs = append(inputs, nsjailPath) + inputs = append(inputs, r.nsjailImplicits...) + } else if r.sbox { // If running the command inside sbox, write the rule data out to an sbox // manifest.textproto. manifest := sbox_proto.Manifest{} @@ -734,11 +837,6 @@ func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString b rewrapperCommand := r.rbeParams.NoVarTemplate(r.ctx.Config().RBEWrapper()) commandString = rewrapperCommand + " bash -c '" + strings.ReplaceAll(commandString, `'`, `'\''`) + "'" } - } else { - // If not using sbox the rule will run the command directly, put the hash of the - // list of input files in a comment at the end of the command line to ensure ninja - // reruns the rule when the list of input files changes. - commandString += " # hash of input list: " + hashSrcFiles(inputs) } // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to @@ -869,6 +967,8 @@ func (c *RuleBuilderCommand) PathForInput(path Path) string { rel = filepath.Join(sboxSandboxBaseDir, rel) } return rel + } else if c.rule.nsjail { + return c.rule.nsjailPathForInputRel(path) } return path.String() } @@ -894,6 +994,10 @@ func (c *RuleBuilderCommand) PathForOutput(path WritablePath) string { // Errors will be handled in RuleBuilder.Build where we have a context to report them rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String()) return filepath.Join(sboxOutDir, rel) + } else if c.rule.nsjail { + // Errors will be handled in RuleBuilder.Build where we have a context to report them + rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String()) + return filepath.Join(nsjailOutDir, rel) } return path.String() } @@ -945,15 +1049,49 @@ func sboxPathForPackagedToolRel(spec PackagingSpec) string { return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage) } +func nsjailPathForToolRel(ctx BuilderContext, path Path) string { + // Errors will be handled in RuleBuilder.Build where we have a context to report them + toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "") + relOutSoong, isRelOutSoong, _ := maybeRelErr(toolDir.String(), path.String()) + if isRelOutSoong { + // The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out + return filepath.Join(nsjailToolsSubDir, "out", relOutSoong) + } + // The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src + return filepath.Join(nsjailToolsSubDir, "src", path.String()) +} + +func (r *RuleBuilder) nsjailPathForInputRel(path Path) string { + rel, isRelSboxOut, _ := maybeRelErr(r.outDir.String(), path.String()) + if isRelSboxOut { + return filepath.Join(nsjailOutDir, rel) + } + return path.String() +} + +func (r *RuleBuilder) nsjailPathsForInputsRel(paths Paths) []string { + ret := make([]string, len(paths)) + for i, path := range paths { + ret[i] = r.nsjailPathForInputRel(path) + } + return ret +} + +func nsjailPathForPackagedToolRel(spec PackagingSpec) string { + return filepath.Join(nsjailToolsSubDir, "out", spec.relPathInPackage) +} + // PathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the // tool after copying it into the sandbox. This can be used on the RuleBuilder command line to // reference the tool. func (c *RuleBuilderCommand) PathForPackagedTool(spec PackagingSpec) string { - if !c.rule.sboxTools { - panic("PathForPackagedTool() requires SandboxTools()") + if c.rule.sboxTools { + return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec)) + } else if c.rule.nsjail { + return nsjailPathForPackagedToolRel(spec) + } else { + panic("PathForPackagedTool() requires SandboxTools() or Nsjail()") } - - return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec)) } // PathForTool takes a path to a tool, which may be an output file or a source file, and returns @@ -962,6 +1100,8 @@ func (c *RuleBuilderCommand) PathForPackagedTool(spec PackagingSpec) string { func (c *RuleBuilderCommand) PathForTool(path Path) string { if c.rule.sbox && c.rule.sboxTools { return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path)) + } else if c.rule.nsjail { + return nsjailPathForToolRel(c.rule.ctx, path) } return path.String() } @@ -976,6 +1116,12 @@ func (c *RuleBuilderCommand) PathsForTools(paths Paths) []string { ret = append(ret, filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))) } return ret + } else if c.rule.nsjail { + var ret []string + for _, path := range paths { + ret = append(ret, nsjailPathForToolRel(c.rule.ctx, path)) + } + return ret } return paths.Strings() } @@ -983,20 +1129,22 @@ func (c *RuleBuilderCommand) PathsForTools(paths Paths) []string { // PackagedTool adds the specified tool path to the command line. It can only be used with tool // sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox. func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand { - if !c.rule.sboxTools { - panic("PackagedTool() requires SandboxTools()") - } - c.packagedTools = append(c.packagedTools, spec) - c.Text(sboxPathForPackagedToolRel(spec)) + if c.rule.sboxTools { + c.Text(sboxPathForPackagedToolRel(spec)) + } else if c.rule.nsjail { + c.Text(nsjailPathForPackagedToolRel(spec)) + } else { + panic("PackagedTool() requires SandboxTools() or Nsjail()") + } return c } // ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command // line. It can only be used with tool sandboxing enabled by SandboxTools(). func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand { - if !c.rule.sboxTools { - panic("ImplicitPackagedTool() requires SandboxTools()") + if !c.rule.sboxTools && !c.rule.nsjail { + panic("ImplicitPackagedTool() requires SandboxTools() or Nsjail()") } c.packagedTools = append(c.packagedTools, spec) @@ -1006,8 +1154,8 @@ func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuild // ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command // line. It can only be used with tool sandboxing enabled by SandboxTools(). func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand { - if !c.rule.sboxTools { - panic("ImplicitPackagedTools() requires SandboxTools()") + if !c.rule.sboxTools && !c.rule.nsjail { + panic("ImplicitPackagedTools() requires SandboxTools() or Nsjail()") } c.packagedTools = append(c.packagedTools, specs...) diff --git a/android/sdk.go b/android/sdk.go index d3f04a4f9..ab9a91ccb 100644 --- a/android/sdk.go +++ b/android/sdk.go @@ -813,8 +813,6 @@ type SdkMemberProperties interface { // SdkMemberContext provides access to information common to a specific member. type SdkMemberContext interface { - ConfigAndErrorContext - // SdkModuleContext returns the module context of the sdk common os variant which is creating the // snapshot. // diff --git a/android/singleton.go b/android/singleton.go index 8542bd9e6..913bf6a56 100644 --- a/android/singleton.go +++ b/android/singleton.go @@ -90,6 +90,10 @@ type SingletonContext interface { // OtherModulePropertyErrorf reports an error on the line number of the given property of the given module OtherModulePropertyErrorf(module Module, property string, format string, args ...interface{}) + + // HasMutatorFinished returns true if the given mutator has finished running. + // It will panic if given an invalid mutator name. + HasMutatorFinished(mutatorName string) bool } type singletonAdaptor struct { @@ -286,3 +290,7 @@ func (s *singletonContextAdaptor) otherModuleProvider(module blueprint.Module, p func (s *singletonContextAdaptor) OtherModulePropertyErrorf(module Module, property string, format string, args ...interface{}) { s.blueprintSingletonContext().OtherModulePropertyErrorf(module, property, format, args...) } + +func (s *singletonContextAdaptor) HasMutatorFinished(mutatorName string) bool { + return s.blueprintSingletonContext().HasMutatorFinished(mutatorName) +} diff --git a/android/singleton_module_test.go b/android/singleton_module_test.go index 3b1bf39e3..3b8c6b213 100644 --- a/android/singleton_module_test.go +++ b/android/singleton_module_test.go @@ -96,12 +96,6 @@ func TestUnusedSingletonModule(t *testing.T) { } } -func testVariantSingletonModuleMutator(ctx BottomUpMutatorContext) { - if _, ok := ctx.Module().(*testSingletonModule); ok { - ctx.CreateVariations("a", "b") - } -} - func TestVariantSingletonModule(t *testing.T) { if testing.Short() { t.Skip("test fails with data race enabled") @@ -116,7 +110,11 @@ func TestVariantSingletonModule(t *testing.T) { prepareForSingletonModuleTest, FixtureRegisterWithContext(func(ctx RegistrationContext) { ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.BottomUp("test_singleton_module_mutator", testVariantSingletonModuleMutator) + ctx.Transition("test_singleton_module_mutator", &testTransitionMutator{ + split: func(ctx BaseModuleContext) []string { + return []string{"a", "b"} + }, + }) }) }), ). diff --git a/android/team_proto/Android.bp b/android/team_proto/Android.bp index 7e2a4c137..5faaaf10e 100644 --- a/android/team_proto/Android.bp +++ b/android/team_proto/Android.bp @@ -40,4 +40,8 @@ python_library_host { proto: { canonical_path_from_root: false, }, + visibility: [ + "//build/soong:__subpackages__", + "//tools/asuite/team_build_scripts", + ], } diff --git a/android/testing.go b/android/testing.go index 816707d63..196b22e3e 100644 --- a/android/testing.go +++ b/android/testing.go @@ -1326,7 +1326,15 @@ func (ctx *panickingConfigAndErrorContext) Config() Config { return ctx.ctx.Config() } -func PanickingConfigAndErrorContext(ctx *TestContext) ConfigAndErrorContext { +func (ctx *panickingConfigAndErrorContext) HasMutatorFinished(mutatorName string) bool { + return ctx.ctx.HasMutatorFinished(mutatorName) +} + +func (ctx *panickingConfigAndErrorContext) otherModuleProvider(m blueprint.Module, p blueprint.AnyProviderKey) (any, bool) { + return ctx.ctx.otherModuleProvider(m, p) +} + +func PanickingConfigAndErrorContext(ctx *TestContext) ConfigurableEvaluatorContext { return &panickingConfigAndErrorContext{ ctx: ctx, } diff --git a/android/updatable_modules.go b/android/updatable_modules.go deleted file mode 100644 index d2595ed14..000000000 --- a/android/updatable_modules.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2022 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package android - -// This file contains branch specific constants. They are stored in a separate -// file to minimise the potential of merge conflicts between branches when -// the code from the package is changed. - -// The default manifest version for all the modules on this branch. -// This version code will be used only if there is no version field in the -// module's apex_manifest.json. Release branches have their version injected -// into apex_manifest.json by the tooling and will not use the version set -// here. Developers can also set the version field locally in the -// apex_manifest.json to build a module with a specific version. -// -// The value follows the schema from go/mainline-version-codes, and is chosen -// based on the branch such that the builds from testing and development -// branches will have a version higher than the prebuilts. -// Versions per branch: -// * x-dev - xx0090000 (where xx is the branch SDK level) -// * AOSP - xx9990000 -// * x-mainline-prod - xx9990000 -// * master - 990090000 -const DefaultUpdatableModuleVersion = "352090000" diff --git a/android/util.go b/android/util.go index 3c0af2f38..2d269b724 100644 --- a/android/util.go +++ b/android/util.go @@ -177,6 +177,41 @@ func setFromList[T comparable](l []T) map[T]bool { return m } +// PrettyConcat returns the formatted concatenated string suitable for displaying user-facing +// messages. +func PrettyConcat(list []string, quote bool, lastSep string) string { + if len(list) == 0 { + return "" + } + + quoteStr := func(v string) string { + if !quote { + return v + } + return fmt.Sprintf("%q", v) + } + + if len(list) == 1 { + return quoteStr(list[0]) + } + + var sb strings.Builder + for i, val := range list { + if i > 0 { + sb.WriteString(", ") + } + if i == len(list)-1 { + sb.WriteString(lastSep) + if lastSep != "" { + sb.WriteString(" ") + } + } + sb.WriteString(quoteStr(val)) + } + + return sb.String() +} + // ListSetDifference checks if the two lists contain the same elements. It returns // a boolean which is true if there is a difference, and then returns lists of elements // that are in l1 but not l2, and l2 but not l1. diff --git a/android/util_test.go b/android/util_test.go index 6537d69b9..b76ffcfea 100644 --- a/android/util_test.go +++ b/android/util_test.go @@ -867,3 +867,51 @@ func TestHasIntersection(t *testing.T) { }) } } + +var prettyConcatTestCases = []struct { + name string + list []string + quote bool + lastSeparator string + expected string +}{ + { + name: "empty", + list: []string{}, + quote: false, + lastSeparator: "and", + expected: ``, + }, + { + name: "single", + list: []string{"a"}, + quote: true, + lastSeparator: "and", + expected: `"a"`, + }, + { + name: "with separator", + list: []string{"a", "b", "c"}, + quote: true, + lastSeparator: "or", + expected: `"a", "b", or "c"`, + }, + { + name: "without separator", + list: []string{"a", "b", "c"}, + quote: false, + lastSeparator: "", + expected: `a, b, c`, + }, +} + +func TestPrettyConcat(t *testing.T) { + for _, testCase := range prettyConcatTestCases { + t.Run(testCase.name, func(t *testing.T) { + concatString := PrettyConcat(testCase.list, testCase.quote, testCase.lastSeparator) + if !reflect.DeepEqual(concatString, testCase.expected) { + t.Errorf("expected %#v, got %#v", testCase.expected, concatString) + } + }) + } +} diff --git a/android/variable.go b/android/variable.go index 557b0b70f..fbab93b7a 100644 --- a/android/variable.go +++ b/android/variable.go @@ -424,10 +424,10 @@ type ProductVariables struct { TargetFSConfigGen []string `json:",omitempty"` - EnforceProductPartitionInterface *bool `json:",omitempty"` + UseSoongSystemImage *bool `json:",omitempty"` + ProductSoongDefinedSystemImage *string `json:",omitempty"` - EnforceInterPartitionJavaSdkLibrary *bool `json:",omitempty"` - InterPartitionJavaLibraryAllowList []string `json:",omitempty"` + EnforceProductPartitionInterface *bool `json:",omitempty"` BoardUsesRecoveryAsBoot *bool `json:",omitempty"` @@ -521,6 +521,10 @@ type ProductVariables struct { BoardAvbSystemAddHashtreeFooterArgs []string `json:",omitempty"` DeviceFrameworkCompatibilityMatrixFile []string `json:",omitempty"` DeviceProductCompatibilityMatrixFile []string `json:",omitempty"` + + PartitionVarsForSoongMigrationOnlyDoNotUse PartitionVariables + + ExtraAllowedDepsTxt *string `json:",omitempty"` } type PartitionQualifiedVariablesType struct { @@ -577,7 +581,8 @@ type PartitionVariables struct { BoardAvbEnable bool `json:",omitempty"` - ProductPackages []string `json:",omitempty"` + ProductPackages []string `json:",omitempty"` + ProductPackagesDebug []string `json:",omitempty"` } func boolPtr(v bool) *bool { diff --git a/android/visibility.go b/android/visibility.go index 89c0adc15..61f220026 100644 --- a/android/visibility.go +++ b/android/visibility.go @@ -283,7 +283,7 @@ func RegisterVisibilityRuleGatherer(ctx RegisterMutatorsContext) { // This must be registered after the deps have been resolved. func RegisterVisibilityRuleEnforcer(ctx RegisterMutatorsContext) { - ctx.TopDown("visibilityRuleEnforcer", visibilityRuleEnforcer).Parallel() + ctx.BottomUp("visibilityRuleEnforcer", visibilityRuleEnforcer).Parallel() } // Checks the per-module visibility rule lists before defaults expansion. @@ -507,7 +507,7 @@ func splitRule(ctx BaseModuleContext, ruleExpression string, currentPkg, propert return true, pkg, name } -func visibilityRuleEnforcer(ctx TopDownMutatorContext) { +func visibilityRuleEnforcer(ctx BottomUpMutatorContext) { qualified := createVisibilityModuleReference(ctx.ModuleName(), ctx.ModuleDir(), ctx.Module()) // Visit all the dependencies making sure that this module has access to them all. diff --git a/apex/Android.bp b/apex/Android.bp index 17fdfc36a..4848513f2 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -15,7 +15,6 @@ bootstrap_go_package { "soong-cc", "soong-filesystem", "soong-java", - "soong-multitree", "soong-provenance", "soong-python", "soong-rust", @@ -43,4 +42,6 @@ bootstrap_go_package { "systemserver_classpath_fragment_test.go", ], pluginFor: ["soong_build"], + // Used by plugins + visibility: ["//visibility:public"], } diff --git a/apex/apex.go b/apex/apex.go index 27e9caa57..07464660c 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -32,7 +32,6 @@ import ( prebuilt_etc "android/soong/etc" "android/soong/filesystem" "android/soong/java" - "android/soong/multitree" "android/soong/rust" "android/soong/sh" ) @@ -50,17 +49,11 @@ func registerApexBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("override_apex", OverrideApexFactory) ctx.RegisterModuleType("apex_set", apexSetFactory) - ctx.PreArchMutators(registerPreArchMutators) ctx.PreDepsMutators(RegisterPreDepsMutators) ctx.PostDepsMutators(RegisterPostDepsMutators) } -func registerPreArchMutators(ctx android.RegisterMutatorsContext) { - ctx.TopDown("prebuilt_apex_module_creator", prebuiltApexModuleCreatorMutator).Parallel() -} - func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) { - ctx.TopDown("apex_vndk", apexVndkMutator).Parallel() ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).Parallel() } @@ -75,8 +68,6 @@ func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) { ctx.Transition("apex", &apexTransitionMutator{}) ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel() ctx.BottomUp("apex_dcla_deps", apexDCLADepsMutator).Parallel() - // Register after apex_info mutator so that it can use ApexVariationName - ctx.TopDown("apex_strict_updatability_lint", apexStrictUpdatibilityLintMutator).Parallel() } type apexBundleProperties struct { @@ -209,7 +200,7 @@ type ApexNativeDependencies struct { Native_shared_libs proptools.Configurable[[]string] // List of JNI libraries that are embedded inside this APEX. - Jni_libs []string + Jni_libs proptools.Configurable[[]string] // List of rust dyn libraries that are embedded inside this APEX. Rust_dyn_libs []string @@ -295,7 +286,7 @@ type ResolvedApexNativeDependencies struct { // Merge combines another ApexNativeDependencies into this one func (a *ResolvedApexNativeDependencies) Merge(ctx android.BaseMutatorContext, b ApexNativeDependencies) { a.Native_shared_libs = append(a.Native_shared_libs, b.Native_shared_libs.GetOrDefault(ctx, nil)...) - a.Jni_libs = append(a.Jni_libs, b.Jni_libs...) + a.Jni_libs = append(a.Jni_libs, b.Jni_libs.GetOrDefault(ctx, nil)...) a.Rust_dyn_libs = append(a.Rust_dyn_libs, b.Rust_dyn_libs...) a.Binaries = append(a.Binaries, b.Binaries.GetOrDefault(ctx, nil)...) a.Tests = append(a.Tests, b.Tests...) @@ -431,7 +422,6 @@ type apexBundle struct { android.ModuleBase android.DefaultableModuleBase android.OverridableModuleBase - multitree.ExportableModuleBase // Properties properties apexBundleProperties @@ -584,7 +574,7 @@ type apexFile struct { dataPaths []android.DataPath // becomes LOCAL_TEST_DATA jacocoReportClassesFile android.Path // only for javalibs and apps - lintDepSets java.LintDepSets // only for javalibs and apps + lintInfo *java.LintInfo // only for javalibs and apps certificate java.Certificate // only for apps overriddenPackageName string // only for apps @@ -847,7 +837,7 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) { deps.Merge(ctx, ApexNativeDependencies{ Native_shared_libs: proptools.NewConfigurable[[]string](nil, nil), Tests: nil, - Jni_libs: nil, + Jni_libs: proptools.NewConfigurable[[]string](nil, nil), Binaries: a.properties.Binaries, }) } @@ -1116,34 +1106,6 @@ func apexInfoMutator(mctx android.TopDownMutatorContext) { enforceAppUpdatability(mctx) } -// apexStrictUpdatibilityLintMutator propagates strict_updatability_linting to transitive deps of a mainline module -// This check is enforced for updatable modules -func apexStrictUpdatibilityLintMutator(mctx android.TopDownMutatorContext) { - if !mctx.Module().Enabled(mctx) { - return - } - if apex, ok := mctx.Module().(*apexBundle); ok && apex.checkStrictUpdatabilityLinting(mctx) { - mctx.WalkDeps(func(child, parent android.Module) bool { - // b/208656169 Do not propagate strict updatability linting to libcore/ - // These libs are available on the classpath during compilation - // These libs are transitive deps of the sdk. See java/sdk.go:decodeSdkDep - // Only skip libraries defined in libcore root, not subdirectories - if mctx.OtherModuleDir(child) == "libcore" { - // Do not traverse transitive deps of libcore/ libs - return false - } - if android.InList(child.Name(), skipLintJavalibAllowlist) { - return false - } - if lintable, ok := child.(java.LintDepSetsIntf); ok { - lintable.SetStrictUpdatabilityLinting(true) - } - // visit transitive deps - return true - }) - } -} - // enforceAppUpdatability propagates updatable=true to apps of updatable apexes func enforceAppUpdatability(mctx android.TopDownMutatorContext) { if !mctx.Module().Enabled(mctx) { @@ -1204,20 +1166,9 @@ var ( "test_jitzygote_com.android.art", // go/keep-sorted end } - - // TODO: b/215736885 Remove this list - skipLintJavalibAllowlist = []string{ - "conscrypt.module.platform.api.stubs", - "conscrypt.module.public.api.stubs", - "conscrypt.module.public.api.stubs.system", - "conscrypt.module.public.api.stubs.module_lib", - "framework-media.stubs", - "framework-media.stubs.system", - "framework-media.stubs.module_lib", - } ) -func (a *apexBundle) checkStrictUpdatabilityLinting(mctx android.TopDownMutatorContext) bool { +func (a *apexBundle) checkStrictUpdatabilityLinting(mctx android.ModuleContext) bool { // The allowlist contains the base apex name, so use that instead of the ApexVariationName return a.Updatable() && !android.InList(mctx.ModuleName(), skipStrictUpdatabilityLintAllowlist) } @@ -1399,8 +1350,6 @@ func (a *apexBundle) DepIsInSameApex(_ android.BaseModuleContext, _ android.Modu return true } -var _ multitree.Exportable = (*apexBundle)(nil) - func (a *apexBundle) Exportable() bool { return true } @@ -1660,7 +1609,6 @@ type javaModule interface { BaseModuleName() string DexJarBuildPath(ctx android.ModuleErrorfContext) java.OptionalDexJarPath JacocoReportClassesFile() android.Path - LintDepSets() java.LintDepSets Stem() string } @@ -1680,7 +1628,9 @@ func apexFileForJavaModuleWithFile(ctx android.ModuleContext, module javaModule, dirInApex := "javalib" af := newApexFile(ctx, dexImplementationJar, module.BaseModuleName(), dirInApex, javaSharedLib, module) af.jacocoReportClassesFile = module.JacocoReportClassesFile() - af.lintDepSets = module.LintDepSets() + if lintInfo, ok := android.OtherModuleProvider(ctx, module, java.LintProvider); ok { + af.lintInfo = lintInfo + } af.customStem = module.Stem() + ".jar" // TODO: b/338641779 - Remove special casing of sdkLibrary once bcpf and sscpf depends // on the implementation library @@ -1718,7 +1668,6 @@ type androidApp interface { JacocoReportClassesFile() android.Path Certificate() java.Certificate BaseModuleName() string - LintDepSets() java.LintDepSets PrivAppAllowlist() android.OptionalPath } @@ -1754,7 +1703,9 @@ func apexFilesForAndroidApp(ctx android.BaseModuleContext, aapp androidApp) []ap af := newApexFile(ctx, fileToCopy, aapp.BaseModuleName(), dirInApex, app, aapp) af.jacocoReportClassesFile = aapp.JacocoReportClassesFile() - af.lintDepSets = aapp.LintDepSets() + if lintInfo, ok := android.OtherModuleProvider(ctx, aapp, java.LintProvider); ok { + af.lintInfo = lintInfo + } af.certificate = aapp.Certificate() if app, ok := aapp.(interface { @@ -1805,7 +1756,7 @@ func apexFileForFilesystem(ctx android.BaseModuleContext, buildFile android.Path // visited module, the `do` callback is executed. Returning true in the callback continues the visit // to the child modules. Returning false makes the visit to continue in the sibling or the parent // modules. This is used in check* functions below. -func (a *apexBundle) WalkPayloadDeps(ctx android.ModuleContext, do android.PayloadDepsCallback) { +func (a *apexBundle) WalkPayloadDeps(ctx android.BaseModuleContext, do android.PayloadDepsCallback) { ctx.WalkDeps(func(child, parent android.Module) bool { am, ok := child.(android.ApexModule) if !ok || !am.CanHaveApexVariants() { @@ -1972,12 +1923,12 @@ func (vctx *visitorContext) normalizeFileInfo(mctx android.ModuleContext) { }) } -func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, child, parent blueprint.Module) bool { +func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, child, parent android.Module) bool { depTag := ctx.OtherModuleDependencyTag(child) if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok { return false } - if mod, ok := child.(android.Module); ok && !mod.Enabled(ctx) { + if !child.Enabled(ctx) { return false } depName := ctx.OtherModuleName(child) @@ -2321,7 +2272,7 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { checkDuplicate: a.shouldCheckDuplicate(ctx), unwantedTransitiveDeps: a.properties.Unwanted_transitive_deps, } - ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool { return a.depVisitor(&vctx, ctx, child, parent) }) + ctx.WalkDeps(func(child, parent android.Module) bool { return a.depVisitor(&vctx, ctx, child, parent) }) vctx.normalizeFileInfo(ctx) if a.privateKeyFile == nil { if ctx.Config().AllowMissingDependencies() { @@ -2528,7 +2479,6 @@ func newApexBundle() *apexBundle { android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) android.InitDefaultableModule(module) android.InitOverridableModule(module, &module.overridableProperties.Overrides) - multitree.InitExportableModule(module) return module } @@ -2652,7 +2602,7 @@ func (a *apexBundle) checkStaticLinkingToStubLibraries(ctx android.ModuleContext abInfo, _ := android.ModuleProvider(ctx, android.ApexBundleInfoProvider) - a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool { + a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool { if ccm, ok := to.(*cc.Module); ok { apexName := ctx.ModuleName() fromName := ctx.OtherModuleName(from) @@ -2723,7 +2673,7 @@ func (a *apexBundle) checkClasspathFragments(ctx android.ModuleContext) { func (a *apexBundle) checkJavaStableSdkVersion(ctx android.ModuleContext) { // Visit direct deps only. As long as we guarantee top-level deps are using stable SDKs, // java's checkLinkType guarantees correct usage for transitive deps - ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) { + ctx.VisitDirectDeps(func(module android.Module) { tag := ctx.OtherModuleDependencyTag(module) switch tag { case javaLibTag, androidAppTag: @@ -2751,6 +2701,12 @@ func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) { return } + // Temporarily bypass /product APEXes with a specific prefix. + // TODO: b/352818241 - Remove this after APEX availability is enforced for /product APEXes. + if a.ProductSpecific() && strings.HasPrefix(a.ApexVariationName(), "com.sdv.") { + return + } + // Coverage build adds additional dependencies for the coverage-only runtime libraries. // Requiring them and their transitive depencies with apex_available is not right // because they just add noise. @@ -2758,7 +2714,7 @@ func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) { return } - a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool { + a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool { // As soon as the dependency graph crosses the APEX boundary, don't go further. if externalDep { return false @@ -2785,7 +2741,7 @@ func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) { return false } - if to.AvailableFor(apexName) || baselineApexAvailable(apexName, toName) { + if to.AvailableFor(apexName) { return true } @@ -2810,7 +2766,7 @@ func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) { // checkStaticExecutable ensures that executables in an APEX are not static. func (a *apexBundle) checkStaticExecutables(ctx android.ModuleContext) { - ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) { + ctx.VisitDirectDeps(func(module android.Module) { if ctx.OtherModuleDependencyTag(module) != executableTag { return } @@ -2845,74 +2801,6 @@ func (a *apexBundle) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeI dpInfo.Deps = append(dpInfo.Deps, a.properties.ResolvedSystemserverclasspathFragments...) } -var ( - apexAvailBaseline = makeApexAvailableBaseline() - inverseApexAvailBaseline = invertApexBaseline(apexAvailBaseline) -) - -func baselineApexAvailable(apex, moduleName string) bool { - key := apex - moduleName = normalizeModuleName(moduleName) - - if val, ok := apexAvailBaseline[key]; ok && android.InList(moduleName, val) { - return true - } - - key = android.AvailableToAnyApex - if val, ok := apexAvailBaseline[key]; ok && android.InList(moduleName, val) { - return true - } - - return false -} - -func normalizeModuleName(moduleName string) string { - // Prebuilt modules (e.g. java_import, etc.) have "prebuilt_" prefix added by the build - // system. Trim the prefix for the check since they are confusing - moduleName = android.RemoveOptionalPrebuiltPrefix(moduleName) - if strings.HasPrefix(moduleName, "libclang_rt.") { - // This module has many arch variants that depend on the product being built. - // We don't want to list them all - moduleName = "libclang_rt" - } - if strings.HasPrefix(moduleName, "androidx.") { - // TODO(b/156996905) Set apex_available/min_sdk_version for androidx support libraries - moduleName = "androidx" - } - return moduleName -} - -// Transform the map of apex -> modules to module -> apexes. -func invertApexBaseline(m map[string][]string) map[string][]string { - r := make(map[string][]string) - for apex, modules := range m { - for _, module := range modules { - r[module] = append(r[module], apex) - } - } - return r -} - -// Retrieve the baseline of apexes to which the supplied module belongs. -func BaselineApexAvailable(moduleName string) []string { - return inverseApexAvailBaseline[normalizeModuleName(moduleName)] -} - -// This is a map from apex to modules, which overrides the apex_available setting for that -// particular module to make it available for the apex regardless of its setting. -// TODO(b/147364041): remove this -func makeApexAvailableBaseline() map[string][]string { - // The "Module separator"s below are employed to minimize merge conflicts. - m := make(map[string][]string) - // - // Module separator - // - m["com.android.runtime"] = []string{ - "libz", - } - return m -} - func init() { android.AddNeverAllowRules(createBcpPermittedPackagesRules(qBcpPackages())...) android.AddNeverAllowRules(createBcpPermittedPackagesRules(rBcpPackages())...) diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go index f405cb2fe..00dd44648 100644 --- a/apex/apex_singleton.go +++ b/apex/apex_singleton.go @@ -18,6 +18,7 @@ package apex import ( "encoding/json" + "strings" "github.com/google/blueprint" @@ -58,9 +59,9 @@ var ( // Diff two given lists while ignoring comments in the allowed deps file. diffAllowedApexDepsInfoRule = pctx.AndroidStaticRule("diffAllowedApexDepsInfoRule", blueprint.RuleParams{ - Description: "Diff ${allowed_deps} and ${new_allowed_deps}", + Description: "Diff ${allowed_deps_list} and ${new_allowed_deps}", Command: ` - if grep -v '^#' ${allowed_deps} | diff -B - ${new_allowed_deps}; then + if grep -v -h '^#' ${allowed_deps_list} | sort -u -f| diff -B -u - ${new_allowed_deps}; then touch ${out}; else echo -e "\n******************************"; @@ -81,10 +82,15 @@ var ( exit 1; fi; `, - }, "allowed_deps", "new_allowed_deps") + }, "allowed_deps_list", "new_allowed_deps") ) func (s *apexDepsInfoSingleton) GenerateBuildActions(ctx android.SingletonContext) { + allowedDepsSources := []android.OptionalPath{android.ExistentPathForSource(ctx, "packages/modules/common/build/allowed_deps.txt")} + extraAllowedDepsPath := ctx.Config().ExtraAllowedDepsTxt() + if extraAllowedDepsPath != "" { + allowedDepsSources = append(allowedDepsSources, android.ExistentPathForSource(ctx, extraAllowedDepsPath)) + } updatableFlatLists := android.Paths{} ctx.VisitAllModules(func(module android.Module) { if binaryInfo, ok := module.(android.ApexBundleDepsInfoIntf); ok { @@ -96,37 +102,42 @@ func (s *apexDepsInfoSingleton) GenerateBuildActions(ctx android.SingletonContex } } }) - - allowedDepsSource := android.ExistentPathForSource(ctx, "packages/modules/common/build/allowed_deps.txt") newAllowedDeps := android.PathForOutput(ctx, "apex", "depsinfo", "new-allowed-deps.txt") s.allowedApexDepsInfoCheckResult = android.PathForOutput(ctx, newAllowedDeps.Rel()+".check") - - if !allowedDepsSource.Valid() { + hasOneValidDepsPath := false + for _, allowedDepsSource := range allowedDepsSources { + if allowedDepsSource.Valid() { + hasOneValidDepsPath = true + updatableFlatLists = append(updatableFlatLists, allowedDepsSource.Path()) + } + } + allowedDepsStrList := make([]string, len(allowedDepsSources)) + for _, value := range allowedDepsSources { + allowedDepsStrList = append(allowedDepsStrList, value.String()) + } + allowedDepsListString := strings.Join(allowedDepsStrList, " ") + if !hasOneValidDepsPath { // Unbundled projects may not have packages/modules/common/ checked out; ignore those. ctx.Build(pctx, android.BuildParams{ Rule: android.Touch, Output: s.allowedApexDepsInfoCheckResult, }) } else { - allowedDeps := allowedDepsSource.Path() - ctx.Build(pctx, android.BuildParams{ Rule: generateApexDepsInfoFilesRule, - Inputs: append(updatableFlatLists, allowedDeps), + Inputs: updatableFlatLists, Output: newAllowedDeps, }) - ctx.Build(pctx, android.BuildParams{ Rule: diffAllowedApexDepsInfoRule, Input: newAllowedDeps, Output: s.allowedApexDepsInfoCheckResult, Args: map[string]string{ - "allowed_deps": allowedDeps.String(), - "new_allowed_deps": newAllowedDeps.String(), + "allowed_deps_list": allowedDepsListString, + "new_allowed_deps": newAllowedDeps.String(), }, }) } - ctx.Phony("apex-allowed-deps-check", s.allowedApexDepsInfoCheckResult) } diff --git a/apex/apex_test.go b/apex/apex_test.go index 68c8fb62e..c3a6e6926 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -2185,6 +2185,151 @@ func TestTrackAllowedDeps(t *testing.T) { flatlist, "yourlib(minSdkVersion:29)") } +func TestTrackCustomAllowedDepsInvalidDefaultTxt(t *testing.T) { + ctx := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + updatable: true, + native_shared_libs: [ + "mylib", + "yourlib", + ], + min_sdk_version: "29", + } + + apex { + name: "myapex2", + key: "myapex.key", + updatable: false, + native_shared_libs: ["yourlib"], + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "mylib", + srcs: ["mylib.cpp"], + shared_libs: ["libbar"], + min_sdk_version: "29", + apex_available: ["myapex"], + } + + cc_library { + name: "libbar", + stubs: { versions: ["29", "30"] }, + } + + cc_library { + name: "yourlib", + srcs: ["mylib.cpp"], + min_sdk_version: "29", + apex_available: ["myapex", "myapex2", "//apex_available:platform"], + } + `, withFiles(android.MockFS{ + "packages/modules/common/build/custom_allowed_deps.txt": nil, + }), + android.FixtureModifyProductVariables( + func(variables android.FixtureProductVariables) { + variables.ExtraAllowedDepsTxt = proptools.StringPtr("packages/modules/common/build/custom_allowed_deps.txt") + }, + )) + + depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton") + inputs := depsinfo.Rule("generateApexDepsInfoFilesRule").BuildParams.Inputs.Strings() + android.AssertStringListContains(t, "updatable myapex should generate depsinfo file", inputs, + "out/soong/.intermediates/myapex/android_common_myapex/depsinfo/flatlist.txt") + android.AssertStringListDoesNotContain(t, "non-updatable myapex2 should not generate depsinfo file", inputs, + "out/soong/.intermediates/myapex2/android_common_myapex2/depsinfo/flatlist.txt") + + myapex := ctx.ModuleForTests("myapex", "android_common_myapex") + flatlist := strings.Split(android.ContentFromFileRuleForTests(t, ctx, + myapex.Output("depsinfo/flatlist.txt")), "\n") + android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep", + flatlist, "libbar(minSdkVersion:(no version)) (external)") + android.AssertStringListDoesNotContain(t, "do not track if not available for platform", + flatlist, "mylib:(minSdkVersion:29)") + android.AssertStringListContains(t, "track platform-available lib", + flatlist, "yourlib(minSdkVersion:29)") +} + +func TestTrackCustomAllowedDepsWithDefaultTxt(t *testing.T) { + ctx := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + updatable: true, + native_shared_libs: [ + "mylib", + "yourlib", + ], + min_sdk_version: "29", + } + + apex { + name: "myapex2", + key: "myapex.key", + updatable: false, + native_shared_libs: ["yourlib"], + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "mylib", + srcs: ["mylib.cpp"], + shared_libs: ["libbar"], + min_sdk_version: "29", + apex_available: ["myapex"], + } + + cc_library { + name: "libbar", + stubs: { versions: ["29", "30"] }, + } + + cc_library { + name: "yourlib", + srcs: ["mylib.cpp"], + min_sdk_version: "29", + apex_available: ["myapex", "myapex2", "//apex_available:platform"], + } + `, withFiles(android.MockFS{ + "packages/modules/common/build/custom_allowed_deps.txt": nil, + "packages/modules/common/build/allowed_deps.txt": nil, + }), + android.FixtureModifyProductVariables( + func(variables android.FixtureProductVariables) { + variables.ExtraAllowedDepsTxt = proptools.StringPtr("packages/modules/common/build/custom_allowed_deps.txt") + }, + )) + + depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton") + inputs := depsinfo.Rule("generateApexDepsInfoFilesRule").BuildParams.Inputs.Strings() + android.AssertStringListContains(t, "updatable myapex should generate depsinfo file", inputs, + "out/soong/.intermediates/myapex/android_common_myapex/depsinfo/flatlist.txt") + android.AssertStringListDoesNotContain(t, "non-updatable myapex2 should not generate depsinfo file", inputs, + "out/soong/.intermediates/myapex2/android_common_myapex2/depsinfo/flatlist.txt") + + myapex := ctx.ModuleForTests("myapex", "android_common_myapex") + flatlist := strings.Split(android.ContentFromFileRuleForTests(t, ctx, + myapex.Output("depsinfo/flatlist.txt")), "\n") + android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep", + flatlist, "libbar(minSdkVersion:(no version)) (external)") + android.AssertStringListDoesNotContain(t, "do not track if not available for platform", + flatlist, "mylib:(minSdkVersion:29)") + android.AssertStringListContains(t, "track platform-available lib", + flatlist, "yourlib(minSdkVersion:29)") +} + func TestTrackAllowedDeps_SkipWithoutAllowedDepsTxt(t *testing.T) { ctx := testApex(t, ` apex { @@ -3685,7 +3830,7 @@ func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName, var } func ensureExactDeapexedContents(t *testing.T, ctx *android.TestContext, moduleName string, variant string, files []string) { - deapexer := ctx.ModuleForTests(moduleName+".deapexer", variant).Description("deapex") + deapexer := ctx.ModuleForTests(moduleName, variant).Description("deapex") outputs := make([]string, 0, len(deapexer.ImplicitOutputs)+1) if deapexer.Output != nil { outputs = append(outputs, deapexer.Output.String()) @@ -4885,236 +5030,6 @@ type moduleErrorfTestCtx struct { func (ctx moduleErrorfTestCtx) ModuleErrorf(format string, args ...interface{}) { } -// 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) { - transform := android.NullFixturePreparer - - checkDexJarBuildPath := func(t *testing.T, ctx *android.TestContext, name string) { - t.Helper() - // Make sure the import has been given the correct path to the dex jar. - p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency) - dexJarBuildPath := p.DexJarBuildPath(moduleErrorfTestCtx{}).PathOrNil() - stem := android.RemoveOptionalPrebuiltPrefix(name) - android.AssertStringEquals(t, "DexJarBuildPath should be apex-related path.", - ".intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/"+stem+".jar", - android.NormalizePathForTesting(dexJarBuildPath)) - } - - checkDexJarInstallPath := func(t *testing.T, ctx *android.TestContext, name string) { - t.Helper() - // Make sure the import has been given the correct path to the dex jar. - p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency) - dexJarBuildPath := p.DexJarInstallPath() - stem := android.RemoveOptionalPrebuiltPrefix(name) - android.AssertStringEquals(t, "DexJarInstallPath should be apex-related path.", - "target/product/test_device/apex/myapex/javalib/"+stem+".jar", - android.NormalizePathForTesting(dexJarBuildPath)) - } - - ensureNoSourceVariant := func(t *testing.T, ctx *android.TestContext, name string) { - t.Helper() - // Make sure that an apex variant is not created for the source module. - android.AssertArrayString(t, "Check if there is no source variant", - []string{"android_common"}, - ctx.ModuleVariantsForTests(name)) - } - - t.Run("prebuilt only", func(t *testing.T) { - bp := ` - prebuilt_apex { - name: "myapex", - arch: { - arm64: { - src: "myapex-arm64.apex", - }, - arm: { - src: "myapex-arm.apex", - }, - }, - exported_java_libs: ["libfoo", "libbar"], - } - - java_import { - name: "libfoo", - jars: ["libfoo.jar"], - sdk_version: "core_current", - } - - java_sdk_library_import { - name: "libbar", - public: { - jars: ["libbar.jar"], - }, - } - ` - - // Make sure that dexpreopt can access dex implementation files from the prebuilt. - ctx := testDexpreoptWithApexes(t, bp, "", transform) - - deapexerName := deapexerModuleName("prebuilt_myapex") - android.AssertStringEquals(t, "APEX module name from deapexer name", "prebuilt_myapex", apexModuleName(deapexerName)) - - // Make sure that the deapexer has the correct input APEX. - deapexer := ctx.ModuleForTests(deapexerName, "android_common") - rule := deapexer.Rule("deapexer") - if expected, actual := []string{"myapex-arm64.apex"}, android.NormalizePathsForTesting(rule.Implicits); !reflect.DeepEqual(expected, actual) { - t.Errorf("expected: %q, found: %q", expected, actual) - } - - // Make sure that the prebuilt_apex has the correct input APEX. - prebuiltApex := ctx.ModuleForTests("myapex", "android_common_myapex") - rule = prebuiltApex.Rule("android/soong/android.Cp") - if expected, actual := "myapex-arm64.apex", android.NormalizePathForTesting(rule.Input); !reflect.DeepEqual(expected, actual) { - t.Errorf("expected: %q, found: %q", expected, actual) - } - - checkDexJarBuildPath(t, ctx, "libfoo") - checkDexJarInstallPath(t, ctx, "libfoo") - - checkDexJarBuildPath(t, ctx, "libbar") - checkDexJarInstallPath(t, ctx, "libbar") - }) - - t.Run("prebuilt with source preferred", func(t *testing.T) { - - bp := ` - apex { - name: "myapex", - key: "myapex.key", - updatable: false, - java_libs: [ - "libfoo", - "libbar", - ], - } - - apex_key { - name: "myapex.key", - public_key: "testkey.avbpubkey", - private_key: "testkey.pem", - } - - prebuilt_apex { - name: "myapex", - arch: { - arm64: { - src: "myapex-arm64.apex", - }, - arm: { - src: "myapex-arm.apex", - }, - }, - exported_java_libs: ["libfoo", "libbar"], - } - - java_import { - name: "libfoo", - jars: ["libfoo.jar"], - apex_available: [ - "myapex", - ], - compile_dex: true, - sdk_version: "core_current", - } - - java_library { - name: "libfoo", - srcs: ["foo/bar/MyClass.java"], - apex_available: [ - "myapex", - ], - compile_dex: true, - sdk_version: "core_current", - } - - java_sdk_library_import { - name: "libbar", - public: { - jars: ["libbar.jar"], - }, - apex_available: [ - "myapex", - ], - compile_dex: true, - } - - java_sdk_library { - name: "libbar", - srcs: ["foo/bar/MyClass.java"], - unsafe_ignore_missing_latest_api: true, - apex_available: [ - "myapex", - ], - compile_dex: true, - sdk_version: "core_current", - } - ` - - // Make sure that dexpreopt can access dex implementation files from the prebuilt. - ctx := testDexpreoptWithApexes(t, bp, "", transform) - - checkDexJarBuildPath(t, ctx, "prebuilt_libfoo") - checkDexJarInstallPath(t, ctx, "prebuilt_libfoo") - - checkDexJarBuildPath(t, ctx, "prebuilt_libbar") - checkDexJarInstallPath(t, ctx, "prebuilt_libbar") - }) - - t.Run("prebuilt preferred with source", func(t *testing.T) { - bp := ` - prebuilt_apex { - name: "myapex", - arch: { - arm64: { - src: "myapex-arm64.apex", - }, - arm: { - src: "myapex-arm.apex", - }, - }, - exported_java_libs: ["libfoo", "libbar"], - } - - java_import { - name: "libfoo", - prefer: true, - jars: ["libfoo.jar"], - } - - java_library { - name: "libfoo", - sdk_version: "core_current", - } - - java_sdk_library_import { - name: "libbar", - prefer: true, - public: { - jars: ["libbar.jar"], - }, - } - - java_sdk_library { - name: "libbar", - srcs: ["foo/bar/MyClass.java"], - unsafe_ignore_missing_latest_api: true, - } - ` - - // Make sure that dexpreopt can access dex implementation files from the prebuilt. - ctx := testDexpreoptWithApexes(t, bp, "", transform) - - checkDexJarBuildPath(t, ctx, "prebuilt_libfoo") - checkDexJarInstallPath(t, ctx, "prebuilt_libfoo") - ensureNoSourceVariant(t, ctx, "libfoo") - - checkDexJarBuildPath(t, ctx, "prebuilt_libbar") - checkDexJarInstallPath(t, ctx, "prebuilt_libbar") - ensureNoSourceVariant(t, ctx, "libbar") - }) -} - func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { preparer := android.GroupFixturePreparers( java.FixtureConfigureApexBootJars("myapex:libfoo", "myapex:libbar"), @@ -5137,23 +5052,6 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { }), ) - checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, stem string, bootDexJarPath string) { - t.Helper() - s := ctx.ModuleForTests("dex_bootjars", "android_common") - foundLibfooJar := false - base := stem + ".jar" - for _, output := range s.AllOutputs() { - if filepath.Base(output) == base { - foundLibfooJar = true - buildRule := s.Output(output) - android.AssertStringEquals(t, "boot dex jar path", bootDexJarPath, buildRule.Input.String()) - } - } - if !foundLibfooJar { - t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs %q", android.StringPathsRelativeToTop(ctx.Config().SoongOutDir(), s.AllOutputs())) - } - } - checkHiddenAPIIndexFromClassesInputs := func(t *testing.T, ctx *android.TestContext, expectedIntermediateInputs string) { t.Helper() platformBootclasspath := ctx.ModuleForTests("platform-bootclasspath", "android_common") @@ -5206,12 +5104,14 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { }, } - java_import { + java_sdk_library_import { name: "libfoo", - jars: ["libfoo.jar"], + public: { + jars: ["libfoo.jar"], + }, apex_available: ["myapex"], + shared_library: false, permitted_packages: ["foo"], - sdk_version: "core_current", } java_sdk_library_import { @@ -5226,8 +5126,6 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { ` ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment) - checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") - checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar") // Verify the correct module jars contribute to the hiddenapi index file. checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`) @@ -5243,18 +5141,10 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { apex_set { name: "myapex", set: "myapex.apks", - exported_java_libs: ["myjavalib"], exported_bootclasspath_fragments: ["my-bootclasspath-fragment"], exported_systemserverclasspath_fragments: ["my-systemserverclasspath-fragment"], } - java_import { - name: "myjavalib", - jars: ["myjavalib.jar"], - apex_available: ["myapex"], - permitted_packages: ["javalib"], - } - prebuilt_bootclasspath_fragment { name: "my-bootclasspath-fragment", contents: ["libfoo", "libbar"], @@ -5275,13 +5165,17 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { apex_available: ["myapex"], } - java_import { + java_sdk_library_import { name: "libfoo", - jars: ["libfoo.jar"], + public: { + jars: ["libfoo.jar"], + }, apex_available: ["myapex"], - permitted_packages: ["foo"], + shared_library: false, + permitted_packages: ["libfoo"], } + java_sdk_library_import { name: "libbar", public: { @@ -5304,8 +5198,6 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { ` ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment) - checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") - checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar") // Verify the correct module jars contribute to the hiddenapi index file. checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`) @@ -5459,13 +5351,15 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { }, } - java_import { + java_sdk_library_import { name: "libfoo", prefer: true, - jars: ["libfoo.jar"], + public: { + jars: ["libfoo.jar"], + }, apex_available: ["myapex"], - permitted_packages: ["foo"], - sdk_version: "core_current", + shared_library: false, + permitted_packages: ["libfoo"], } java_library { @@ -5497,8 +5391,6 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { ` ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment) - checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") - checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar") // Verify the correct module jars contribute to the hiddenapi index file. checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`) @@ -5597,8 +5489,6 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { ` ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment) - checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/hiddenapi-modular/encoded/libfoo.jar") - checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/hiddenapi-modular/encoded/libbar.jar") // Verify the correct module jars contribute to the hiddenapi index file. checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`) @@ -5684,11 +5574,11 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { apex_available: ["myapex"], shared_library: false, permitted_packages: ["bar"], + prefer: true, } java_sdk_library { name: "libbar", - enabled: false, srcs: ["foo/bar/MyClass.java"], unsafe_ignore_missing_latest_api: true, apex_available: ["myapex"], @@ -5705,13 +5595,11 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { android.PrepareForTestWithAllowMissingDependencies, android.FixtureMergeMockFs(map[string][]byte{ "build/soong/scripts/check_boot_jars/package_allowed_list.txt": nil, - "frameworks/base/config/boot-profile.txt": nil, + "frameworks/base/boot/boot-profile.txt": nil, }), ) ctx := testDexpreoptWithApexes(t, bp, "", preparer2, fragment) - checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") - checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar") // Verify the correct module jars contribute to the hiddenapi index file. checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`) @@ -6270,6 +6158,87 @@ func TestApexAvailable_DirectDep(t *testing.T) { system_shared_libs: [], apex_available: ["otherapex"], }`) + + // 'apex_available' check is bypassed for /product apex with a specific prefix. + // TODO: b/352818241 - Remove below two cases after APEX availability is enforced for /product APEXes. + testApex(t, ` + apex { + name: "com.sdv.myapex", + key: "myapex.key", + native_shared_libs: ["libfoo"], + updatable: false, + product_specific: true, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + apex { + name: "com.any.otherapex", + key: "otherapex.key", + native_shared_libs: ["libfoo"], + updatable: false, + } + + apex_key { + name: "otherapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "libfoo", + stl: "none", + system_shared_libs: [], + apex_available: ["com.any.otherapex"], + product_specific: true, + }`, + android.FixtureMergeMockFs(android.MockFS{ + "system/sepolicy/apex/com.sdv.myapex-file_contexts": nil, + "system/sepolicy/apex/com.any.otherapex-file_contexts": nil, + })) + + // 'apex_available' check is not bypassed for non-product apex with a specific prefix. + testApexError(t, "requires \"libfoo\" that doesn't list the APEX under 'apex_available'.", ` + apex { + name: "com.sdv.myapex", + key: "myapex.key", + native_shared_libs: ["libfoo"], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + apex { + name: "com.any.otherapex", + key: "otherapex.key", + native_shared_libs: ["libfoo"], + updatable: false, + } + + apex_key { + name: "otherapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "libfoo", + stl: "none", + system_shared_libs: [], + apex_available: ["com.any.otherapex"], + }`, + android.FixtureMergeMockFs(android.MockFS{ + "system/sepolicy/apex/com.sdv.myapex-file_contexts": nil, + "system/sepolicy/apex/com.any.otherapex-file_contexts": nil, + })) } func TestApexAvailable_IndirectDep(t *testing.T) { @@ -6315,6 +6284,91 @@ func TestApexAvailable_IndirectDep(t *testing.T) { stl: "none", system_shared_libs: [], }`) + + // 'apex_available' check is bypassed for /product apex with a specific prefix. + // TODO: b/352818241 - Remove below two cases after APEX availability is enforced for /product APEXes. + testApex(t, ` + apex { + name: "com.sdv.myapex", + key: "myapex.key", + native_shared_libs: ["libfoo"], + updatable: false, + product_specific: true, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "libfoo", + stl: "none", + shared_libs: ["libbar"], + system_shared_libs: [], + apex_available: ["com.sdv.myapex"], + product_specific: true, + } + + cc_library { + name: "libbar", + stl: "none", + shared_libs: ["libbaz"], + system_shared_libs: [], + apex_available: ["com.sdv.myapex"], + product_specific: true, + } + + cc_library { + name: "libbaz", + stl: "none", + system_shared_libs: [], + product_specific: true, + }`, + android.FixtureMergeMockFs(android.MockFS{ + "system/sepolicy/apex/com.sdv.myapex-file_contexts": nil, + })) + + // 'apex_available' check is not bypassed for non-product apex with a specific prefix. + testApexError(t, `requires "libbaz" that doesn't list the APEX under 'apex_available'.`, ` + apex { + name: "com.sdv.myapex", + key: "myapex.key", + native_shared_libs: ["libfoo"], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "libfoo", + stl: "none", + shared_libs: ["libbar"], + system_shared_libs: [], + apex_available: ["com.sdv.myapex"], + } + + cc_library { + name: "libbar", + stl: "none", + shared_libs: ["libbaz"], + system_shared_libs: [], + apex_available: ["com.sdv.myapex"], + } + + cc_library { + name: "libbaz", + stl: "none", + system_shared_libs: [], + }`, + android.FixtureMergeMockFs(android.MockFS{ + "system/sepolicy/apex/com.sdv.myapex-file_contexts": nil, + })) } func TestApexAvailable_IndirectStaticDep(t *testing.T) { @@ -6516,14 +6570,14 @@ func TestApexAvailable_ApexAvailableNameWithVersionCode(t *testing.T) { `) fooManifestRule := result.ModuleForTests("foo", "android_common_foo").Rule("apexManifestRule") - fooExpectedDefaultVersion := android.DefaultUpdatableModuleVersion + fooExpectedDefaultVersion := testDefaultUpdatableModuleVersion fooActualDefaultVersion := fooManifestRule.Args["default_version"] if fooActualDefaultVersion != fooExpectedDefaultVersion { t.Errorf("expected to find defaultVersion %q; got %q", fooExpectedDefaultVersion, fooActualDefaultVersion) } barManifestRule := result.ModuleForTests("bar", "android_common_bar").Rule("apexManifestRule") - defaultVersionInt, _ := strconv.Atoi(android.DefaultUpdatableModuleVersion) + defaultVersionInt, _ := strconv.Atoi(testDefaultUpdatableModuleVersion) barExpectedDefaultVersion := fmt.Sprint(defaultVersionInt + 3) barActualDefaultVersion := barManifestRule.Args["default_version"] if barActualDefaultVersion != barExpectedDefaultVersion { @@ -7338,7 +7392,7 @@ func TestJavaSDKLibrary_WithinApex(t *testing.T) { java_library { name: "bar", srcs: ["a.java"], - libs: ["foo"], + libs: ["foo.impl"], apex_available: ["myapex"], sdk_version: "none", system_modules: "none", @@ -7391,7 +7445,7 @@ func TestJavaSDKLibrary_CrossBoundary(t *testing.T) { java_library { name: "bar", srcs: ["a.java"], - libs: ["foo"], + libs: ["foo.stubs"], sdk_version: "none", system_modules: "none", } @@ -7445,7 +7499,7 @@ func TestJavaSDKLibrary_ImportPreferred(t *testing.T) { java_library { name: "bar", srcs: ["a.java"], - libs: ["foo"], + libs: ["foo.impl"], apex_available: ["myapex"], sdk_version: "none", system_modules: "none", @@ -8118,9 +8172,9 @@ func TestAppSetBundlePrebuilt(t *testing.T) { ctx := testApex(t, bp, prepareForTestWithSantitizeHwaddress) // Check that the extractor produces the correct output file from the correct input file. - extractorOutput := "out/soong/.intermediates/prebuilt_myapex.apex.extractor/android_common/extracted/myapex.hwasan.apks" + extractorOutput := "out/soong/.intermediates/myapex/android_common_myapex/extracted/myapex.hwasan.apks" - m := ctx.ModuleForTests("prebuilt_myapex.apex.extractor", "android_common") + m := ctx.ModuleForTests("myapex", "android_common_myapex") extractedApex := m.Output(extractorOutput) android.AssertArrayString(t, "extractor input", []string{"myapex.hwasan.apks"}, extractedApex.Inputs.Strings()) @@ -8145,10 +8199,10 @@ func TestApexSetApksModuleAssignment(t *testing.T) { } `) - m := ctx.ModuleForTests("prebuilt_myapex.apex.extractor", "android_common") + m := ctx.ModuleForTests("myapex", "android_common_myapex") // Check that the extractor produces the correct apks file from the input module - extractorOutput := "out/soong/.intermediates/prebuilt_myapex.apex.extractor/android_common/extracted/myapex.apks" + extractorOutput := "out/soong/.intermediates/myapex/android_common_myapex/extracted/myapex.apks" extractedApex := m.Output(extractorOutput) android.AssertArrayString(t, "extractor input", []string{"myapex.apks"}, extractedApex.Inputs.Strings()) @@ -8210,189 +8264,6 @@ func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, preparer android.F return result.TestContext } -func TestDuplicateDeapexersFromPrebuiltApexes(t *testing.T) { - preparers := android.GroupFixturePreparers( - java.PrepareForTestWithJavaDefaultModules, - prepareForTestWithBootclasspathFragment, - dexpreopt.FixtureSetTestOnlyArtBootImageJars("com.android.art:libfoo"), - PrepareForTestWithApexBuildComponents, - ). - ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern( - "Multiple installable prebuilt APEXes provide ambiguous deapexers: prebuilt_com.android.art and prebuilt_com.mycompany.android.art")) - - bpBase := ` - apex_set { - name: "com.android.art", - installable: true, - exported_bootclasspath_fragments: ["art-bootclasspath-fragment"], - set: "myapex.apks", - } - - apex_set { - name: "com.mycompany.android.art", - apex_name: "com.android.art", - installable: true, - exported_bootclasspath_fragments: ["art-bootclasspath-fragment"], - set: "company-myapex.apks", - } - - prebuilt_bootclasspath_fragment { - name: "art-bootclasspath-fragment", - apex_available: ["com.android.art"], - hidden_api: { - annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv", - metadata: "my-bootclasspath-fragment/metadata.csv", - index: "my-bootclasspath-fragment/index.csv", - stub_flags: "my-bootclasspath-fragment/stub-flags.csv", - all_flags: "my-bootclasspath-fragment/all-flags.csv", - }, - %s - } - ` - - t.Run("java_import", func(t *testing.T) { - _ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+` - java_import { - name: "libfoo", - jars: ["libfoo.jar"], - apex_available: ["com.android.art"], - } - `) - }) - - t.Run("java_sdk_library_import", func(t *testing.T) { - _ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+` - java_sdk_library_import { - name: "libfoo", - public: { - jars: ["libbar.jar"], - }, - shared_library: false, - apex_available: ["com.android.art"], - } - `) - }) - - t.Run("prebuilt_bootclasspath_fragment", func(t *testing.T) { - _ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, ` - image_name: "art", - contents: ["libfoo"], - `)+` - java_sdk_library_import { - name: "libfoo", - public: { - jars: ["libbar.jar"], - }, - shared_library: false, - apex_available: ["com.android.art"], - } - `) - }) -} - -func TestDuplicateButEquivalentDeapexersFromPrebuiltApexes(t *testing.T) { - preparers := android.GroupFixturePreparers( - java.PrepareForTestWithJavaDefaultModules, - PrepareForTestWithApexBuildComponents, - ) - - errCtx := moduleErrorfTestCtx{} - - bpBase := ` - apex_set { - name: "com.android.myapex", - installable: true, - exported_bootclasspath_fragments: ["my-bootclasspath-fragment"], - set: "myapex.apks", - } - - apex_set { - name: "com.android.myapex_compressed", - apex_name: "com.android.myapex", - installable: true, - exported_bootclasspath_fragments: ["my-bootclasspath-fragment"], - set: "myapex_compressed.apks", - } - - prebuilt_bootclasspath_fragment { - name: "my-bootclasspath-fragment", - apex_available: [ - "com.android.myapex", - "com.android.myapex_compressed", - ], - hidden_api: { - annotation_flags: "annotation-flags.csv", - metadata: "metadata.csv", - index: "index.csv", - signature_patterns: "signature_patterns.csv", - }, - %s - } - ` - - t.Run("java_import", func(t *testing.T) { - result := preparers.RunTestWithBp(t, - fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+` - java_import { - name: "libfoo", - jars: ["libfoo.jar"], - apex_available: [ - "com.android.myapex", - "com.android.myapex_compressed", - ], - } - `) - - module := result.Module("libfoo", "android_common_com.android.myapex") - usesLibraryDep := module.(java.UsesLibraryDependency) - android.AssertPathRelativeToTopEquals(t, "dex jar path", - "out/soong/.intermediates/prebuilt_com.android.myapex.deapexer/android_common/deapexer/javalib/libfoo.jar", - usesLibraryDep.DexJarBuildPath(errCtx).Path()) - }) - - t.Run("java_sdk_library_import", func(t *testing.T) { - result := preparers.RunTestWithBp(t, - fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+` - java_sdk_library_import { - name: "libfoo", - public: { - jars: ["libbar.jar"], - }, - apex_available: [ - "com.android.myapex", - "com.android.myapex_compressed", - ], - compile_dex: true, - } - `) - - module := result.Module("libfoo", "android_common_com.android.myapex") - usesLibraryDep := module.(java.UsesLibraryDependency) - android.AssertPathRelativeToTopEquals(t, "dex jar path", - "out/soong/.intermediates/prebuilt_com.android.myapex.deapexer/android_common/deapexer/javalib/libfoo.jar", - usesLibraryDep.DexJarBuildPath(errCtx).Path()) - }) - - t.Run("prebuilt_bootclasspath_fragment", func(t *testing.T) { - _ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, ` - image_name: "art", - contents: ["libfoo"], - `)+` - java_sdk_library_import { - name: "libfoo", - public: { - jars: ["libbar.jar"], - }, - apex_available: [ - "com.android.myapex", - "com.android.myapex_compressed", - ], - compile_dex: true, - } - `) - }) -} - func TestUpdatable_should_set_min_sdk_version(t *testing.T) { testApexError(t, `"myapex" .*: updatable: updatable APEXes should set min_sdk_version`, ` apex { @@ -8504,12 +8375,16 @@ func TestDexpreoptAccessDexFilesFromPrebuiltApex(t *testing.T) { }, } - java_import { - name: "libfoo", + java_sdk_library_import { + name: "libfoo", + prefer: true, + public: { jars: ["libfoo.jar"], - apex_available: ["myapex"], - permitted_packages: ["libfoo"], - } + }, + apex_available: ["myapex"], + shared_library: false, + permitted_packages: ["libfoo"], + } `, "", preparer, fragment) }) } @@ -8889,7 +8764,7 @@ func TestApexSet(t *testing.T) { }), ) - m := ctx.ModuleForTests("prebuilt_myapex.apex.extractor", "android_common") + m := ctx.ModuleForTests("myapex", "android_common_myapex") // Check extract_apks tool parameters. extractedApex := m.Output("extracted/myapex.apks") @@ -8930,7 +8805,7 @@ func TestApexSet_NativeBridge(t *testing.T) { }), ) - m := ctx.ModuleForTests("prebuilt_myapex.apex.extractor", "android_common") + m := ctx.ModuleForTests("myapex", "android_common_myapex") // Check extract_apks tool parameters. No native bridge arch expected extractedApex := m.Output("extracted/myapex.apks") @@ -9617,42 +9492,6 @@ func TestAndroidMk_DexpreoptBuiltInstalledForApex(t *testing.T) { ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := foo.myapex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.odex foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.vdex\n") } -func TestAndroidMk_DexpreoptBuiltInstalledForApex_Prebuilt(t *testing.T) { - ctx := testApex(t, ` - prebuilt_apex { - name: "myapex", - arch: { - arm64: { - src: "myapex-arm64.apex", - }, - arm: { - src: "myapex-arm.apex", - }, - }, - exported_java_libs: ["foo"], - } - - java_import { - name: "foo", - jars: ["foo.jar"], - apex_available: ["myapex"], - } - `, - dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"), - ) - - prebuilt := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*Prebuilt) - entriesList := android.AndroidMkEntriesForTest(t, ctx, prebuilt) - mainModuleEntries := entriesList[0] - android.AssertArrayString(t, - "LOCAL_REQUIRED_MODULES", - mainModuleEntries.EntryMap["LOCAL_REQUIRED_MODULES"], - []string{ - "foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.odex", - "foo-dexpreopt-arm64-apex@myapex@javalib@foo.jar@classes.vdex", - }) -} - func TestAndroidMk_RequiredModules(t *testing.T) { ctx := testApex(t, ` apex { @@ -9998,120 +9837,84 @@ func TestApexStrictUpdtabilityLint(t *testing.T) { } testCases := []struct { - testCaseName string - apexUpdatable bool - javaStrictUpdtabilityLint bool - lintFileExists bool - disallowedFlagExpected bool + testCaseName string + apexUpdatable bool + javaStrictUpdtabilityLint bool + lintFileExists bool + disallowedFlagExpectedOnApex bool + disallowedFlagExpectedOnJavalib bool }{ { - testCaseName: "lint-baseline.xml does not exist, no disallowed flag necessary in lint cmd", - apexUpdatable: true, - javaStrictUpdtabilityLint: true, - lintFileExists: false, - disallowedFlagExpected: false, + testCaseName: "lint-baseline.xml does not exist, no disallowed flag necessary in lint cmd", + apexUpdatable: true, + javaStrictUpdtabilityLint: true, + lintFileExists: false, + disallowedFlagExpectedOnApex: false, + disallowedFlagExpectedOnJavalib: false, }, { - testCaseName: "non-updatable apex respects strict_updatability of javalib", - apexUpdatable: false, - javaStrictUpdtabilityLint: false, - lintFileExists: true, - disallowedFlagExpected: false, + testCaseName: "non-updatable apex respects strict_updatability of javalib", + apexUpdatable: false, + javaStrictUpdtabilityLint: false, + lintFileExists: true, + disallowedFlagExpectedOnApex: false, + disallowedFlagExpectedOnJavalib: false, }, { - testCaseName: "non-updatable apex respects strict updatability of javalib", - apexUpdatable: false, - javaStrictUpdtabilityLint: true, - lintFileExists: true, - disallowedFlagExpected: true, + testCaseName: "non-updatable apex respects strict updatability of javalib", + apexUpdatable: false, + javaStrictUpdtabilityLint: true, + lintFileExists: true, + disallowedFlagExpectedOnApex: false, + disallowedFlagExpectedOnJavalib: true, }, { - testCaseName: "updatable apex sets strict updatability of javalib to true", - apexUpdatable: true, - javaStrictUpdtabilityLint: false, // will be set to true by mutator - lintFileExists: true, - disallowedFlagExpected: true, + testCaseName: "updatable apex checks strict updatability of javalib", + apexUpdatable: true, + javaStrictUpdtabilityLint: false, + lintFileExists: true, + disallowedFlagExpectedOnApex: true, + disallowedFlagExpectedOnJavalib: false, }, } for _, testCase := range testCases { - fixtures := []android.FixturePreparer{} - baselineProperty := "" - if testCase.lintFileExists { - fixtures = append(fixtures, fs.AddToFixture()) - baselineProperty = "baseline_filename: \"lint-baseline.xml\"" - } - bp := fmt.Sprintf(bpTemplate, testCase.apexUpdatable, testCase.javaStrictUpdtabilityLint, baselineProperty) - - result := testApex(t, bp, fixtures...) - myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29") - sboxProto := android.RuleBuilderSboxProtoForTests(t, result, myjavalib.Output("lint.sbox.textproto")) - disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi") + t.Run(testCase.testCaseName, func(t *testing.T) { + fixtures := []android.FixturePreparer{} + baselineProperty := "" + if testCase.lintFileExists { + fixtures = append(fixtures, fs.AddToFixture()) + baselineProperty = "baseline_filename: \"lint-baseline.xml\"" + } + bp := fmt.Sprintf(bpTemplate, testCase.apexUpdatable, testCase.javaStrictUpdtabilityLint, baselineProperty) - if disallowedFlagActual != testCase.disallowedFlagExpected { - t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command) - } - } -} + result := testApex(t, bp, fixtures...) -func TestUpdatabilityLintSkipLibcore(t *testing.T) { - bp := ` - apex { - name: "myapex", - key: "myapex.key", - java_libs: ["myjavalib"], - updatable: true, - min_sdk_version: "29", - } - apex_key { - name: "myapex.key", - } - java_library { - name: "myjavalib", - srcs: ["MyClass.java"], - apex_available: [ "myapex" ], - sdk_version: "current", - min_sdk_version: "29", - lint: { - baseline_filename: "lint-baseline.xml", + checkModule := func(m android.TestingBuildParams, name string, expectStrictUpdatability bool) { + if expectStrictUpdatability { + if m.Rule == nil { + t.Errorf("expected strict updatability check rule on %s", name) + } else { + android.AssertStringDoesContain(t, fmt.Sprintf("strict updatability check rule for %s", name), + m.RuleParams.Command, "--disallowed_issues NewApi") + android.AssertStringListContains(t, fmt.Sprintf("strict updatability check baselines for %s", name), + m.Inputs.Strings(), "lint-baseline.xml") + } + } else { + if m.Rule != nil { + t.Errorf("expected no strict updatability check rule on %s", name) + } + } } - } - ` - testCases := []struct { - testCaseName string - moduleDirectory string - disallowedFlagExpected bool - }{ - { - testCaseName: "lintable module defined outside libcore", - moduleDirectory: "", - disallowedFlagExpected: true, - }, - { - testCaseName: "lintable module defined in libcore root directory", - moduleDirectory: "libcore/", - disallowedFlagExpected: false, - }, - { - testCaseName: "lintable module defined in libcore child directory", - moduleDirectory: "libcore/childdir/", - disallowedFlagExpected: true, - }, - } + myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29") + apex := result.ModuleForTests("myapex", "android_common_myapex") + apexStrictUpdatabilityCheck := apex.MaybeOutput("lint_strict_updatability_check.stamp") + javalibStrictUpdatabilityCheck := myjavalib.MaybeOutput("lint_strict_updatability_check.stamp") - for _, testCase := range testCases { - lintFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"lint-baseline.xml", "") - bpFileCreator := android.FixtureAddTextFile(testCase.moduleDirectory+"Android.bp", bp) - result := testApex(t, "", lintFileCreator, bpFileCreator) - myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29") - sboxProto := android.RuleBuilderSboxProtoForTests(t, result, myjavalib.Output("lint.sbox.textproto")) - cmdFlags := fmt.Sprintf("--baseline %vlint-baseline.xml --disallowed_issues NewApi", testCase.moduleDirectory) - disallowedFlagActual := strings.Contains(*sboxProto.Commands[0].Command, cmdFlags) - - if disallowedFlagActual != testCase.disallowedFlagExpected { - t.Errorf("Failed testcase: %v \nActual lint cmd: %v", testCase.testCaseName, *sboxProto.Commands[0].Command) - } + checkModule(apexStrictUpdatabilityCheck, "myapex", testCase.disallowedFlagExpectedOnApex) + checkModule(javalibStrictUpdatabilityCheck, "myjavalib", testCase.disallowedFlagExpectedOnJavalib) + }) } } @@ -10153,11 +9956,12 @@ func TestApexStrictUpdtabilityLintBcpFragmentDeps(t *testing.T) { } result := testApex(t, bp, dexpreopt.FixtureSetApexBootJars("myapex:myjavalib"), fs.AddToFixture()) - myjavalib := result.ModuleForTests("myjavalib", "android_common_apex29") - sboxProto := android.RuleBuilderSboxProtoForTests(t, result, myjavalib.Output("lint.sbox.textproto")) - if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml --disallowed_issues NewApi") { - t.Errorf("Strict updabality lint missing in myjavalib coming from bootclasspath_fragment mybootclasspath-fragment\nActual lint cmd: %v", *sboxProto.Commands[0].Command) - } + apex := result.ModuleForTests("myapex", "android_common_myapex") + apexStrictUpdatabilityCheck := apex.Output("lint_strict_updatability_check.stamp") + android.AssertStringDoesContain(t, "strict updatability check rule for myapex", + apexStrictUpdatabilityCheck.RuleParams.Command, "--disallowed_issues NewApi") + android.AssertStringListContains(t, "strict updatability check baselines for myapex", + apexStrictUpdatabilityCheck.Inputs.Strings(), "lint-baseline.xml") } func TestApexLintBcpFragmentSdkLibDeps(t *testing.T) { @@ -10273,208 +10077,6 @@ func TestUpdatableApexEnforcesAppUpdatability(t *testing.T) { } } -func TestApexBuildsAgainstApiSurfaceStubLibraries(t *testing.T) { - bp := ` - apex { - name: "myapex", - key: "myapex.key", - native_shared_libs: ["libbaz"], - binaries: ["binfoo"], - min_sdk_version: "29", - } - apex_key { - name: "myapex.key", - } - cc_binary { - name: "binfoo", - shared_libs: ["libbar", "libbaz", "libqux",], - apex_available: ["myapex"], - min_sdk_version: "29", - recovery_available: false, - } - cc_library { - name: "libbar", - srcs: ["libbar.cc"], - stubs: { - symbol_file: "libbar.map.txt", - versions: [ - "29", - ], - }, - } - cc_library { - name: "libbaz", - srcs: ["libbaz.cc"], - apex_available: ["myapex"], - min_sdk_version: "29", - stubs: { - symbol_file: "libbaz.map.txt", - versions: [ - "29", - ], - }, - } - cc_api_library { - name: "libbar", - src: "libbar_stub.so", - min_sdk_version: "29", - variants: ["apex.29"], - } - cc_api_variant { - name: "libbar", - variant: "apex", - version: "29", - src: "libbar_apex_29.so", - } - cc_api_library { - name: "libbaz", - src: "libbaz_stub.so", - min_sdk_version: "29", - variants: ["apex.29"], - } - cc_api_variant { - name: "libbaz", - variant: "apex", - version: "29", - src: "libbaz_apex_29.so", - } - cc_api_library { - name: "libqux", - src: "libqux_stub.so", - min_sdk_version: "29", - variants: ["apex.29"], - } - cc_api_variant { - name: "libqux", - variant: "apex", - version: "29", - src: "libqux_apex_29.so", - } - api_imports { - name: "api_imports", - apex_shared_libs: [ - "libbar", - "libbaz", - "libqux", - ], - } - ` - result := testApex(t, bp) - - hasDep := func(m android.Module, wantDep android.Module) bool { - t.Helper() - var found bool - result.VisitDirectDeps(m, func(dep blueprint.Module) { - if dep == wantDep { - found = true - } - }) - return found - } - - // Library defines stubs and cc_api_library should be used with cc_api_library - binfooApexVariant := result.ModuleForTests("binfoo", "android_arm64_armv8-a_apex29").Module() - libbarCoreVariant := result.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module() - libbarApiImportCoreVariant := result.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module() - - android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries", true, hasDep(binfooApexVariant, libbarApiImportCoreVariant)) - android.AssertBoolEquals(t, "apex variant should link against original library if exists", true, hasDep(binfooApexVariant, libbarCoreVariant)) - - binFooCFlags := result.ModuleForTests("binfoo", "android_arm64_armv8-a_apex29").Rule("ld").Args["libFlags"] - android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libbar.apex.29.apiimport.so") - android.AssertStringDoesNotContain(t, "binfoo should not link against cc_api_library itself", binFooCFlags, "libbar.apiimport.so") - android.AssertStringDoesNotContain(t, "binfoo should not link against original definition", binFooCFlags, "libbar.so") - - // Library defined in the same APEX should be linked with original definition instead of cc_api_library - libbazApexVariant := result.ModuleForTests("libbaz", "android_arm64_armv8-a_shared_apex29").Module() - libbazApiImportCoreVariant := result.ModuleForTests("libbaz.apiimport", "android_arm64_armv8-a_shared").Module() - android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries even from same APEX", true, hasDep(binfooApexVariant, libbazApiImportCoreVariant)) - android.AssertBoolEquals(t, "apex variant should link against original library if exists", true, hasDep(binfooApexVariant, libbazApexVariant)) - - android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libbaz.so") - android.AssertStringDoesNotContain(t, "binfoo should not link against cc_api_library itself", binFooCFlags, "libbaz.apiimport.so") - android.AssertStringDoesNotContain(t, "binfoo should not link against original definition", binFooCFlags, "libbaz.apex.29.apiimport.so") - - // cc_api_library defined without original library should be linked with cc_api_library - libquxApiImportApexVariant := result.ModuleForTests("libqux.apiimport", "android_arm64_armv8-a_shared").Module() - android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries even original library definition does not exist", true, hasDep(binfooApexVariant, libquxApiImportApexVariant)) - android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libqux.apex.29.apiimport.so") -} - -func TestPlatformBinaryBuildsAgainstApiSurfaceStubLibraries(t *testing.T) { - bp := ` - apex { - name: "myapex", - key: "myapex.key", - native_shared_libs: ["libbar"], - min_sdk_version: "29", - } - apex_key { - name: "myapex.key", - } - cc_binary { - name: "binfoo", - shared_libs: ["libbar"], - recovery_available: false, - } - cc_library { - name: "libbar", - srcs: ["libbar.cc"], - apex_available: ["myapex"], - min_sdk_version: "29", - stubs: { - symbol_file: "libbar.map.txt", - versions: [ - "29", - ], - }, - } - cc_api_library { - name: "libbar", - src: "libbar_stub.so", - variants: ["apex.29"], - } - cc_api_variant { - name: "libbar", - variant: "apex", - version: "29", - src: "libbar_apex_29.so", - } - api_imports { - name: "api_imports", - apex_shared_libs: [ - "libbar", - ], - } - ` - - result := testApex(t, bp) - - hasDep := func(m android.Module, wantDep android.Module) bool { - t.Helper() - var found bool - result.VisitDirectDeps(m, func(dep blueprint.Module) { - if dep == wantDep { - found = true - } - }) - return found - } - - // Library defines stubs and cc_api_library should be used with cc_api_library - binfooApexVariant := result.ModuleForTests("binfoo", "android_arm64_armv8-a").Module() - libbarCoreVariant := result.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module() - libbarApiImportCoreVariant := result.ModuleForTests("libbar.apiimport", "android_arm64_armv8-a_shared").Module() - - android.AssertBoolEquals(t, "apex variant should link against API surface stub libraries", true, hasDep(binfooApexVariant, libbarApiImportCoreVariant)) - android.AssertBoolEquals(t, "apex variant should link against original library if exists", true, hasDep(binfooApexVariant, libbarCoreVariant)) - - binFooCFlags := result.ModuleForTests("binfoo", "android_arm64_armv8-a").Rule("ld").Args["libFlags"] - android.AssertStringDoesContain(t, "binfoo should link against APEX variant", binFooCFlags, "libbar.apex.29.apiimport.so") - android.AssertStringDoesNotContain(t, "binfoo should not link against cc_api_library itself", binFooCFlags, "libbar.apiimport.so") - android.AssertStringDoesNotContain(t, "binfoo should not link against original definition", binFooCFlags, "libbar.so") -} - func TestTrimmedApex(t *testing.T) { bp := ` apex { @@ -10513,21 +10115,6 @@ func TestTrimmedApex(t *testing.T) { apex_available: ["myapex","mydcla"], min_sdk_version: "29", } - cc_api_library { - name: "libc", - src: "libc.so", - min_sdk_version: "29", - recovery_available: true, - vendor_available: true, - product_available: true, - } - api_imports { - name: "api_imports", - shared_libs: [ - "libc", - ], - header_libs: [], - } ` ctx := testApex(t, bp) module := ctx.ModuleForTests("myapex", "android_common_myapex") @@ -10799,14 +10386,15 @@ func TestAconfigFilesJavaDeps(t *testing.T) { mod := ctx.ModuleForTests("myapex", "android_common_myapex") s := mod.Rule("apexRule").Args["copy_commands"] copyCmds := regexp.MustCompile(" *&& *").Split(s, -1) - if len(copyCmds) != 12 { - t.Fatalf("Expected 12 commands, got %d in:\n%s", len(copyCmds), s) + if len(copyCmds) != 14 { + t.Fatalf("Expected 14 commands, got %d in:\n%s", len(copyCmds), s) } ensureListContainsMatch(t, copyCmds, "^cp -f .*/aconfig_flags.pb .*/image.apex/etc/aconfig_flags.pb") ensureListContainsMatch(t, copyCmds, "^cp -f .*/package.map .*/image.apex/etc/package.map") ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.map .*/image.apex/etc/flag.map") ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.val .*/image.apex/etc/flag.val") + ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.info.*/image.apex/etc/flag.info") inputs := []string{ "my_aconfig_declarations_foo/intermediate.pb", @@ -10816,6 +10404,7 @@ func TestAconfigFilesJavaDeps(t *testing.T) { VerifyAconfigRule(t, &mod, "create_aconfig_package_map_file", inputs, "android_common_myapex/package.map", "myapex", "package_map") VerifyAconfigRule(t, &mod, "create_aconfig_flag_map_file", inputs, "android_common_myapex/flag.map", "myapex", "flag_map") VerifyAconfigRule(t, &mod, "create_aconfig_flag_val_file", inputs, "android_common_myapex/flag.val", "myapex", "flag_val") + VerifyAconfigRule(t, &mod, "create_aconfig_flag_info_file", inputs, "android_common_myapex/flag.info", "myapex", "flag_info") } func TestAconfigFilesJavaAndCcDeps(t *testing.T) { @@ -10934,14 +10523,15 @@ func TestAconfigFilesJavaAndCcDeps(t *testing.T) { mod := ctx.ModuleForTests("myapex", "android_common_myapex") s := mod.Rule("apexRule").Args["copy_commands"] copyCmds := regexp.MustCompile(" *&& *").Split(s, -1) - if len(copyCmds) != 16 { - t.Fatalf("Expected 16 commands, got %d in:\n%s", len(copyCmds), s) + if len(copyCmds) != 18 { + t.Fatalf("Expected 18 commands, got %d in:\n%s", len(copyCmds), s) } ensureListContainsMatch(t, copyCmds, "^cp -f .*/aconfig_flags.pb .*/image.apex/etc/aconfig_flags.pb") ensureListContainsMatch(t, copyCmds, "^cp -f .*/package.map .*/image.apex/etc/package.map") ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.map .*/image.apex/etc/flag.map") ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.val .*/image.apex/etc/flag.val") + ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.info .*/image.apex/etc/flag.info") inputs := []string{ "my_aconfig_declarations_foo/intermediate.pb", @@ -10952,6 +10542,7 @@ func TestAconfigFilesJavaAndCcDeps(t *testing.T) { VerifyAconfigRule(t, &mod, "create_aconfig_package_map_file", inputs, "android_common_myapex/package.map", "myapex", "package_map") VerifyAconfigRule(t, &mod, "create_aconfig_flag_map_file", inputs, "android_common_myapex/flag.map", "myapex", "flag_map") VerifyAconfigRule(t, &mod, "create_aconfig_flag_val_file", inputs, "android_common_myapex/flag.val", "myapex", "flag_val") + VerifyAconfigRule(t, &mod, "create_aconfig_flag_info_file", inputs, "android_common_myapex/flag.info", "myapex", "flag_info") } func TestAconfigFilesRustDeps(t *testing.T) { @@ -11102,14 +10693,15 @@ func TestAconfigFilesRustDeps(t *testing.T) { mod := ctx.ModuleForTests("myapex", "android_common_myapex") s := mod.Rule("apexRule").Args["copy_commands"] copyCmds := regexp.MustCompile(" *&& *").Split(s, -1) - if len(copyCmds) != 36 { - t.Fatalf("Expected 36 commands, got %d in:\n%s", len(copyCmds), s) + if len(copyCmds) != 38 { + t.Fatalf("Expected 38 commands, got %d in:\n%s", len(copyCmds), s) } ensureListContainsMatch(t, copyCmds, "^cp -f .*/aconfig_flags.pb .*/image.apex/etc/aconfig_flags.pb") ensureListContainsMatch(t, copyCmds, "^cp -f .*/package.map .*/image.apex/etc/package.map") ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.map .*/image.apex/etc/flag.map") ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.val .*/image.apex/etc/flag.val") + ensureListContainsMatch(t, copyCmds, "^cp -f .*/flag.info .*/image.apex/etc/flag.info") inputs := []string{ "my_aconfig_declarations_foo/intermediate.pb", @@ -11121,6 +10713,7 @@ func TestAconfigFilesRustDeps(t *testing.T) { VerifyAconfigRule(t, &mod, "create_aconfig_package_map_file", inputs, "android_common_myapex/package.map", "myapex", "package_map") VerifyAconfigRule(t, &mod, "create_aconfig_flag_map_file", inputs, "android_common_myapex/flag.map", "myapex", "flag_map") VerifyAconfigRule(t, &mod, "create_aconfig_flag_val_file", inputs, "android_common_myapex/flag.val", "myapex", "flag_val") + VerifyAconfigRule(t, &mod, "create_aconfig_flag_info_file", inputs, "android_common_myapex/flag.info", "myapex", "flag_info") } func VerifyAconfigRule(t *testing.T, mod *android.TestingModule, desc string, inputs []string, output string, container string, file_type string) { @@ -11449,12 +11042,12 @@ func TestBootDexJarsMultipleApexPrebuilts(t *testing.T) { { desc: "Prebuilt apex prebuilt_com.android.foo is selected, profile should come from .prof deapexed from the prebuilt", selectedApexContributions: "foo.prebuilt.contributions", - expectedBootJar: "out/soong/.intermediates/prebuilt_com.android.foo.deapexer/android_common/deapexer/javalib/framework-foo.jar", + expectedBootJar: "out/soong/.intermediates/prebuilt_com.android.foo/android_common_com.android.foo/deapexer/javalib/framework-foo.jar", }, { desc: "Prebuilt apex prebuilt_com.android.foo.v2 is selected, profile should come from .prof deapexed from the prebuilt", selectedApexContributions: "foo.prebuilt.v2.contributions", - expectedBootJar: "out/soong/.intermediates/prebuilt_com.android.foo.v2.deapexer/android_common/deapexer/javalib/framework-foo.jar", + expectedBootJar: "out/soong/.intermediates/com.android.foo.v2/android_common_com.android.foo/deapexer/javalib/framework-foo.jar", }, } @@ -12146,3 +11739,110 @@ func TestPrebuiltStubNoinstall(t *testing.T) { ) }) } + +func TestSdkLibraryTransitiveClassLoaderContext(t *testing.T) { + // This test case tests that listing the impl lib instead of the top level java_sdk_library + // in libs of android_app and java_library does not lead to class loader context device/host + // path mismatch errors. + android.GroupFixturePreparers( + prepareForApexTest, + android.PrepareForIntegrationTestWithAndroid, + PrepareForTestWithApexBuildComponents, + android.FixtureModifyEnv(func(env map[string]string) { + env["DISABLE_CONTAINER_CHECK"] = "true" + }), + withFiles(filesForSdkLibrary), + android.FixtureMergeMockFs(android.MockFS{ + "system/sepolicy/apex/com.android.foo30-file_contexts": nil, + }), + ).RunTestWithBp(t, ` + apex { + name: "com.android.foo30", + key: "myapex.key", + updatable: true, + bootclasspath_fragments: [ + "foo-bootclasspath-fragment", + ], + java_libs: [ + "bar", + ], + apps: [ + "bar-app", + ], + min_sdk_version: "30", + } + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + bootclasspath_fragment { + name: "foo-bootclasspath-fragment", + contents: [ + "framework-foo", + ], + apex_available: [ + "com.android.foo30", + ], + hidden_api: { + split_packages: ["*"] + }, + } + + java_sdk_library { + name: "framework-foo", + srcs: [ + "A.java" + ], + unsafe_ignore_missing_latest_api: true, + apex_available: [ + "com.android.foo30", + ], + compile_dex: true, + sdk_version: "core_current", + shared_library: false, + } + + java_library { + name: "bar", + srcs: [ + "A.java" + ], + libs: [ + "framework-foo.impl", + ], + apex_available: [ + "com.android.foo30", + ], + sdk_version: "core_current", + } + + java_library { + name: "baz", + srcs: [ + "A.java" + ], + libs: [ + "bar", + ], + sdk_version: "core_current", + } + + android_app { + name: "bar-app", + srcs: [ + "A.java" + ], + libs: [ + "baz", + "framework-foo.impl", + ], + apex_available: [ + "com.android.foo30", + ], + sdk_version: "core_current", + min_sdk_version: "30", + manifest: "AndroidManifest.xml", + } + `) +} diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go index df7857f29..e44d3f552 100644 --- a/apex/bootclasspath_fragment_test.go +++ b/apex/bootclasspath_fragment_test.go @@ -398,11 +398,20 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) { // Make sure that a preferred prebuilt with consistent contents doesn't affect the apex. addPrebuilt(true, "foo", "bar"), + android.FixtureMergeMockFs(android.MockFS{ + "apex_contributions/Android.bp": []byte(` + apex_contributions { + name: "prebuilt_art_contributions", + contents: ["prebuilt_com.android.art"], + api_domain: "com.android.art", + } + `)}), + android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "prebuilt_art_contributions"), java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"), ).RunTest(t) - ensureExactDeapexedContents(t, result.TestContext, "prebuilt_com.android.art", "android_common", []string{ + ensureExactDeapexedContents(t, result.TestContext, "prebuilt_com.android.art", "android_common_com.android.art", []string{ "etc/boot-image.prof", "javalib/bar.jar", "javalib/foo.jar", @@ -495,6 +504,7 @@ func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) { java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"), dexpreopt.FixtureSetTestOnlyArtBootImageJars("com.android.art:foo", "com.android.art:bar"), java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"), + android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "prebuilt_art_contributions"), ) bp := ` @@ -552,6 +562,12 @@ func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) { src: "com.mycompany.android.art.apex", exported_bootclasspath_fragments: ["art-bootclasspath-fragment"], } + + apex_contributions { + name: "prebuilt_art_contributions", + contents: ["prebuilt_com.android.art"], + api_domain: "com.android.art", + } ` t.Run("disabled alternative APEX", func(t *testing.T) { @@ -561,27 +577,18 @@ func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) { `all_apex_contributions`, `dex2oatd`, `prebuilt_art-bootclasspath-fragment`, - `prebuilt_com.android.art.apex.selector`, - `prebuilt_com.android.art.deapexer`, }) java.CheckModuleDependencies(t, result.TestContext, "art-bootclasspath-fragment", "android_common_com.android.art", []string{ `all_apex_contributions`, `dex2oatd`, `prebuilt_bar`, - `prebuilt_com.android.art.deapexer`, `prebuilt_foo`, }) module := result.ModuleForTests("dex_bootjars", "android_common") checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo") }) - - t.Run("enabled alternative APEX", func(t *testing.T) { - preparers.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern( - "Multiple installable prebuilt APEXes provide ambiguous deapexers: prebuilt_com.android.art and prebuilt_com.mycompany.android.art")). - RunTestWithBp(t, fmt.Sprintf(bp, "")) - }) } // checkCopiesToPredefinedLocationForArt checks that the supplied modules are copied to the diff --git a/apex/builder.go b/apex/builder.go index a851d120d..ba7598b31 100644 --- a/apex/builder.go +++ b/apex/builder.go @@ -96,6 +96,7 @@ var createStorageInfo = []createStorageStruct{ {"package.map", "create_aconfig_package_map_file", "package_map"}, {"flag.map", "create_aconfig_flag_map_file", "flag_map"}, {"flag.val", "create_aconfig_flag_val_file", "flag_val"}, + {"flag.info", "create_aconfig_flag_info_file", "flag_info"}, } var ( @@ -359,14 +360,14 @@ func (a *apexBundle) buildManifest(ctx android.ModuleContext, provideNativeLibs, } manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json") - defaultVersion := android.DefaultUpdatableModuleVersion + defaultVersion := ctx.Config().ReleaseDefaultUpdatableModuleVersion() if a.properties.Variant_version != nil { defaultVersionInt, err := strconv.Atoi(defaultVersion) if err != nil { - ctx.ModuleErrorf("expected DefaultUpdatableModuleVersion to be an int, but got %s", defaultVersion) + ctx.ModuleErrorf("expected RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION to be an int, but got %s", defaultVersion) } if defaultVersionInt%10 != 0 { - ctx.ModuleErrorf("expected DefaultUpdatableModuleVersion to end in a zero, but got %s", defaultVersion) + ctx.ModuleErrorf("expected RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION to end in a zero, but got %s", defaultVersion) } variantVersion := []rune(*a.properties.Variant_version) if len(variantVersion) != 1 || variantVersion[0] < '0' || variantVersion[0] > '9' { @@ -1079,7 +1080,7 @@ func (a *apexBundle) buildApexDependencyInfo(ctx android.ModuleContext) { } depInfos := android.DepNameToDepInfoMap{} - a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool { + a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool { if from.Name() == to.Name() { // This can happen for cc.reuseObjTag. We are not interested in tracking this. // As soon as the dependency graph crosses the APEX boundary, don't go further. @@ -1146,10 +1147,23 @@ func (a *apexBundle) buildApexDependencyInfo(ctx android.ModuleContext) { func (a *apexBundle) buildLintReports(ctx android.ModuleContext) { depSetsBuilder := java.NewLintDepSetBuilder() for _, fi := range a.filesInfo { - depSetsBuilder.Transitive(fi.lintDepSets) + if fi.lintInfo != nil { + depSetsBuilder.Transitive(fi.lintInfo) + } + } + + depSets := depSetsBuilder.Build() + var validations android.Paths + + if a.checkStrictUpdatabilityLinting(ctx) { + baselines := depSets.Baseline.ToList() + if len(baselines) > 0 { + outputFile := java.VerifyStrictUpdatabilityChecks(ctx, baselines) + validations = append(validations, outputFile) + } } - a.lintReports = java.BuildModuleLintReportZips(ctx, depSetsBuilder.Build()) + a.lintReports = java.BuildModuleLintReportZips(ctx, depSets, validations) } func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext) android.OutputPath { diff --git a/apex/deapexer.go b/apex/deapexer.go index a6731080c..3b8233d33 100644 --- a/apex/deapexer.go +++ b/apex/deapexer.go @@ -15,37 +15,9 @@ package apex import ( - "strings" - "android/soong/android" ) -// Contains 'deapexer' a private module type used by 'prebuilt_apex' to make dex files contained -// within a .apex file referenced by `prebuilt_apex` available for use by their associated -// `java_import` modules. -// -// An 'apex' module references `java_library` modules from which .dex files are obtained that are -// stored in the resulting `.apex` file. The resulting `.apex` file is then made available as a -// prebuilt by referencing it from a `prebuilt_apex`. For each such `java_library` that is used by -// modules outside the `.apex` file a `java_import` prebuilt is made available referencing a jar -// that contains the Java classes. -// -// When building a Java module type, e.g. `java_module` or `android_app` against such prebuilts the -// `java_import` provides the classes jar (jar containing `.class` files) against which the -// module's `.java` files are compiled. That classes jar usually contains only stub classes. The -// resulting classes jar is converted into a dex jar (jar containing `.dex` files). Then if -// necessary the dex jar is further processed by `dexpreopt` to produce an optimized form of the -// library specific to the current Android version. This process requires access to implementation -// dex jars for each `java_import`. The `java_import` will obtain the implementation dex jar from -// the `.apex` file in the associated `prebuilt_apex`. -// -// This is intentionally not registered by name as it is not intended to be used from within an -// `Android.bp` file. - -// DeapexerProperties specifies the properties supported by the deapexer module. -// -// As these are never intended to be supplied in a .bp file they use a different naming convention -// to make it clear that they are different. type DeapexerProperties struct { // List of common modules that may need access to files exported by this module. // @@ -72,46 +44,9 @@ type SelectedApexProperties struct { Selected_apex *string `android:"path" blueprint:"mutated"` } -type Deapexer struct { - android.ModuleBase - - properties DeapexerProperties - selectedApexProperties SelectedApexProperties - - inputApex android.Path -} - -// Returns the name of the deapexer module corresponding to an APEX module with the given name. -func deapexerModuleName(apexModuleName string) string { - return apexModuleName + ".deapexer" -} - -// Returns the name of the APEX module corresponding to an deapexer module with -// the given name. This reverses deapexerModuleName. -func apexModuleName(deapexerModuleName string) string { - return strings.TrimSuffix(deapexerModuleName, ".deapexer") -} - -func privateDeapexerFactory() android.Module { - module := &Deapexer{} - module.AddProperties(&module.properties, &module.selectedApexProperties) - android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) - return module -} - -func (p *Deapexer) DepsMutator(ctx android.BottomUpMutatorContext) { - // Add dependencies from the java modules to which this exports files from the `.apex` file onto - // this module so that they can access the `DeapexerInfo` object that this provides. - // TODO: b/308174306 - Once all the mainline modules have been flagged, drop this dependency edge - for _, lib := range p.properties.CommonModules { - dep := prebuiltApexExportedModuleName(ctx, lib) - ctx.AddReverseDependency(ctx.Module(), android.DeapexerTag, dep) - } -} - -func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) { - p.inputApex = android.OptionalPathForModuleSrc(ctx, p.selectedApexProperties.Selected_apex).Path() - +// deapex creates the build rules to deapex a prebuilt .apex file +// it returns a pointer to a DeapexerInfo object +func deapex(ctx android.ModuleContext, apexFile android.Path, deapexerProps DeapexerProperties) *android.DeapexerInfo { // Create and remember the directory into which the .apex file's contents will be unpacked. deapexerOutput := android.PathForModuleOut(ctx, "deapexer") @@ -119,7 +54,7 @@ func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) { // Create mappings from apex relative path to the extracted file's path. exportedPaths := make(android.Paths, 0, len(exports)) - for _, path := range p.properties.ExportedFiles { + for _, path := range deapexerProps.ExportedFiles { // Populate the exports that this makes available. extractedPath := deapexerOutput.Join(ctx, path) exports[path] = extractedPath @@ -131,9 +66,8 @@ func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) { // apex relative path to extracted file path available for other modules. if len(exports) > 0 { // Make the information available for other modules. - di := android.NewDeapexerInfo(apexModuleName(ctx.ModuleName()), exports, p.properties.CommonModules) - di.AddDexpreoptProfileGuidedExportedModuleNames(p.properties.DexpreoptProfileGuidedModules...) - android.SetProvider(ctx, android.DeapexerProvider, di) + di := android.NewDeapexerInfo(ctx.ModuleName(), exports, deapexerProps.CommonModules) + di.AddDexpreoptProfileGuidedExportedModuleNames(deapexerProps.DexpreoptProfileGuidedModules...) // Create a sorted list of the files that this exports. exportedPaths = android.SortedUniquePaths(exportedPaths) @@ -147,11 +81,13 @@ func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) { BuiltTool("deapexer"). BuiltTool("debugfs"). BuiltTool("fsck.erofs"). - Input(p.inputApex). + Input(apexFile). Text(deapexerOutput.String()) for _, p := range exportedPaths { command.Output(p.(android.WritablePath)) } - builder.Build("deapexer", "deapex "+apexModuleName(ctx.ModuleName())) + builder.Build("deapexer", "deapex "+ctx.ModuleName()) + return &di } + return nil } diff --git a/apex/dexpreopt_bootjars_test.go b/apex/dexpreopt_bootjars_test.go index d8ee4ba6c..4feade8ca 100644 --- a/apex/dexpreopt_bootjars_test.go +++ b/apex/dexpreopt_bootjars_test.go @@ -127,16 +127,29 @@ func testDexpreoptBoot(t *testing.T, ruleFile string, expectedInputs, expectedOu src: "com.android.art-arm.apex", exported_bootclasspath_fragments: ["art-bootclasspath-fragment"], } + + apex_contributions { + name: "prebuilt_art_contributions", + contents: ["prebuilt_com.android.art"], + api_domain: "com.android.art", + } ` - result := android.GroupFixturePreparers( + fixture := android.GroupFixturePreparers( java.PrepareForTestWithDexpreopt, java.PrepareForTestWithJavaSdkLibraryFiles, java.FixtureWithLastReleaseApis("foo"), java.FixtureConfigureBootJars("com.android.art:core-oj", "platform:foo", "system_ext:bar", "platform:baz"), PrepareForTestWithApexBuildComponents, prepareForTestWithArtApex, - ).RunTestWithBp(t, fmt.Sprintf(bp, preferPrebuilt)) + ) + if preferPrebuilt { + fixture = android.GroupFixturePreparers( + fixture, + android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "prebuilt_art_contributions"), + ) + } + result := fixture.RunTestWithBp(t, fmt.Sprintf(bp, preferPrebuilt)) dexBootJars := result.ModuleForTests("dex_bootjars", "android_common") rule := dexBootJars.Output(ruleFile) @@ -200,7 +213,7 @@ func TestDexpreoptBootJarsWithPrebuiltArtApex(t *testing.T) { "out/soong/dexpreopt_arm64/dex_bootjars_input/foo.jar", "out/soong/dexpreopt_arm64/dex_bootjars_input/bar.jar", "out/soong/dexpreopt_arm64/dex_bootjars_input/baz.jar", - "out/soong/.intermediates/prebuilt_com.android.art.deapexer/android_common/deapexer/etc/boot-image.prof", + "out/soong/.intermediates/prebuilt_com.android.art/android_common_com.android.art/deapexer/etc/boot-image.prof", "out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof", "out/soong/dexpreopt/uffd_gc_flag.txt", } @@ -384,12 +397,12 @@ func TestDexpreoptProfileWithMultiplePrebuiltArtApexes(t *testing.T) { { desc: "Prebuilt apex prebuilt_com.android.art is selected, profile should come from .prof deapexed from the prebuilt", selectedArtApexContributions: "art.prebuilt.contributions", - expectedProfile: "out/soong/.intermediates/prebuilt_com.android.art.deapexer/android_common/deapexer/etc/boot-image.prof", + expectedProfile: "out/soong/.intermediates/prebuilt_com.android.art/android_common_com.android.art/deapexer/etc/boot-image.prof", }, { desc: "Prebuilt apex prebuilt_com.android.art.v2 is selected, profile should come from .prof deapexed from the prebuilt", selectedArtApexContributions: "art.prebuilt.v2.contributions", - expectedProfile: "out/soong/.intermediates/prebuilt_com.android.art.v2.deapexer/android_common/deapexer/etc/boot-image.prof", + expectedProfile: "out/soong/.intermediates/com.android.art.v2/android_common_com.android.art/deapexer/etc/boot-image.prof", }, } for _, tc := range testCases { diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go index 9c2d89951..f4da31ed2 100644 --- a/apex/platform_bootclasspath_test.go +++ b/apex/platform_bootclasspath_test.go @@ -409,6 +409,9 @@ func TestPlatformBootclasspathDependencies(t *testing.T) { // The fragments. `com.android.art:art-bootclasspath-fragment`, `myapex:my-bootclasspath-fragment`, + + // Impl lib of sdk_library for transitive srcjar generation + `platform:foo.impl`, }) } @@ -565,6 +568,9 @@ func TestPlatformBootclasspath_AlwaysUsePrebuiltSdks(t *testing.T) { // The fragments. "myapex:mybootclasspath-fragment", "myapex:prebuilt_mybootclasspath-fragment", + + // Impl lib of sdk_library for transitive srcjar generation + "platform:foo.impl", }) } diff --git a/apex/prebuilt.go b/apex/prebuilt.go index f1a134eea..9cd5688ba 100644 --- a/apex/prebuilt.go +++ b/apex/prebuilt.go @@ -107,11 +107,6 @@ type PrebuiltCommonProperties struct { // from PRODUCT_PACKAGES. Overrides []string - // List of java libraries that are embedded inside this prebuilt APEX bundle and for which this - // APEX bundle will create an APEX variant and provide dex implementation jars for use by - // dexpreopt and boot jars package check. - Exported_java_libs []string - // List of bootclasspath fragments inside this prebuilt APEX bundle and for which this APEX // bundle will create an APEX variant. Exported_bootclasspath_fragments []string @@ -199,9 +194,8 @@ func (p *prebuiltCommon) initApexFilesForAndroidMk(ctx android.ModuleContext) { } // If this prebuilt has system server jar, create the rules to dexpreopt it and install it alongside the prebuilt apex -func (p *prebuiltCommon) dexpreoptSystemServerJars(ctx android.ModuleContext) { - // If this apex does not export anything, return - if !p.hasExportedDeps() { +func (p *prebuiltCommon) dexpreoptSystemServerJars(ctx android.ModuleContext, di *android.DeapexerInfo) { + if di == nil { return } // If this prebuilt apex has not been selected, return @@ -210,10 +204,7 @@ func (p *prebuiltCommon) dexpreoptSystemServerJars(ctx android.ModuleContext) { } // Use apex_name to determine the api domain of this prebuilt apex apexName := p.ApexVariationName() - di, err := android.FindDeapexerProviderForModule(ctx) - if err != nil { - ctx.ModuleErrorf(err.Error()) - } + // TODO: do not compute twice dc := dexpreopt.GetGlobalConfig(ctx) systemServerJarList := dc.AllApexSystemServerJars(ctx) @@ -262,29 +253,8 @@ func (p *prebuiltCommon) AndroidMkEntries() []android.AndroidMkEntries { return entriesList } -// prebuiltApexModuleCreator defines the methods that need to be implemented by prebuilt_apex and -// apex_set in order to create the modules needed to provide access to the prebuilt .apex file. -type prebuiltApexModuleCreator interface { - createPrebuiltApexModules(ctx android.TopDownMutatorContext) -} - -// prebuiltApexModuleCreatorMutator is the mutator responsible for invoking the -// prebuiltApexModuleCreator's createPrebuiltApexModules method. -// -// It is registered as a pre-arch mutator as it must run after the ComponentDepsMutator because it -// will need to access dependencies added by that (exported modules) but must run before the -// DepsMutator so that the deapexer module it creates can add dependencies onto itself from the -// exported modules. -func prebuiltApexModuleCreatorMutator(ctx android.TopDownMutatorContext) { - module := ctx.Module() - if creator, ok := module.(prebuiltApexModuleCreator); ok { - creator.createPrebuiltApexModules(ctx) - } -} - func (p *prebuiltCommon) hasExportedDeps() bool { - return len(p.prebuiltCommonProperties.Exported_java_libs) > 0 || - len(p.prebuiltCommonProperties.Exported_bootclasspath_fragments) > 0 || + return len(p.prebuiltCommonProperties.Exported_bootclasspath_fragments) > 0 || len(p.prebuiltCommonProperties.Exported_systemserverclasspath_fragments) > 0 } @@ -292,11 +262,6 @@ func (p *prebuiltCommon) hasExportedDeps() bool { func (p *prebuiltCommon) prebuiltApexContentsDeps(ctx android.BottomUpMutatorContext) { module := ctx.Module() - for _, dep := range p.prebuiltCommonProperties.Exported_java_libs { - prebuiltDep := android.PrebuiltNameFromSource(dep) - ctx.AddDependency(module, exportedJavaLibTag, prebuiltDep) - } - for _, dep := range p.prebuiltCommonProperties.Exported_bootclasspath_fragments { prebuiltDep := android.PrebuiltNameFromSource(dep) ctx.AddDependency(module, exportedBootclasspathFragmentTag, prebuiltDep) @@ -414,34 +379,6 @@ func (p *prebuiltCommon) apexInfoMutator(mctx android.TopDownMutatorContext) { } } -// prebuiltApexSelectorModule is a private module type that is only created by the prebuilt_apex -// module. It selects the apex to use and makes it available for use by prebuilt_apex and the -// deapexer. -type prebuiltApexSelectorModule struct { - android.ModuleBase - - apexFileProperties ApexFileProperties - - inputApex android.Path -} - -func privateApexSelectorModuleFactory() android.Module { - module := &prebuiltApexSelectorModule{} - module.AddProperties( - &module.apexFileProperties, - ) - android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) - return module -} - -func (p *prebuiltApexSelectorModule) Srcs() android.Paths { - return android.Paths{p.inputApex} -} - -func (p *prebuiltApexSelectorModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { - p.inputApex = android.SingleSourcePathFromSupplier(ctx, p.apexFileProperties.prebuiltApexSelector, "src") -} - type Prebuilt struct { prebuiltCommon @@ -484,11 +421,11 @@ type ApexFileProperties struct { // to use methods on it that are specific to the current module. // // See the ApexFileProperties.Src property. -func (p *ApexFileProperties) prebuiltApexSelector(ctx android.BaseModuleContext, prebuilt android.Module) []string { +func (p *ApexFileProperties) prebuiltApexSelector(ctx android.BaseModuleContext, prebuilt android.Module) string { multiTargets := prebuilt.MultiTargets() if len(multiTargets) != 1 { ctx.OtherModuleErrorf(prebuilt, "compile_multilib shouldn't be \"both\" for prebuilt_apex") - return nil + return "" } var src string switch multiTargets[0].Arch.ArchType { @@ -521,7 +458,7 @@ func (p *ApexFileProperties) prebuiltApexSelector(ctx android.BaseModuleContext, // logic from reporting a more general, less useful message. } - return []string{src} + return src } type PrebuiltProperties struct { @@ -538,35 +475,19 @@ func (a *Prebuilt) hasSanitizedSource(sanitizer string) bool { func PrebuiltFactory() android.Module { module := &Prebuilt{} module.AddProperties(&module.properties) - module.initPrebuiltCommon(module, &module.properties.PrebuiltCommonProperties) + module.prebuiltCommon.prebuiltCommonProperties = &module.properties.PrebuiltCommonProperties - return module -} - -func createApexSelectorModule(ctx android.TopDownMutatorContext, name string, apexFileProperties *ApexFileProperties) { - props := struct { - Name *string - }{ - Name: proptools.StringPtr(name), - } + // init the module as a prebuilt + // even though this module type has srcs, use `InitPrebuiltModuleWithoutSrcs`, since the existing + // InitPrebuiltModule* are not friendly with Sources of Configurable type. + // The actual src will be evaluated in GenerateAndroidBuildActions. + android.InitPrebuiltModuleWithoutSrcs(module) + android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) - ctx.CreateModule(privateApexSelectorModuleFactory, - &props, - apexFileProperties, - ) + return module } -// createDeapexerModuleIfNeeded will create a deapexer module if it is needed. -// -// A deapexer module is only needed when the prebuilt apex specifies one or more modules in either -// the `exported_java_libs` or `exported_bootclasspath_fragments` properties as that indicates that -// the listed modules need access to files from within the prebuilt .apex file. -func (p *prebuiltCommon) createDeapexerModuleIfNeeded(ctx android.TopDownMutatorContext, deapexerName string, apexFileSource string) { - // Only create the deapexer module if it is needed. - if !p.hasExportedDeps() { - return - } - +func (p *prebuiltCommon) getDeapexerPropertiesIfNeeded(ctx android.ModuleContext) DeapexerProperties { // Compute the deapexer properties from the transitive dependencies of this module. commonModules := []string{} dexpreoptProfileGuidedModules := []string{} @@ -600,7 +521,7 @@ func (p *prebuiltCommon) createDeapexerModuleIfNeeded(ctx android.TopDownMutator }) // Create properties for deapexer module. - deapexerProperties := &DeapexerProperties{ + deapexerProperties := DeapexerProperties{ // Remove any duplicates from the common modules lists as a module may be included via a direct // dependency as well as transitive ones. CommonModules: android.SortedUniqueStrings(commonModules), @@ -609,22 +530,7 @@ func (p *prebuiltCommon) createDeapexerModuleIfNeeded(ctx android.TopDownMutator // Populate the exported files property in a fixed order. deapexerProperties.ExportedFiles = android.SortedUniqueStrings(exportedFiles) - - props := struct { - Name *string - Selected_apex *string - }{ - Name: proptools.StringPtr(deapexerName), - Selected_apex: proptools.StringPtr(apexFileSource), - } - ctx.CreateModule(privateDeapexerFactory, - &props, - deapexerProperties, - ) -} - -func apexSelectorModuleName(baseModuleName string) string { - return baseModuleName + ".apex.selector" + return deapexerProperties } func prebuiltApexExportedModuleName(ctx android.BottomUpMutatorContext, name string) string { @@ -666,97 +572,50 @@ func (t exportedDependencyTag) RequiresFilesFromPrebuiltApex() {} var _ android.RequiresFilesFromPrebuiltApexTag = exportedDependencyTag{} var ( - exportedJavaLibTag = exportedDependencyTag{name: "exported_java_libs"} exportedBootclasspathFragmentTag = exportedDependencyTag{name: "exported_bootclasspath_fragments"} exportedSystemserverclasspathFragmentTag = exportedDependencyTag{name: "exported_systemserverclasspath_fragments"} ) -var _ prebuiltApexModuleCreator = (*Prebuilt)(nil) - -// createPrebuiltApexModules creates modules necessary to export files from the prebuilt apex to the -// build. -// -// If this needs to make files from within a `.apex` file available for use by other Soong modules, -// e.g. make dex implementation jars available for java_import modules listed in exported_java_libs, -// it does so as follows: -// -// 1. It creates a `deapexer` module that actually extracts the files from the `.apex` file and -// makes them available for use by other modules, at both Soong and ninja levels. -// -// 2. It adds a dependency onto those modules and creates an apex specific variant similar to what -// an `apex` module does. That ensures that code which looks for specific apex variant, e.g. -// dexpreopt, will work the same way from source and prebuilt. -// -// 3. The `deapexer` module adds a dependency from the modules that require the exported files onto -// itself so that they can retrieve the file paths to those files. -// -// It also creates a child module `selector` that is responsible for selecting the appropriate -// input apex for both the prebuilt_apex and the deapexer. That is needed for a couple of reasons: -// -// 1. To dedup the selection logic so it only runs in one module. -// -// 2. To allow the deapexer to be wired up to a different source for the input apex, e.g. an -// `apex_set`. -// -// prebuilt_apex -// / | \ -// / | \ -// V V V -// selector <--- deapexer <--- exported java lib -func (p *Prebuilt) createPrebuiltApexModules(ctx android.TopDownMutatorContext) { - apexSelectorModuleName := apexSelectorModuleName(p.Name()) - createApexSelectorModule(ctx, apexSelectorModuleName, &p.properties.ApexFileProperties) - - apexFileSource := ":" + apexSelectorModuleName - p.createDeapexerModuleIfNeeded(ctx, deapexerModuleName(p.Name()), apexFileSource) - - // Add a source reference to retrieve the selected apex from the selector module. - p.prebuiltCommonProperties.Selected_apex = proptools.StringPtr(apexFileSource) -} - func (p *Prebuilt) ComponentDepsMutator(ctx android.BottomUpMutatorContext) { p.prebuiltApexContentsDeps(ctx) } -func (p *prebuiltCommon) DepsMutator(ctx android.BottomUpMutatorContext) { - if p.hasExportedDeps() { - // Create a dependency from the prebuilt apex (prebuilt_apex/apex_set) to the internal deapexer module - // The deapexer will return a provider which will be used to determine the exported artfifacts from this prebuilt. - ctx.AddDependency(ctx.Module(), android.DeapexerTag, deapexerModuleName(p.Name())) - } -} - var _ ApexInfoMutator = (*Prebuilt)(nil) func (p *Prebuilt) ApexInfoMutator(mctx android.TopDownMutatorContext) { p.apexInfoMutator(mctx) } +// creates the build rules to deapex the prebuilt, and returns a deapexerInfo +func (p *prebuiltCommon) getDeapexerInfo(ctx android.ModuleContext, apexFile android.Path) *android.DeapexerInfo { + if !p.hasExportedDeps() { + // nothing to do + return nil + } + deapexerProps := p.getDeapexerPropertiesIfNeeded(ctx) + return deapex(ctx, apexFile, deapexerProps) +} + // Set a provider containing information about the jars and .prof provided by the apex // Apexes built from prebuilts retrieve this information by visiting its internal deapexer module // Used by dex_bootjars to generate the boot image -func (p *prebuiltCommon) provideApexExportsInfo(ctx android.ModuleContext) { - if !p.hasExportedDeps() { - // nothing to do +func (p *prebuiltCommon) provideApexExportsInfo(ctx android.ModuleContext, di *android.DeapexerInfo) { + if di == nil { return } - if di, err := android.FindDeapexerProviderForModule(ctx); err == nil { - javaModuleToDexPath := map[string]android.Path{} - for _, commonModule := range di.GetExportedModuleNames() { - if dex := di.PrebuiltExportPath(java.ApexRootRelativePathToJavaLib(commonModule)); dex != nil { - javaModuleToDexPath[commonModule] = dex - } + javaModuleToDexPath := map[string]android.Path{} + for _, commonModule := range di.GetExportedModuleNames() { + if dex := di.PrebuiltExportPath(java.ApexRootRelativePathToJavaLib(commonModule)); dex != nil { + javaModuleToDexPath[commonModule] = dex } + } - exports := android.ApexExportsInfo{ - ApexName: p.ApexVariationName(), - ProfilePathOnHost: di.PrebuiltExportPath(java.ProfileInstallPathInApex), - LibraryNameToDexJarPathOnHost: javaModuleToDexPath, - } - android.SetProvider(ctx, android.ApexExportsInfoProvider, exports) - } else { - ctx.ModuleErrorf(err.Error()) + exports := android.ApexExportsInfo{ + ApexName: p.ApexVariationName(), + ProfilePathOnHost: di.PrebuiltExportPath(java.ProfileInstallPathInApex), + LibraryNameToDexJarPathOnHost: javaModuleToDexPath, } + android.SetProvider(ctx, android.ApexExportsInfoProvider, exports) } // Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file @@ -792,7 +651,7 @@ func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) { p.apexKeysPath = writeApexKeys(ctx, p) // TODO(jungjw): Check the key validity. - p.inputApex = android.OptionalPathForModuleSrc(ctx, p.prebuiltCommonProperties.Selected_apex).Path() + p.inputApex = android.PathForModuleSrc(ctx, p.properties.prebuiltApexSelector(ctx, ctx.Module())) p.installDir = android.PathForModuleInstall(ctx, "apex") p.installFilename = p.InstallFilename() if !strings.HasSuffix(p.installFilename, imageApexSuffix) { @@ -810,11 +669,13 @@ func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) { return } + deapexerInfo := p.getDeapexerInfo(ctx, p.inputApex) + // dexpreopt any system server jars if present - p.dexpreoptSystemServerJars(ctx) + p.dexpreoptSystemServerJars(ctx, deapexerInfo) // provide info used for generating the boot image - p.provideApexExportsInfo(ctx) + p.provideApexExportsInfo(ctx, deapexerInfo) p.providePrebuiltInfo(ctx) @@ -850,26 +711,11 @@ type prebuiltApexExtractorModule struct { extractedApex android.WritablePath } -func privateApexExtractorModuleFactory() android.Module { - module := &prebuiltApexExtractorModule{} - module.AddProperties( - &module.properties, - ) - android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) - return module -} - -func (p *prebuiltApexExtractorModule) Srcs() android.Paths { - return android.Paths{p.extractedApex} -} - -func (p *prebuiltApexExtractorModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { - srcsSupplier := func(ctx android.BaseModuleContext, prebuilt android.Module) []string { - return p.properties.prebuiltSrcs(ctx) - } +// extract registers the build actions to extract an apex from .apks file +// returns the path of the extracted apex +func extract(ctx android.ModuleContext, apexSet android.Path, prerelease *bool) android.Path { defaultAllowPrerelease := ctx.Config().IsEnvTrue("SOONG_ALLOW_PRERELEASE_APEXES") - apexSet := android.SingleSourcePathFromSupplier(ctx, srcsSupplier, "set") - p.extractedApex = android.PathForModuleOut(ctx, "extracted", apexSet.Base()) + extractedApex := android.PathForModuleOut(ctx, "extracted", apexSet.Base()) // Filter out NativeBridge archs (b/260115309) abis := java.SupportedAbis(ctx, true) ctx.Build(pctx, @@ -877,14 +723,16 @@ func (p *prebuiltApexExtractorModule) GenerateAndroidBuildActions(ctx android.Mo Rule: extractMatchingApex, Description: "Extract an apex from an apex set", Inputs: android.Paths{apexSet}, - Output: p.extractedApex, + Output: extractedApex, Args: map[string]string{ "abis": strings.Join(abis, ","), - "allow-prereleased": strconv.FormatBool(proptools.BoolDefault(p.properties.Prerelease, defaultAllowPrerelease)), + "allow-prereleased": strconv.FormatBool(proptools.BoolDefault(prerelease, defaultAllowPrerelease)), "sdk-version": ctx.Config().PlatformSdkVersion().String(), "skip-sdk-check": strconv.FormatBool(ctx.Config().IsEnvTrue("SOONG_SKIP_APPSET_SDK_CHECK")), }, - }) + }, + ) + return extractedApex } type ApexSet struct { @@ -953,46 +801,16 @@ func (a *ApexSet) hasSanitizedSource(sanitizer string) bool { func apexSetFactory() android.Module { module := &ApexSet{} module.AddProperties(&module.properties) - module.initPrebuiltCommon(module, &module.properties.PrebuiltCommonProperties) + module.prebuiltCommon.prebuiltCommonProperties = &module.properties.PrebuiltCommonProperties - return module -} - -func createApexExtractorModule(ctx android.TopDownMutatorContext, name string, apexExtractorProperties *ApexExtractorProperties) { - props := struct { - Name *string - }{ - Name: proptools.StringPtr(name), - } - - ctx.CreateModule(privateApexExtractorModuleFactory, - &props, - apexExtractorProperties, - ) -} - -func apexExtractorModuleName(baseModuleName string) string { - return baseModuleName + ".apex.extractor" -} - -var _ prebuiltApexModuleCreator = (*ApexSet)(nil) + // init the module as a prebuilt + // even though this module type has srcs, use `InitPrebuiltModuleWithoutSrcs`, since the existing + // InitPrebuiltModule* are not friendly with Sources of Configurable type. + // The actual src will be evaluated in GenerateAndroidBuildActions. + android.InitPrebuiltModuleWithoutSrcs(module) + android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) -// createPrebuiltApexModules creates modules necessary to export files from the apex set to other -// modules. -// -// This effectively does for apex_set what Prebuilt.createPrebuiltApexModules does for a -// prebuilt_apex except that instead of creating a selector module which selects one .apex file -// from those provided this creates an extractor module which extracts the appropriate .apex file -// from the zip file containing them. -func (a *ApexSet) createPrebuiltApexModules(ctx android.TopDownMutatorContext) { - apexExtractorModuleName := apexExtractorModuleName(a.Name()) - createApexExtractorModule(ctx, apexExtractorModuleName, &a.properties.ApexExtractorProperties) - - apexFileSource := ":" + apexExtractorModuleName - a.createDeapexerModuleIfNeeded(ctx, deapexerModuleName(a.Name()), apexFileSource) - - // After passing the arch specific src properties to the creating the apex selector module - a.prebuiltCommonProperties.Selected_apex = proptools.StringPtr(apexFileSource) + return module } func (a *ApexSet) ComponentDepsMutator(ctx android.BottomUpMutatorContext) { @@ -1017,7 +835,15 @@ func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) { ctx.ModuleErrorf("filename should end in %s or %s for apex_set", imageApexSuffix, imageCapexSuffix) } - inputApex := android.OptionalPathForModuleSrc(ctx, a.prebuiltCommonProperties.Selected_apex).Path() + var apexSet android.Path + if srcs := a.properties.prebuiltSrcs(ctx); len(srcs) == 1 { + apexSet = android.PathForModuleSrc(ctx, srcs[0]) + } else { + ctx.ModuleErrorf("Expected exactly one source apex_set file, found %v\n", srcs) + } + + extractedApex := extract(ctx, apexSet, a.properties.Prerelease) + a.outputApex = android.PathForModuleOut(ctx, a.installFilename) // Build the output APEX. If compression is not enabled, make sure the output is not compressed even if the input is compressed @@ -1027,7 +853,7 @@ func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) { } ctx.Build(pctx, android.BuildParams{ Rule: buildRule, - Input: inputApex, + Input: extractedApex, Output: a.outputApex, }) @@ -1036,11 +862,13 @@ func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) { return } + deapexerInfo := a.getDeapexerInfo(ctx, extractedApex) + // dexpreopt any system server jars if present - a.dexpreoptSystemServerJars(ctx) + a.dexpreoptSystemServerJars(ctx, deapexerInfo) // provide info used for generating the boot image - a.provideApexExportsInfo(ctx) + a.provideApexExportsInfo(ctx, deapexerInfo) a.providePrebuiltInfo(ctx) diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go index fd9020b7f..acb364973 100644 --- a/apex/systemserver_classpath_fragment_test.go +++ b/apex/systemserver_classpath_fragment_test.go @@ -277,8 +277,6 @@ func TestPrebuiltSystemserverclasspathFragmentContents(t *testing.T) { java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex", []string{ `all_apex_contributions`, `dex2oatd`, - `prebuilt_myapex.apex.selector`, - `prebuilt_myapex.deapexer`, `prebuilt_mysystemserverclasspathfragment`, }) @@ -286,10 +284,9 @@ func TestPrebuiltSystemserverclasspathFragmentContents(t *testing.T) { `all_apex_contributions`, `prebuilt_bar`, `prebuilt_foo`, - `prebuilt_myapex.deapexer`, }) - ensureExactDeapexedContents(t, ctx, "prebuilt_myapex", "android_common", []string{ + ensureExactDeapexedContents(t, ctx, "myapex", "android_common_myapex", []string{ "javalib/foo.jar", "javalib/bar.jar", "javalib/bar.jar.prof", @@ -439,10 +436,9 @@ func TestPrebuiltStandaloneSystemserverclasspathFragmentContents(t *testing.T) { `all_apex_contributions`, `prebuilt_bar`, `prebuilt_foo`, - `prebuilt_myapex.deapexer`, }) - ensureExactDeapexedContents(t, ctx, "prebuilt_myapex", "android_common", []string{ + ensureExactDeapexedContents(t, ctx, "myapex", "android_common_myapex", []string{ "javalib/foo.jar", "javalib/bar.jar", "javalib/bar.jar.prof", diff --git a/apex/testing.go b/apex/testing.go index 3b200f05b..63c5b699e 100644 --- a/apex/testing.go +++ b/apex/testing.go @@ -16,6 +16,8 @@ package apex import "android/soong/android" +const testDefaultUpdatableModuleVersion = "340090000" + var PrepareForTestWithApexBuildComponents = android.GroupFixturePreparers( android.FixtureRegisterWithContext(registerApexBuildComponents), android.FixtureRegisterWithContext(registerApexKeyBuildComponents), @@ -29,4 +31,5 @@ var PrepareForTestWithApexBuildComponents = android.GroupFixturePreparers( // Needed by prebuilt_apex. "build/soong/scripts/unpack-prebuilt-apex.sh": nil, }.AddToFixture(), + android.PrepareForTestWithBuildFlag("RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION", testDefaultUpdatableModuleVersion), ) diff --git a/apex/vndk.go b/apex/vndk.go index 781aa3cbf..3ececc5c1 100644 --- a/apex/vndk.go +++ b/apex/vndk.go @@ -54,13 +54,26 @@ type apexVndkProperties struct { Vndk_version *string } -func apexVndkMutator(mctx android.TopDownMutatorContext) { - if ab, ok := mctx.Module().(*apexBundle); ok && ab.vndkApex { - if ab.IsNativeBridgeSupported() { +func apexVndkDepsMutator(mctx android.BottomUpMutatorContext) { + if m, ok := mctx.Module().(*cc.Module); ok && cc.IsForVndkApex(mctx, m) { + vndkVersion := m.VndkVersion() + + if vndkVersion == "" { + return + } + vndkVersion = "v" + vndkVersion + + vndkApexName := "com.android.vndk." + vndkVersion + + if mctx.OtherModuleExists(vndkApexName) { + mctx.AddReverseDependency(mctx.Module(), sharedLibTag, vndkApexName) + } + } else if a, ok := mctx.Module().(*apexBundle); ok && a.vndkApex { + if a.IsNativeBridgeSupported() { mctx.PropertyErrorf("native_bridge_supported", "%q doesn't support native bridge binary.", mctx.ModuleType()) } - vndkVersion := ab.vndkVersion() + vndkVersion := a.vndkVersion() if vndkVersion != "" { apiLevel, err := android.ApiLevelFromUser(mctx, vndkVersion) if err != nil { @@ -72,32 +85,14 @@ func apexVndkMutator(mctx android.TopDownMutatorContext) { if len(targets) > 0 && apiLevel.LessThan(cc.MinApiForArch(mctx, targets[0].Arch.ArchType)) { // Disable VNDK APEXes for VNDK versions less than the minimum supported API // level for the primary architecture. - ab.Disable() + a.Disable() + } else { + mctx.AddDependency(mctx.Module(), prebuiltTag, cc.VndkLibrariesTxtModules(vndkVersion, mctx)...) } } } } -func apexVndkDepsMutator(mctx android.BottomUpMutatorContext) { - if m, ok := mctx.Module().(*cc.Module); ok && cc.IsForVndkApex(mctx, m) { - vndkVersion := m.VndkVersion() - - if vndkVersion == "" { - return - } - vndkVersion = "v" + vndkVersion - - vndkApexName := "com.android.vndk." + vndkVersion - - if mctx.OtherModuleExists(vndkApexName) { - mctx.AddReverseDependency(mctx.Module(), sharedLibTag, vndkApexName) - } - } else if a, ok := mctx.Module().(*apexBundle); ok && a.vndkApex { - vndkVersion := proptools.StringDefault(a.vndkProperties.Vndk_version, "current") - mctx.AddDependency(mctx.Module(), prebuiltTag, cc.VndkLibrariesTxtModules(vndkVersion, mctx)...) - } -} - // name is module.BaseModuleName() which is used as LOCAL_MODULE_NAME and also LOCAL_OVERRIDES_* func makeCompatSymlinks(name string, ctx android.ModuleContext) (symlinks android.InstallPaths) { // small helper to add symlink commands diff --git a/bazel/Android.bp b/bazel/Android.bp deleted file mode 100644 index f8273a847..000000000 --- a/bazel/Android.bp +++ /dev/null @@ -1,22 +0,0 @@ -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -bootstrap_go_package { - name: "soong-bazel", - pkgPath: "android/soong/bazel", - srcs: [ - "configurability.go", - "properties.go", - "testing.go", - ], - testSrcs: [ - "properties_test.go", - ], - pluginFor: [ - "soong_build", - ], - deps: [ - "blueprint", - ], -} diff --git a/bazel/configurability.go b/bazel/configurability.go deleted file mode 100644 index 2c9a5364a..000000000 --- a/bazel/configurability.go +++ /dev/null @@ -1,393 +0,0 @@ -// Copyright 2021 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bazel - -import ( - "fmt" - "math" - "sort" - "strings" -) - -const ( - // ArchType names in arch.go - archArm = "arm" - archArm64 = "arm64" - archRiscv64 = "riscv64" - archX86 = "x86" - archX86_64 = "x86_64" - - // OsType names in arch.go - OsAndroid = "android" - OsDarwin = "darwin" - OsLinux = "linux_glibc" - osLinuxMusl = "linux_musl" - osLinuxBionic = "linux_bionic" - OsWindows = "windows" - - // Targets in arch.go - osArchAndroidArm = "android_arm" - OsArchAndroidArm64 = "android_arm64" - osArchAndroidRiscv64 = "android_riscv64" - osArchAndroidX86 = "android_x86" - osArchAndroidX86_64 = "android_x86_64" - osArchDarwinArm64 = "darwin_arm64" - osArchDarwinX86_64 = "darwin_x86_64" - osArchLinuxX86 = "linux_glibc_x86" - osArchLinuxX86_64 = "linux_glibc_x86_64" - osArchLinuxMuslArm = "linux_musl_arm" - osArchLinuxMuslArm64 = "linux_musl_arm64" - osArchLinuxMuslX86 = "linux_musl_x86" - osArchLinuxMuslX86_64 = "linux_musl_x86_64" - osArchLinuxBionicArm64 = "linux_bionic_arm64" - osArchLinuxBionicX86_64 = "linux_bionic_x86_64" - osArchWindowsX86 = "windows_x86" - osArchWindowsX86_64 = "windows_x86_64" - - // This is the string representation of the default condition wherever a - // configurable attribute is used in a select statement, i.e. - // //conditions:default for Bazel. - // - // This is consistently named "conditions_default" to mirror the Soong - // config variable default key in an Android.bp file, although there's no - // integration with Soong config variables (yet). - ConditionsDefaultConfigKey = "conditions_default" - - ConditionsDefaultSelectKey = "//conditions:default" - - productVariableBazelPackage = "//build/bazel/product_config/config_settings" - - AndroidAndInApex = "android-in_apex" - AndroidPlatform = "system" - Unbundled_app = "unbundled_app" - - InApex = "in_apex" - NonApex = "non_apex" - - ErrorproneDisabled = "errorprone_disabled" - // TODO: b/294868620 - Remove when completing the bug - SanitizersEnabled = "sanitizers_enabled" -) - -func PowerSetWithoutEmptySet[T any](items []T) [][]T { - resultSize := int(math.Pow(2, float64(len(items)))) - powerSet := make([][]T, 0, resultSize-1) - for i := 1; i < resultSize; i++ { - combination := make([]T, 0) - for j := 0; j < len(items); j++ { - if (i>>j)%2 == 1 { - combination = append(combination, items[j]) - } - } - powerSet = append(powerSet, combination) - } - return powerSet -} - -func createPlatformArchMap() map[string]string { - // Copy of archFeatures from android/arch_list.go because the bazel - // package can't access the android package - archFeatures := map[string][]string{ - "arm": { - "neon", - }, - "arm64": { - "dotprod", - }, - "riscv64": {}, - "x86": { - "ssse3", - "sse4", - "sse4_1", - "sse4_2", - "aes_ni", - "avx", - "avx2", - "avx512", - "popcnt", - "movbe", - }, - "x86_64": { - "ssse3", - "sse4", - "sse4_1", - "sse4_2", - "aes_ni", - "avx", - "avx2", - "avx512", - "popcnt", - }, - } - result := make(map[string]string) - for arch, allFeatures := range archFeatures { - result[arch] = "//build/bazel_common_rules/platforms/arch:" + arch - // Sometimes we want to select on multiple features being active, so - // add the power set of all possible features to the map. More details - // in android.ModuleBase.GetArchVariantProperties - for _, features := range PowerSetWithoutEmptySet(allFeatures) { - sort.Strings(features) - archFeaturesName := arch + "-" + strings.Join(features, "-") - result[archFeaturesName] = "//build/bazel/platforms/arch/variants:" + archFeaturesName - } - } - result[ConditionsDefaultConfigKey] = ConditionsDefaultSelectKey - return result -} - -var ( - // These are the list of OSes and architectures with a Bazel config_setting - // and constraint value equivalent. These exist in arch.go, but the android - // package depends on the bazel package, so a cyclic dependency prevents - // using those variables here. - - // A map of architectures to the Bazel label of the constraint_value - // for the @platforms//cpu:cpu constraint_setting - platformArchMap = createPlatformArchMap() - - // A map of target operating systems to the Bazel label of the - // constraint_value for the @platforms//os:os constraint_setting - platformOsMap = map[string]string{ - OsAndroid: "//build/bazel_common_rules/platforms/os:android", - OsDarwin: "//build/bazel_common_rules/platforms/os:darwin", - OsLinux: "//build/bazel_common_rules/platforms/os:linux_glibc", - osLinuxMusl: "//build/bazel_common_rules/platforms/os:linux_musl", - osLinuxBionic: "//build/bazel_common_rules/platforms/os:linux_bionic", - OsWindows: "//build/bazel_common_rules/platforms/os:windows", - ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map. - } - - platformOsArchMap = map[string]string{ - osArchAndroidArm: "//build/bazel_common_rules/platforms/os_arch:android_arm", - OsArchAndroidArm64: "//build/bazel_common_rules/platforms/os_arch:android_arm64", - osArchAndroidRiscv64: "//build/bazel_common_rules/platforms/os_arch:android_riscv64", - osArchAndroidX86: "//build/bazel_common_rules/platforms/os_arch:android_x86", - osArchAndroidX86_64: "//build/bazel_common_rules/platforms/os_arch:android_x86_64", - osArchDarwinArm64: "//build/bazel_common_rules/platforms/os_arch:darwin_arm64", - osArchDarwinX86_64: "//build/bazel_common_rules/platforms/os_arch:darwin_x86_64", - osArchLinuxX86: "//build/bazel_common_rules/platforms/os_arch:linux_glibc_x86", - osArchLinuxX86_64: "//build/bazel_common_rules/platforms/os_arch:linux_glibc_x86_64", - osArchLinuxMuslArm: "//build/bazel_common_rules/platforms/os_arch:linux_musl_arm", - osArchLinuxMuslArm64: "//build/bazel_common_rules/platforms/os_arch:linux_musl_arm64", - osArchLinuxMuslX86: "//build/bazel_common_rules/platforms/os_arch:linux_musl_x86", - osArchLinuxMuslX86_64: "//build/bazel_common_rules/platforms/os_arch:linux_musl_x86_64", - osArchLinuxBionicArm64: "//build/bazel_common_rules/platforms/os_arch:linux_bionic_arm64", - osArchLinuxBionicX86_64: "//build/bazel_common_rules/platforms/os_arch:linux_bionic_x86_64", - osArchWindowsX86: "//build/bazel_common_rules/platforms/os_arch:windows_x86", - osArchWindowsX86_64: "//build/bazel_common_rules/platforms/os_arch:windows_x86_64", - ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map. - } - - // Map where keys are OsType names, and values are slices containing the archs - // that that OS supports. - // These definitions copied from arch.go. - // TODO(cparsons): Source from arch.go; this task is nontrivial, as it currently results - // in a cyclic dependency. - osToArchMap = map[string][]string{ - OsAndroid: {archArm, archArm64, archRiscv64, archX86, archX86_64}, - OsLinux: {archX86, archX86_64}, - osLinuxMusl: {archX86, archX86_64}, - OsDarwin: {archArm64, archX86_64}, - osLinuxBionic: {archArm64, archX86_64}, - // TODO(cparsons): According to arch.go, this should contain archArm, archArm64, as well. - OsWindows: {archX86, archX86_64}, - } - - osAndInApexMap = map[string]string{ - AndroidAndInApex: "//build/bazel/rules/apex:android-in_apex", - AndroidPlatform: "//build/bazel/rules/apex:system", - Unbundled_app: "//build/bazel/rules/apex:unbundled_app", - OsDarwin: "//build/bazel_common_rules/platforms/os:darwin", - OsLinux: "//build/bazel_common_rules/platforms/os:linux_glibc", - osLinuxMusl: "//build/bazel_common_rules/platforms/os:linux_musl", - osLinuxBionic: "//build/bazel_common_rules/platforms/os:linux_bionic", - OsWindows: "//build/bazel_common_rules/platforms/os:windows", - ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, - } - - inApexMap = map[string]string{ - InApex: "//build/bazel/rules/apex:in_apex", - NonApex: "//build/bazel/rules/apex:non_apex", - ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, - } - - errorProneMap = map[string]string{ - ErrorproneDisabled: "//build/bazel/rules/java/errorprone:errorprone_globally_disabled", - ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, - } - - // TODO: b/294868620 - Remove when completing the bug - sanitizersEnabledMap = map[string]string{ - SanitizersEnabled: "//build/bazel/rules/cc:sanitizers_enabled", - ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, - } -) - -// basic configuration types -type configurationType int - -const ( - noConfig configurationType = iota - arch - os - osArch - productVariables - osAndInApex - inApex - errorProneDisabled - // TODO: b/294868620 - Remove when completing the bug - sanitizersEnabled -) - -func osArchString(os string, arch string) string { - return fmt.Sprintf("%s_%s", os, arch) -} - -func (ct configurationType) String() string { - return map[configurationType]string{ - noConfig: "no_config", - arch: "arch", - os: "os", - osArch: "arch_os", - productVariables: "product_variables", - osAndInApex: "os_in_apex", - inApex: "in_apex", - errorProneDisabled: "errorprone_disabled", - // TODO: b/294868620 - Remove when completing the bug - sanitizersEnabled: "sanitizers_enabled", - }[ct] -} - -func (ct configurationType) validateConfig(config string) { - switch ct { - case noConfig: - if config != "" { - panic(fmt.Errorf("Cannot specify config with %s, but got %s", ct, config)) - } - case arch: - if _, ok := platformArchMap[config]; !ok { - panic(fmt.Errorf("Unknown arch: %s", config)) - } - case os: - if _, ok := platformOsMap[config]; !ok { - panic(fmt.Errorf("Unknown os: %s", config)) - } - case osArch: - if _, ok := platformOsArchMap[config]; !ok { - panic(fmt.Errorf("Unknown os+arch: %s", config)) - } - case productVariables: - // do nothing - case osAndInApex: - // do nothing - // this axis can contain additional per-apex keys - case inApex: - if _, ok := inApexMap[config]; !ok { - panic(fmt.Errorf("Unknown in_apex config: %s", config)) - } - case errorProneDisabled: - if _, ok := errorProneMap[config]; !ok { - panic(fmt.Errorf("Unknown errorprone config: %s", config)) - } - // TODO: b/294868620 - Remove when completing the bug - case sanitizersEnabled: - if _, ok := sanitizersEnabledMap[config]; !ok { - panic(fmt.Errorf("Unknown sanitizers_enabled config: %s", config)) - } - default: - panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct)) - } -} - -// SelectKey returns the Bazel select key for a given configurationType and config string. -func (ca ConfigurationAxis) SelectKey(config string) string { - ca.validateConfig(config) - switch ca.configurationType { - case noConfig: - panic(fmt.Errorf("SelectKey is unnecessary for noConfig ConfigurationType ")) - case arch: - return platformArchMap[config] - case os: - return platformOsMap[config] - case osArch: - return platformOsArchMap[config] - case productVariables: - if config == ConditionsDefaultConfigKey { - return ConditionsDefaultSelectKey - } - return fmt.Sprintf("%s:%s", productVariableBazelPackage, config) - case osAndInApex: - if ret, exists := osAndInApexMap[config]; exists { - return ret - } - return config - case inApex: - return inApexMap[config] - case errorProneDisabled: - return errorProneMap[config] - // TODO: b/294868620 - Remove when completing the bug - case sanitizersEnabled: - return sanitizersEnabledMap[config] - default: - panic(fmt.Errorf("Unrecognized ConfigurationType %d", ca.configurationType)) - } -} - -var ( - // Indicating there is no configuration axis - NoConfigAxis = ConfigurationAxis{configurationType: noConfig} - // An axis for architecture-specific configurations - ArchConfigurationAxis = ConfigurationAxis{configurationType: arch} - // An axis for os-specific configurations - OsConfigurationAxis = ConfigurationAxis{configurationType: os} - // An axis for arch+os-specific configurations - OsArchConfigurationAxis = ConfigurationAxis{configurationType: osArch} - // An axis for os+in_apex-specific configurations - OsAndInApexAxis = ConfigurationAxis{configurationType: osAndInApex} - // An axis for in_apex-specific configurations - InApexAxis = ConfigurationAxis{configurationType: inApex} - - ErrorProneAxis = ConfigurationAxis{configurationType: errorProneDisabled} - - // TODO: b/294868620 - Remove when completing the bug - SanitizersEnabledAxis = ConfigurationAxis{configurationType: sanitizersEnabled} -) - -// ProductVariableConfigurationAxis returns an axis for the given product variable -func ProductVariableConfigurationAxis(archVariant bool, variable string) ConfigurationAxis { - return ConfigurationAxis{ - configurationType: productVariables, - subType: variable, - archVariant: archVariant, - } -} - -// ConfigurationAxis is an independent axis for configuration, there should be no overlap between -// elements within an axis. -type ConfigurationAxis struct { - configurationType - // some configuration types (e.g. productVariables) have multiple independent axes, subType helps - // distinguish between them without needing to list all 17 product variables. - subType string - - archVariant bool -} - -func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool { - if ca.configurationType == other.configurationType { - return ca.subType < other.subType - } - return ca.configurationType < other.configurationType -} diff --git a/bazel/properties.go b/bazel/properties.go deleted file mode 100644 index 9c63bc04b..000000000 --- a/bazel/properties.go +++ /dev/null @@ -1,1467 +0,0 @@ -// Copyright 2020 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bazel - -import ( - "fmt" - "path/filepath" - "reflect" - "regexp" - "sort" - "strings" - - "github.com/google/blueprint" -) - -// BazelTargetModuleProperties contain properties and metadata used for -// Blueprint to BUILD file conversion. -type BazelTargetModuleProperties struct { - // The Bazel rule class for this target. - Rule_class string `blueprint:"mutated"` - - // The target label for the bzl file containing the definition of the rule class. - Bzl_load_location string `blueprint:"mutated"` -} - -var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)") - -// Label is used to represent a Bazel compatible Label. Also stores the original -// bp text to support string replacement. -type Label struct { - // The string representation of a Bazel target label. This can be a relative - // or fully qualified label. These labels are used for generating BUILD - // files with bp2build. - Label string - - // The original Soong/Blueprint module name that the label was derived from. - // This is used for replacing references to the original name with the new - // label, for example in genrule cmds. - // - // While there is a reversible 1:1 mapping from the module name to Bazel - // label with bp2build that could make computing the original module name - // from the label automatic, it is not the case for handcrafted targets, - // where modules can have a custom label mapping through the { bazel_module: - // { label: <label> } } property. - // - // With handcrafted labels, those modules don't go through bp2build - // conversion, but relies on handcrafted targets in the source tree. - OriginalModuleName string -} - -// LabelList is used to represent a list of Bazel labels. -type LabelList struct { - Includes []Label - Excludes []Label -} - -// MakeLabelList creates a LabelList from a list Label -func MakeLabelList(labels []Label) LabelList { - return LabelList{ - Includes: labels, - Excludes: nil, - } -} - -func SortedConfigurationAxes[T any](m map[ConfigurationAxis]T) []ConfigurationAxis { - keys := make([]ConfigurationAxis, 0, len(m)) - for k := range m { - keys = append(keys, k) - } - - sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) }) - return keys -} - -// MakeLabelListFromTargetNames creates a LabelList from unqualified target names -// This is a utiltity function for bp2build converters of Soong modules that have 1:many generated targets -func MakeLabelListFromTargetNames(targetNames []string) LabelList { - labels := []Label{} - for _, name := range targetNames { - label := Label{Label: ":" + name} - labels = append(labels, label) - } - return MakeLabelList(labels) -} - -func (ll *LabelList) Equals(other LabelList) bool { - if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) { - return false - } - for i, _ := range ll.Includes { - if ll.Includes[i] != other.Includes[i] { - return false - } - } - for i, _ := range ll.Excludes { - if ll.Excludes[i] != other.Excludes[i] { - return false - } - } - return true -} - -func (ll *LabelList) IsNil() bool { - return ll.Includes == nil && ll.Excludes == nil -} - -func (ll *LabelList) IsEmpty() bool { - return len(ll.Includes) == 0 && len(ll.Excludes) == 0 -} - -func (ll *LabelList) deepCopy() LabelList { - return LabelList{ - Includes: ll.Includes[:], - Excludes: ll.Excludes[:], - } -} - -// uniqueParentDirectories returns a list of the unique parent directories for -// all files in ll.Includes. -func (ll *LabelList) uniqueParentDirectories() []string { - dirMap := map[string]bool{} - for _, label := range ll.Includes { - dirMap[filepath.Dir(label.Label)] = true - } - dirs := []string{} - for dir := range dirMap { - dirs = append(dirs, dir) - } - return dirs -} - -// Add inserts the label Label at the end of the LabelList.Includes. -func (ll *LabelList) Add(label *Label) { - if label == nil { - return - } - ll.Includes = append(ll.Includes, *label) -} - -// AddExclude inserts the label Label at the end of the LabelList.Excludes. -func (ll *LabelList) AddExclude(label *Label) { - if label == nil { - return - } - ll.Excludes = append(ll.Excludes, *label) -} - -// Append appends the fields of other labelList to the corresponding fields of ll. -func (ll *LabelList) Append(other LabelList) { - if len(ll.Includes) > 0 || len(other.Includes) > 0 { - ll.Includes = append(ll.Includes, other.Includes...) - } - if len(ll.Excludes) > 0 || len(other.Excludes) > 0 { - ll.Excludes = append(ll.Excludes, other.Excludes...) - } -} - -// Partition splits a LabelList into two LabelLists depending on the return value -// of the predicate. -// This function preserves the Includes and Excludes, but it does not provide -// that information to the partition function. -func (ll *LabelList) Partition(predicate func(label Label) bool) (LabelList, LabelList) { - predicated := LabelList{} - unpredicated := LabelList{} - for _, include := range ll.Includes { - if predicate(include) { - predicated.Add(&include) - } else { - unpredicated.Add(&include) - } - } - for _, exclude := range ll.Excludes { - if predicate(exclude) { - predicated.AddExclude(&exclude) - } else { - unpredicated.AddExclude(&exclude) - } - } - return predicated, unpredicated -} - -// UniqueSortedBazelLabels takes a []Label and deduplicates the labels, and returns -// the slice in a sorted order. -func UniqueSortedBazelLabels(originalLabels []Label) []Label { - uniqueLabels := FirstUniqueBazelLabels(originalLabels) - sort.SliceStable(uniqueLabels, func(i, j int) bool { - return uniqueLabels[i].Label < uniqueLabels[j].Label - }) - return uniqueLabels -} - -func FirstUniqueBazelLabels(originalLabels []Label) []Label { - var labels []Label - found := make(map[string]bool, len(originalLabels)) - for _, l := range originalLabels { - if _, ok := found[l.Label]; ok { - continue - } - labels = append(labels, l) - found[l.Label] = true - } - return labels -} - -func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList { - var uniqueLabelList LabelList - uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes) - uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes) - return uniqueLabelList -} - -func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList { - var uniqueLabelList LabelList - uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes) - uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes) - return uniqueLabelList -} - -// Subtract needle from haystack -func SubtractStrings(haystack []string, needle []string) []string { - // This is really a set - needleMap := make(map[string]bool) - for _, s := range needle { - needleMap[s] = true - } - - var strings []string - for _, s := range haystack { - if exclude := needleMap[s]; !exclude { - strings = append(strings, s) - } - } - - return strings -} - -// Subtract needle from haystack -func SubtractBazelLabels(haystack []Label, needle []Label) []Label { - // This is really a set - needleMap := make(map[Label]bool) - for _, s := range needle { - needleMap[s] = true - } - - var labels []Label - for _, label := range haystack { - if exclude := needleMap[label]; !exclude { - labels = append(labels, label) - } - } - - return labels -} - -// Appends two LabelLists, returning the combined list. -func AppendBazelLabelLists(a LabelList, b LabelList) LabelList { - var result LabelList - result.Includes = append(a.Includes, b.Includes...) - result.Excludes = append(a.Excludes, b.Excludes...) - return result -} - -// Subtract needle from haystack -func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList { - var result LabelList - result.Includes = SubtractBazelLabels(haystack.Includes, needle.Includes) - // NOTE: Excludes are intentionally not subtracted - result.Excludes = haystack.Excludes - return result -} - -// FirstUniqueBazelLabelListAttribute takes a LabelListAttribute and makes the LabelList for -// each axis/configuration by keeping the first instance of a Label and omitting all subsequent -// repetitions. -func FirstUniqueBazelLabelListAttribute(attr LabelListAttribute) LabelListAttribute { - var result LabelListAttribute - result.Value = FirstUniqueBazelLabelList(attr.Value) - if attr.HasConfigurableValues() { - result.ConfigurableValues = make(configurableLabelLists) - } - for axis, configToLabels := range attr.ConfigurableValues { - for c, l := range configToLabels { - result.SetSelectValue(axis, c, FirstUniqueBazelLabelList(l)) - } - } - - return result -} - -// SubtractBazelLabelListAttribute subtract needle from haystack for LabelList in each -// axis/configuration. -func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute { - var result LabelListAttribute - result.Value = SubtractBazelLabelList(haystack.Value, needle.Value) - if haystack.HasConfigurableValues() { - result.ConfigurableValues = make(configurableLabelLists) - } - for axis, configToLabels := range haystack.ConfigurableValues { - for haystackConfig, haystackLabels := range configToLabels { - result.SetSelectValue(axis, haystackConfig, SubtractBazelLabelList(haystackLabels, needle.SelectValue(axis, haystackConfig))) - } - } - - return result -} - -type Attribute interface { - HasConfigurableValues() bool -} - -type labelSelectValues map[string]*Label - -type configurableLabels map[ConfigurationAxis]labelSelectValues - -func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) { - if cl[axis] == nil { - cl[axis] = make(labelSelectValues) - } - cl[axis][config] = value -} - -// Represents an attribute whose value is a single label -type LabelAttribute struct { - Value *Label - - ConfigurableValues configurableLabels -} - -func (la *LabelAttribute) axisTypes() map[configurationType]bool { - types := map[configurationType]bool{} - for k := range la.ConfigurableValues { - if len(la.ConfigurableValues[k]) > 0 { - types[k.configurationType] = true - } - } - return types -} - -// Collapse reduces the configurable axes of the label attribute to a single axis. -// This is necessary for final writing to bp2build, as a configurable label -// attribute can only be comprised by a single select. -func (la *LabelAttribute) Collapse() error { - axisTypes := la.axisTypes() - _, containsOs := axisTypes[os] - _, containsArch := axisTypes[arch] - _, containsOsArch := axisTypes[osArch] - _, containsProductVariables := axisTypes[productVariables] - if containsProductVariables { - if containsOs || containsArch || containsOsArch { - if containsArch { - allProductVariablesAreArchVariant := true - for k := range la.ConfigurableValues { - if k.configurationType == productVariables && !k.archVariant { - allProductVariablesAreArchVariant = false - } - } - if !allProductVariablesAreArchVariant { - return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes") - } - } else { - return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes") - } - } - } - if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) { - // If a bool attribute has both os and arch configuration axes, the only - // way to successfully union their values is to increase the granularity - // of the configuration criteria to os_arch. - for osType, supportedArchs := range osToArchMap { - for _, supportedArch := range supportedArchs { - osArch := osArchString(osType, supportedArch) - if archOsVal := la.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil { - // Do nothing, as the arch_os is explicitly defined already. - } else { - archVal := la.SelectValue(ArchConfigurationAxis, supportedArch) - osVal := la.SelectValue(OsConfigurationAxis, osType) - if osVal != nil && archVal != nil { - // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator - // runs after os mutator. - la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal) - } else if osVal != nil && archVal == nil { - la.SetSelectValue(OsArchConfigurationAxis, osArch, *osVal) - } else if osVal == nil && archVal != nil { - la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal) - } - } - } - } - // All os_arch values are now set. Clear os and arch axes. - delete(la.ConfigurableValues, ArchConfigurationAxis) - delete(la.ConfigurableValues, OsConfigurationAxis) - } - return nil -} - -// HasConfigurableValues returns whether there are configurable values set for this label. -func (la LabelAttribute) HasConfigurableValues() bool { - for _, selectValues := range la.ConfigurableValues { - if len(selectValues) > 0 { - return true - } - } - return false -} - -// SetValue sets the base, non-configured value for the Label -func (la *LabelAttribute) SetValue(value Label) { - la.SetSelectValue(NoConfigAxis, "", value) -} - -// SetSelectValue set a value for a bazel select for the given axis, config and value. -func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) { - axis.validateConfig(config) - switch axis.configurationType { - case noConfig: - la.Value = &value - case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled: - if la.ConfigurableValues == nil { - la.ConfigurableValues = make(configurableLabels) - } - la.ConfigurableValues.setValueForAxis(axis, config, &value) - default: - panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) - } -} - -// SelectValue gets a value for a bazel select for the given axis and config. -func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) *Label { - axis.validateConfig(config) - switch axis.configurationType { - case noConfig: - return la.Value - case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled: - return la.ConfigurableValues[axis][config] - default: - panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) - } -} - -// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order. -func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis { - return SortedConfigurationAxes(la.ConfigurableValues) -} - -// MakeLabelAttribute turns a string into a LabelAttribute -func MakeLabelAttribute(label string) *LabelAttribute { - return &LabelAttribute{ - Value: &Label{ - Label: label, - }, - } -} - -type configToBools map[string]bool - -func (ctb configToBools) setValue(config string, value *bool) { - if value == nil { - if _, ok := ctb[config]; ok { - delete(ctb, config) - } - return - } - ctb[config] = *value -} - -type configurableBools map[ConfigurationAxis]configToBools - -func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) { - if cb[axis] == nil { - cb[axis] = make(configToBools) - } - cb[axis].setValue(config, value) -} - -// BoolAttribute represents an attribute whose value is a single bool but may be configurable.. -type BoolAttribute struct { - Value *bool - - ConfigurableValues configurableBools -} - -// HasConfigurableValues returns whether there are configurable values for this attribute. -func (ba BoolAttribute) HasConfigurableValues() bool { - for _, cfgToBools := range ba.ConfigurableValues { - if len(cfgToBools) > 0 { - return true - } - } - return false -} - -// SetValue sets value for the no config axis -func (ba *BoolAttribute) SetValue(value *bool) { - ba.SetSelectValue(NoConfigAxis, "", value) -} - -// SetSelectValue sets value for the given axis/config. -func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) { - axis.validateConfig(config) - switch axis.configurationType { - case noConfig: - ba.Value = value - case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled: - if ba.ConfigurableValues == nil { - ba.ConfigurableValues = make(configurableBools) - } - ba.ConfigurableValues.setValueForAxis(axis, config, value) - default: - panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) - } -} - -// ToLabelListAttribute creates and returns a LabelListAttribute from this -// bool attribute, where each bool in this attribute corresponds to a -// label list value in the resultant attribute. -func (ba *BoolAttribute) ToLabelListAttribute(falseVal LabelList, trueVal LabelList) (LabelListAttribute, error) { - getLabelList := func(boolPtr *bool) LabelList { - if boolPtr == nil { - return LabelList{nil, nil} - } else if *boolPtr { - return trueVal - } else { - return falseVal - } - } - - mainVal := getLabelList(ba.Value) - if !ba.HasConfigurableValues() { - return MakeLabelListAttribute(mainVal), nil - } - - result := LabelListAttribute{} - if err := ba.Collapse(); err != nil { - return result, err - } - - for axis, configToBools := range ba.ConfigurableValues { - if len(configToBools) < 1 { - continue - } - for config, boolPtr := range configToBools { - val := getLabelList(&boolPtr) - if !val.Equals(mainVal) { - result.SetSelectValue(axis, config, val) - } - } - result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal) - } - - return result, nil -} - -// ToStringListAttribute creates a StringListAttribute from this BoolAttribute, -// where each bool corresponds to a string list value generated by the provided -// function. -// TODO(b/271425661): Generalize this -func (ba *BoolAttribute) ToStringListAttribute(valueFunc func(boolPtr *bool, axis ConfigurationAxis, config string) []string) (StringListAttribute, error) { - mainVal := valueFunc(ba.Value, NoConfigAxis, "") - if !ba.HasConfigurableValues() { - return MakeStringListAttribute(mainVal), nil - } - - result := StringListAttribute{} - if err := ba.Collapse(); err != nil { - return result, err - } - - for axis, configToBools := range ba.ConfigurableValues { - if len(configToBools) < 1 { - continue - } - for config, boolPtr := range configToBools { - val := valueFunc(&boolPtr, axis, config) - if !reflect.DeepEqual(val, mainVal) { - result.SetSelectValue(axis, config, val) - } - } - result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal) - } - - return result, nil -} - -// Collapse reduces the configurable axes of the boolean attribute to a single axis. -// This is necessary for final writing to bp2build, as a configurable boolean -// attribute can only be comprised by a single select. -func (ba *BoolAttribute) Collapse() error { - axisTypes := ba.axisTypes() - _, containsOs := axisTypes[os] - _, containsArch := axisTypes[arch] - _, containsOsArch := axisTypes[osArch] - _, containsProductVariables := axisTypes[productVariables] - if containsProductVariables { - if containsOs || containsArch || containsOsArch { - return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes") - } - } - if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) { - // If a bool attribute has both os and arch configuration axes, the only - // way to successfully union their values is to increase the granularity - // of the configuration criteria to os_arch. - for osType, supportedArchs := range osToArchMap { - for _, supportedArch := range supportedArchs { - osArch := osArchString(osType, supportedArch) - if archOsVal := ba.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil { - // Do nothing, as the arch_os is explicitly defined already. - } else { - archVal := ba.SelectValue(ArchConfigurationAxis, supportedArch) - osVal := ba.SelectValue(OsConfigurationAxis, osType) - if osVal != nil && archVal != nil { - // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator - // runs after os mutator. - ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal) - } else if osVal != nil && archVal == nil { - ba.SetSelectValue(OsArchConfigurationAxis, osArch, osVal) - } else if osVal == nil && archVal != nil { - ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal) - } - } - } - } - // All os_arch values are now set. Clear os and arch axes. - delete(ba.ConfigurableValues, ArchConfigurationAxis) - delete(ba.ConfigurableValues, OsConfigurationAxis) - // Verify post-condition; this should never fail, provided no additional - // axes are introduced. - if len(ba.ConfigurableValues) > 1 { - panic(fmt.Errorf("error in collapsing attribute: %#v", ba)) - } - } - return nil -} - -func (ba *BoolAttribute) axisTypes() map[configurationType]bool { - types := map[configurationType]bool{} - for k := range ba.ConfigurableValues { - if len(ba.ConfigurableValues[k]) > 0 { - types[k.configurationType] = true - } - } - return types -} - -// SelectValue gets the value for the given axis/config. -func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool { - axis.validateConfig(config) - switch axis.configurationType { - case noConfig: - return ba.Value - case arch, os, osArch, productVariables, osAndInApex, sanitizersEnabled: - if v, ok := ba.ConfigurableValues[axis][config]; ok { - return &v - } else { - return nil - } - default: - panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) - } -} - -// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order. -func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis { - return SortedConfigurationAxes(ba.ConfigurableValues) -} - -// labelListSelectValues supports config-specific label_list typed Bazel attribute values. -type labelListSelectValues map[string]LabelList - -func (ll labelListSelectValues) addSelects(label labelSelectValues) { - for k, v := range label { - if label == nil { - continue - } - l := ll[k] - (&l).Add(v) - ll[k] = l - } -} - -func (ll labelListSelectValues) appendSelects(other labelListSelectValues, forceSpecifyEmptyList bool) { - for k, v := range other { - l := ll[k] - if forceSpecifyEmptyList && l.IsNil() && !v.IsNil() { - l.Includes = []Label{} - } - (&l).Append(v) - ll[k] = l - } -} - -// HasConfigurableValues returns whether there are configurable values within this set of selects. -func (ll labelListSelectValues) HasConfigurableValues() bool { - for _, v := range ll { - if v.Includes != nil { - return true - } - } - return false -} - -// LabelListAttribute is used to represent a list of Bazel labels as an -// attribute. -type LabelListAttribute struct { - // The non-configured attribute label list Value. Required. - Value LabelList - - // The configured attribute label list Values. Optional - // a map of independent configurability axes - ConfigurableValues configurableLabelLists - - // If true, differentiate between "nil" and "empty" list. nil means that - // this attribute should not be specified at all, and "empty" means that - // the attribute should be explicitly specified as an empty list. - // This mode facilitates use of attribute defaults: an empty list should - // override the default. - ForceSpecifyEmptyList bool - - // If true, signal the intent to the code generator to emit all select keys, - // even if the Includes list for that key is empty. This mode facilitates - // specific select statements where an empty list for a non-default select - // key has a meaning. - EmitEmptyList bool - - // If a property has struct tag "variant_prepend", this value should - // be set to True, so that when bp2build generates BUILD.bazel, variant - // properties(select ...) come before general properties. - Prepend bool -} - -type configurableLabelLists map[ConfigurationAxis]labelListSelectValues - -func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) { - if list.IsNil() { - if _, ok := cll[axis][config]; ok { - delete(cll[axis], config) - } - return - } - if cll[axis] == nil { - cll[axis] = make(labelListSelectValues) - } - - cll[axis][config] = list -} - -func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) { - for axis, otherSelects := range other { - selects := cll[axis] - if selects == nil { - selects = make(labelListSelectValues, len(otherSelects)) - } - selects.appendSelects(otherSelects, forceSpecifyEmptyList) - cll[axis] = selects - } -} - -func (lla *LabelListAttribute) Clone() *LabelListAttribute { - result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList} - return result.Append(*lla) -} - -// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value. -func MakeLabelListAttribute(value LabelList) LabelListAttribute { - return LabelListAttribute{ - Value: value, - ConfigurableValues: make(configurableLabelLists), - } -} - -// MakeSingleLabelListAttribute initializes a LabelListAttribute as a non-arch specific list with 1 element, the given Label. -func MakeSingleLabelListAttribute(value Label) LabelListAttribute { - return MakeLabelListAttribute(MakeLabelList([]Label{value})) -} - -func (lla *LabelListAttribute) SetValue(list LabelList) { - lla.SetSelectValue(NoConfigAxis, "", list) -} - -// SetSelectValue set a value for a bazel select for the given axis, config and value. -func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) { - axis.validateConfig(config) - switch axis.configurationType { - case noConfig: - lla.Value = list - case arch, os, osArch, productVariables, osAndInApex, inApex, errorProneDisabled, sanitizersEnabled: - if lla.ConfigurableValues == nil { - lla.ConfigurableValues = make(configurableLabelLists) - } - lla.ConfigurableValues.setValueForAxis(axis, config, list) - default: - panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) - } -} - -// SelectValue gets a value for a bazel select for the given axis and config. -func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList { - axis.validateConfig(config) - switch axis.configurationType { - case noConfig: - return lla.Value - case arch, os, osArch, productVariables, osAndInApex, inApex, errorProneDisabled, sanitizersEnabled: - return lla.ConfigurableValues[axis][config] - default: - panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) - } -} - -// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order. -func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis { - return SortedConfigurationAxes(lla.ConfigurableValues) -} - -// Append all values, including os and arch specific ones, from another -// LabelListAttribute to this LabelListAttribute. Returns this LabelListAttribute. -func (lla *LabelListAttribute) Append(other LabelListAttribute) *LabelListAttribute { - forceSpecifyEmptyList := lla.ForceSpecifyEmptyList || other.ForceSpecifyEmptyList - if forceSpecifyEmptyList && lla.Value.IsNil() && !other.Value.IsNil() { - lla.Value.Includes = []Label{} - } - lla.Value.Append(other.Value) - if lla.ConfigurableValues == nil { - lla.ConfigurableValues = make(configurableLabelLists) - } - lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList) - return lla -} - -// Add inserts the labels for each axis of LabelAttribute at the end of corresponding axis's -// LabelList within the LabelListAttribute -func (lla *LabelListAttribute) Add(label *LabelAttribute) { - if label == nil { - return - } - - lla.Value.Add(label.Value) - if lla.ConfigurableValues == nil && label.ConfigurableValues != nil { - lla.ConfigurableValues = make(configurableLabelLists) - } - for axis, _ := range label.ConfigurableValues { - if _, exists := lla.ConfigurableValues[axis]; !exists { - lla.ConfigurableValues[axis] = make(labelListSelectValues) - } - lla.ConfigurableValues[axis].addSelects(label.ConfigurableValues[axis]) - } -} - -// HasConfigurableValues returns true if the attribute contains axis-specific label list values. -func (lla LabelListAttribute) HasConfigurableValues() bool { - for _, selectValues := range lla.ConfigurableValues { - if len(selectValues) > 0 { - return true - } - } - return false -} - -// HasAxisSpecificValues returns true if the attribute contains axis specific label list values from a given axis -func (lla LabelListAttribute) HasAxisSpecificValues(axis ConfigurationAxis) bool { - for _, values := range lla.ConfigurableValues[axis] { - if !values.IsNil() { - return true - } - } - return false -} - -// IsEmpty returns true if the attribute has no values under any configuration. -func (lla LabelListAttribute) IsEmpty() bool { - if len(lla.Value.Includes) > 0 { - return false - } - for axis, _ := range lla.ConfigurableValues { - if lla.ConfigurableValues[axis].HasConfigurableValues() { - return false - } - } - return true -} - -// IsNil returns true if the attribute has not been set for any configuration. -func (lla LabelListAttribute) IsNil() bool { - if lla.Value.Includes != nil { - return false - } - return !lla.HasConfigurableValues() -} - -// Exclude for the given axis, config, removes Includes in labelList from Includes and appends them -// to Excludes. This is to special case any excludes that are not specified in a bp file but need to -// be removed, e.g. if they could cause duplicate element failures. -func (lla *LabelListAttribute) Exclude(axis ConfigurationAxis, config string, labelList LabelList) { - val := lla.SelectValue(axis, config) - newList := SubtractBazelLabelList(val, labelList) - newList.Excludes = append(newList.Excludes, labelList.Includes...) - lla.SetSelectValue(axis, config, newList) -} - -// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from -// the base value and included in default values as appropriate. -func (lla *LabelListAttribute) ResolveExcludes() { - // If there are OsAndInApexAxis, we need to use - // * includes from the OS & in APEX Axis for non-Android configs for libraries that need to be - // included in non-Android OSes - // * excludes from the OS Axis for non-Android configs, to exclude libraries that should _not_ - // be included in the non-Android OSes - if _, ok := lla.ConfigurableValues[OsAndInApexAxis]; ok { - inApexLabels := lla.ConfigurableValues[OsAndInApexAxis][ConditionsDefaultConfigKey] - for config, labels := range lla.ConfigurableValues[OsConfigurationAxis] { - // OsAndroid has already handled its excludes. - // We only need to copy the excludes from other arches, so if there are none, skip it. - if config == OsAndroid || len(labels.Excludes) == 0 { - continue - } - lla.ConfigurableValues[OsAndInApexAxis][config] = LabelList{ - Includes: inApexLabels.Includes, - Excludes: labels.Excludes, - } - } - } - - for axis, configToLabels := range lla.ConfigurableValues { - baseLabels := lla.Value.deepCopy() - for config, val := range configToLabels { - // Exclude config-specific excludes from base value - lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes}) - - // add base values to config specific to add labels excluded by others in this axis - // then remove all config-specific excludes - allLabels := baseLabels.deepCopy() - allLabels.Append(val) - lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: allLabels.Excludes}) - } - - // After going through all configs, delete the duplicates in the config - // values that are already in the base Value. - for config, val := range configToLabels { - lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value) - } - - // Now that the Value list is finalized for this axis, compare it with - // the original list, and union the difference with the default - // condition for the axis. - difference := SubtractBazelLabelList(baseLabels, lla.Value) - existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] - existingDefaults.Append(difference) - lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults) - - // if everything ends up without includes, just delete the axis - if !lla.ConfigurableValues[axis].HasConfigurableValues() { - delete(lla.ConfigurableValues, axis) - } - } -} - -// Partition splits a LabelListAttribute into two LabelListAttributes depending -// on the return value of the predicate. -// This function preserves the Includes and Excludes, but it does not provide -// that information to the partition function. -func (lla LabelListAttribute) Partition(predicate func(label Label) bool) (LabelListAttribute, LabelListAttribute) { - predicated := LabelListAttribute{} - unpredicated := LabelListAttribute{} - - valuePartitionTrue, valuePartitionFalse := lla.Value.Partition(predicate) - predicated.SetValue(valuePartitionTrue) - unpredicated.SetValue(valuePartitionFalse) - - for axis, selectValueLabelLists := range lla.ConfigurableValues { - for config, labelList := range selectValueLabelLists { - configPredicated, configUnpredicated := labelList.Partition(predicate) - predicated.SetSelectValue(axis, config, configPredicated) - unpredicated.SetSelectValue(axis, config, configUnpredicated) - } - } - - return predicated, unpredicated -} - -// OtherModuleContext is a limited context that has methods with information about other modules. -type OtherModuleContext interface { - ModuleFromName(name string) (blueprint.Module, bool) - OtherModuleType(m blueprint.Module) string - OtherModuleName(m blueprint.Module) string - OtherModuleDir(m blueprint.Module) string - ModuleErrorf(fmt string, args ...interface{}) -} - -// LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed) -// label and whether it was changed. -type LabelMapper func(OtherModuleContext, Label) (string, bool) - -// LabelPartition contains descriptions of a partition for labels -type LabelPartition struct { - // Extensions to include in this partition - Extensions []string - // LabelMapper is a function that can map a label to a new label, and indicate whether to include - // the mapped label in the partition - LabelMapper LabelMapper - // Whether to store files not included in any other partition in a group of LabelPartitions - // Only one partition in a group of LabelPartitions can enabled Keep_remainder - Keep_remainder bool -} - -// LabelPartitions is a map of partition name to a LabelPartition describing the elements of the -// partition -type LabelPartitions map[string]LabelPartition - -// filter returns a pointer to a label if the label should be included in the partition or nil if -// not. -func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label { - if lf.LabelMapper != nil { - if newLabel, changed := lf.LabelMapper(ctx, label); changed { - return &Label{newLabel, label.OriginalModuleName} - } - } - for _, ext := range lf.Extensions { - if strings.HasSuffix(label.Label, ext) { - return &label - } - } - - return nil -} - -// PartitionToLabelListAttribute is map of partition name to a LabelListAttribute -type PartitionToLabelListAttribute map[string]LabelListAttribute - -type partitionToLabelList map[string]*LabelList - -func (p partitionToLabelList) appendIncludes(partition string, label Label) { - if _, ok := p[partition]; !ok { - p[partition] = &LabelList{} - } - p[partition].Includes = append(p[partition].Includes, label) -} - -func (p partitionToLabelList) excludes(partition string, excludes []Label) { - if _, ok := p[partition]; !ok { - p[partition] = &LabelList{} - } - p[partition].Excludes = excludes -} - -// PartitionLabelListAttribute partitions a LabelListAttribute into the requested partitions -func PartitionLabelListAttribute(ctx OtherModuleContext, lla *LabelListAttribute, partitions LabelPartitions) PartitionToLabelListAttribute { - ret := PartitionToLabelListAttribute{} - var partitionNames []string - // Stored as a pointer to distinguish nil (no remainder partition) from empty string partition - var remainderPartition *string - for p, f := range partitions { - partitionNames = append(partitionNames, p) - if f.Keep_remainder { - if remainderPartition != nil { - panic("only one partition can store the remainder") - } - // If we take the address of p in a loop, we'll end up with the last value of p in - // remainderPartition, we want the requested partition - capturePartition := p - remainderPartition = &capturePartition - } - } - - partitionLabelList := func(axis ConfigurationAxis, config string) { - value := lla.SelectValue(axis, config) - partitionToLabels := partitionToLabelList{} - for _, item := range value.Includes { - wasFiltered := false - var inPartition *string - for partition, f := range partitions { - filtered := f.filter(ctx, item) - if filtered == nil { - // did not match this filter, keep looking - continue - } - wasFiltered = true - partitionToLabels.appendIncludes(partition, *filtered) - // don't need to check other partitions if this filter used the item, - // continue checking if mapped to another name - if *filtered == item { - if inPartition != nil { - ctx.ModuleErrorf("%q was found in multiple partitions: %q, %q", item.Label, *inPartition, partition) - } - capturePartition := partition - inPartition = &capturePartition - } - } - - // if not specified in a partition, add to remainder partition if one exists - if !wasFiltered && remainderPartition != nil { - partitionToLabels.appendIncludes(*remainderPartition, item) - } - } - - // ensure empty lists are maintained - if value.Excludes != nil { - for _, partition := range partitionNames { - partitionToLabels.excludes(partition, value.Excludes) - } - } - - for partition, list := range partitionToLabels { - val := ret[partition] - (&val).SetSelectValue(axis, config, *list) - ret[partition] = val - } - } - - partitionLabelList(NoConfigAxis, "") - for axis, configToList := range lla.ConfigurableValues { - for config, _ := range configToList { - partitionLabelList(axis, config) - } - } - return ret -} - -// StringAttribute corresponds to the string Bazel attribute type with -// support for additional metadata, like configurations. -type StringAttribute struct { - // The base value of the string attribute. - Value *string - - // The configured attribute label list Values. Optional - // a map of independent configurability axes - ConfigurableValues configurableStrings -} - -type configurableStrings map[ConfigurationAxis]stringSelectValues - -func (cs configurableStrings) setValueForAxis(axis ConfigurationAxis, config string, str *string) { - if cs[axis] == nil { - cs[axis] = make(stringSelectValues) - } - cs[axis][config] = str -} - -type stringSelectValues map[string]*string - -// HasConfigurableValues returns true if the attribute contains axis-specific string values. -func (sa StringAttribute) HasConfigurableValues() bool { - for _, selectValues := range sa.ConfigurableValues { - if len(selectValues) > 0 { - return true - } - } - return false -} - -// SetValue sets the base, non-configured value for the Label -func (sa *StringAttribute) SetValue(value string) { - sa.SetSelectValue(NoConfigAxis, "", &value) -} - -// SetSelectValue set a value for a bazel select for the given axis, config and value. -func (sa *StringAttribute) SetSelectValue(axis ConfigurationAxis, config string, str *string) { - axis.validateConfig(config) - switch axis.configurationType { - case noConfig: - sa.Value = str - case arch, os, osArch, productVariables, sanitizersEnabled: - if sa.ConfigurableValues == nil { - sa.ConfigurableValues = make(configurableStrings) - } - sa.ConfigurableValues.setValueForAxis(axis, config, str) - default: - panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) - } -} - -// SelectValue gets a value for a bazel select for the given axis and config. -func (sa *StringAttribute) SelectValue(axis ConfigurationAxis, config string) *string { - axis.validateConfig(config) - switch axis.configurationType { - case noConfig: - return sa.Value - case arch, os, osArch, productVariables, sanitizersEnabled: - if v, ok := sa.ConfigurableValues[axis][config]; ok { - return v - } else { - return nil - } - default: - panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) - } -} - -// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order. -func (sa *StringAttribute) SortedConfigurationAxes() []ConfigurationAxis { - return SortedConfigurationAxes(sa.ConfigurableValues) -} - -// Collapse reduces the configurable axes of the string attribute to a single axis. -// This is necessary for final writing to bp2build, as a configurable string -// attribute can only be comprised by a single select. -func (sa *StringAttribute) Collapse() error { - axisTypes := sa.axisTypes() - _, containsOs := axisTypes[os] - _, containsArch := axisTypes[arch] - _, containsOsArch := axisTypes[osArch] - _, containsProductVariables := axisTypes[productVariables] - if containsProductVariables { - if containsOs || containsArch || containsOsArch { - return fmt.Errorf("string attribute could not be collapsed as it has two or more unrelated axes") - } - } - if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) { - // If a bool attribute has both os and arch configuration axes, the only - // way to successfully union their values is to increase the granularity - // of the configuration criteria to os_arch. - for osType, supportedArchs := range osToArchMap { - for _, supportedArch := range supportedArchs { - osArch := osArchString(osType, supportedArch) - if archOsVal := sa.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil { - // Do nothing, as the arch_os is explicitly defined already. - } else { - archVal := sa.SelectValue(ArchConfigurationAxis, supportedArch) - osVal := sa.SelectValue(OsConfigurationAxis, osType) - if osVal != nil && archVal != nil { - // In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator - // runs after os mutator. - sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal) - } else if osVal != nil && archVal == nil { - sa.SetSelectValue(OsArchConfigurationAxis, osArch, osVal) - } else if osVal == nil && archVal != nil { - sa.SetSelectValue(OsArchConfigurationAxis, osArch, archVal) - } - } - } - } - /// All os_arch values are now set. Clear os and arch axes. - delete(sa.ConfigurableValues, ArchConfigurationAxis) - delete(sa.ConfigurableValues, OsConfigurationAxis) - // Verify post-condition; this should never fail, provided no additional - // axes are introduced. - if len(sa.ConfigurableValues) > 1 { - panic(fmt.Errorf("error in collapsing attribute: %#v", sa)) - } - } else if containsProductVariables { - usedBaseValue := false - for a, configToProp := range sa.ConfigurableValues { - if a.configurationType == productVariables { - for c, p := range configToProp { - if p == nil { - sa.SetSelectValue(a, c, sa.Value) - usedBaseValue = true - } - } - } - } - if usedBaseValue { - sa.Value = nil - } - } - return nil -} - -func (sa *StringAttribute) axisTypes() map[configurationType]bool { - types := map[configurationType]bool{} - for k := range sa.ConfigurableValues { - if strs := sa.ConfigurableValues[k]; len(strs) > 0 { - types[k.configurationType] = true - } - } - return types -} - -// StringListAttribute corresponds to the string_list Bazel attribute type with -// support for additional metadata, like configurations. -type StringListAttribute struct { - // The base value of the string list attribute. - Value []string - - // The configured attribute label list Values. Optional - // a map of independent configurability axes - ConfigurableValues configurableStringLists - - // If a property has struct tag "variant_prepend", this value should - // be set to True, so that when bp2build generates BUILD.bazel, variant - // properties(select ...) come before general properties. - Prepend bool -} - -// IsEmpty returns true if the attribute has no values under any configuration. -func (sla StringListAttribute) IsEmpty() bool { - return len(sla.Value) == 0 && !sla.HasConfigurableValues() -} - -type configurableStringLists map[ConfigurationAxis]stringListSelectValues - -func (csl configurableStringLists) Append(other configurableStringLists) { - for axis, otherSelects := range other { - selects := csl[axis] - if selects == nil { - selects = make(stringListSelectValues, len(otherSelects)) - } - selects.appendSelects(otherSelects) - csl[axis] = selects - } -} - -func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) { - if csl[axis] == nil { - csl[axis] = make(stringListSelectValues) - } - csl[axis][config] = list -} - -type stringListSelectValues map[string][]string - -func (sl stringListSelectValues) appendSelects(other stringListSelectValues) { - for k, v := range other { - sl[k] = append(sl[k], v...) - } -} - -func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool { - for _, val := range sl { - if len(val) > 0 { - return true - } - } - return false -} - -// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value. -func MakeStringListAttribute(value []string) StringListAttribute { - // NOTE: These strings are not necessarily unique or sorted. - return StringListAttribute{ - Value: value, - ConfigurableValues: make(configurableStringLists), - } -} - -// HasConfigurableValues returns true if the attribute contains axis-specific string_list values. -func (sla StringListAttribute) HasConfigurableValues() bool { - for _, selectValues := range sla.ConfigurableValues { - if len(selectValues) > 0 { - return true - } - } - return false -} - -// Append appends all values, including os and arch specific ones, from another -// StringListAttribute to this StringListAttribute -func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute { - sla.Value = append(sla.Value, other.Value...) - if sla.ConfigurableValues == nil { - sla.ConfigurableValues = make(configurableStringLists) - } - sla.ConfigurableValues.Append(other.ConfigurableValues) - return sla -} - -func (sla *StringListAttribute) Clone() *StringListAttribute { - result := &StringListAttribute{} - return result.Append(*sla) -} - -// SetSelectValue set a value for a bazel select for the given axis, config and value. -func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) { - axis.validateConfig(config) - switch axis.configurationType { - case noConfig: - sla.Value = list - case arch, os, osArch, productVariables, osAndInApex, errorProneDisabled, sanitizersEnabled: - if sla.ConfigurableValues == nil { - sla.ConfigurableValues = make(configurableStringLists) - } - sla.ConfigurableValues.setValueForAxis(axis, config, list) - default: - panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) - } -} - -// SelectValue gets a value for a bazel select for the given axis and config. -func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string { - axis.validateConfig(config) - switch axis.configurationType { - case noConfig: - return sla.Value - case arch, os, osArch, productVariables, osAndInApex, errorProneDisabled, sanitizersEnabled: - return sla.ConfigurableValues[axis][config] - default: - panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis)) - } -} - -// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order. -func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis { - return SortedConfigurationAxes(sla.ConfigurableValues) -} - -// DeduplicateAxesFromBase ensures no duplication of items between the no-configuration value and -// configuration-specific values. For example, if we would convert this StringListAttribute as: -// -// ["a", "b", "c"] + select({ -// "//condition:one": ["a", "d"], -// "//conditions:default": [], -// }) -// -// after this function, we would convert this StringListAttribute as: -// -// ["a", "b", "c"] + select({ -// "//condition:one": ["d"], -// "//conditions:default": [], -// }) -func (sla *StringListAttribute) DeduplicateAxesFromBase() { - base := sla.Value - for axis, configToList := range sla.ConfigurableValues { - for config, list := range configToList { - remaining := SubtractStrings(list, base) - if len(remaining) == 0 { - delete(sla.ConfigurableValues[axis], config) - } else { - sla.ConfigurableValues[axis][config] = remaining - } - } - } -} - -// TryVariableSubstitution, replace string substitution formatting within each string in slice with -// Starlark string.format compatible tag for productVariable. -func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) { - if len(slice) == 0 { - return slice, false - } - ret := make([]string, 0, len(slice)) - changesMade := false - for _, s := range slice { - newS, changed := TryVariableSubstitution(s, productVariable) - ret = append(ret, newS) - changesMade = changesMade || changed - } - return ret, changesMade -} - -// TryVariableSubstitution, replace string substitution formatting within s with Starlark -// string.format compatible tag for productVariable. -func TryVariableSubstitution(s string, productVariable string) (string, bool) { - sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")") - return sub, s != sub -} - -// StringMapAttribute is a map of strings. -// The use case for this is storing the flag_values in a config_setting object. -// Bazel rules do not support map attributes, and this should NOT be used in Bazel rules. -type StringMapAttribute map[string]string - -// ConfigSettingAttributes stores the keys of a config_setting object. -type ConfigSettingAttributes struct { - // Each key in Flag_values is a label to a custom string_setting - Flag_values StringMapAttribute - // Each element in Constraint_values is a label to a constraint_value - Constraint_values LabelListAttribute -} diff --git a/bazel/properties_test.go b/bazel/properties_test.go deleted file mode 100644 index 751cb8b30..000000000 --- a/bazel/properties_test.go +++ /dev/null @@ -1,836 +0,0 @@ -// Copyright 2021 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bazel - -import ( - "reflect" - "strings" - "testing" - - "github.com/google/blueprint/proptools" -) - -func TestUniqueBazelLabels(t *testing.T) { - testCases := []struct { - originalLabels []Label - expectedUniqueLabels []Label - }{ - { - originalLabels: []Label{ - {Label: "a"}, - {Label: "b"}, - {Label: "a"}, - {Label: "c"}, - // namespaces - {Label: "//foo:bar", OriginalModuleName: "bar"}, // when referenced from foo namespace - {Label: "//foo:bar", OriginalModuleName: "//foo:bar"}, // when reference from root namespace - }, - expectedUniqueLabels: []Label{ - {Label: "//foo:bar", OriginalModuleName: "bar"}, - {Label: "a"}, - {Label: "b"}, - {Label: "c"}, - }, - }, - } - for _, tc := range testCases { - actualUniqueLabels := UniqueSortedBazelLabels(tc.originalLabels) - if !reflect.DeepEqual(tc.expectedUniqueLabels, actualUniqueLabels) { - t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabels, actualUniqueLabels) - } - } -} - -func TestSubtractStrings(t *testing.T) { - testCases := []struct { - haystack []string - needle []string - expectedResult []string - }{ - { - haystack: []string{ - "a", - "b", - "c", - }, - needle: []string{ - "a", - }, - expectedResult: []string{ - "b", "c", - }, - }, - } - for _, tc := range testCases { - actualResult := SubtractStrings(tc.haystack, tc.needle) - if !reflect.DeepEqual(tc.expectedResult, actualResult) { - t.Fatalf("Expected %v, got %v", tc.expectedResult, actualResult) - } - } -} - -func TestSubtractBazelLabelList(t *testing.T) { - testCases := []struct { - haystack LabelList - needle LabelList - expectedResult LabelList - }{ - { - haystack: LabelList{ - Includes: []Label{ - {Label: "a"}, - {Label: "b"}, - {Label: "c"}, - }, - Excludes: []Label{ - {Label: "x"}, - {Label: "y"}, - {Label: "z"}, - }, - }, - needle: LabelList{ - Includes: []Label{ - {Label: "a"}, - }, - Excludes: []Label{ - {Label: "z"}, - }, - }, - // NOTE: Excludes are intentionally not subtracted - expectedResult: LabelList{ - Includes: []Label{ - {Label: "b"}, - {Label: "c"}, - }, - Excludes: []Label{ - {Label: "x"}, - {Label: "y"}, - {Label: "z"}, - }, - }, - }, - } - for _, tc := range testCases { - actualResult := SubtractBazelLabelList(tc.haystack, tc.needle) - if !reflect.DeepEqual(tc.expectedResult, actualResult) { - t.Fatalf("Expected %v, got %v", tc.expectedResult, actualResult) - } - } -} - -func TestSubtractBazelLabelListAttribute(t *testing.T) { - testCases := []struct { - haystack LabelListAttribute - needle LabelListAttribute - expected LabelListAttribute - }{ - { - haystack: LabelListAttribute{ - Value: makeLabelList( - []string{"a", "b", "a", "c"}, - []string{"x", "x", "y", "z"}, - ), - ConfigurableValues: configurableLabelLists{ - ArchConfigurationAxis: labelListSelectValues{ - "arm": makeLabelList([]string{"arm_1", "arm_2"}, []string{}), - "x86": makeLabelList([]string{"x86_3", "x86_4", "x86_5"}, []string{"x86_5"}), - }, - }, - }, - needle: LabelListAttribute{ - Value: makeLabelList( - []string{"d", "a"}, - []string{"x", "y2", "z2"}, - ), - ConfigurableValues: configurableLabelLists{ - ArchConfigurationAxis: labelListSelectValues{ - "arm": makeLabelList([]string{"arm_1", "arm_3"}, []string{}), - "x86": makeLabelList([]string{"x86_3", "x86_4"}, []string{"x86_6"}), - }, - }, - }, - expected: LabelListAttribute{ - Value: makeLabelList( - []string{"b", "c"}, - []string{"x", "x", "y", "z"}, - ), - ConfigurableValues: configurableLabelLists{ - ArchConfigurationAxis: labelListSelectValues{ - "arm": makeLabelList([]string{"arm_2"}, []string{}), - "x86": makeLabelList([]string{"x86_5"}, []string{"x86_5"}), - }, - }, - ForceSpecifyEmptyList: false, - EmitEmptyList: false, - Prepend: false, - }, - }, - } - for _, tc := range testCases { - got := SubtractBazelLabelListAttribute(tc.haystack, tc.needle) - if !reflect.DeepEqual(tc.expected, got) { - t.Fatalf("Expected\n%v, but got\n%v", tc.expected, got) - } - } -} - -func TestFirstUniqueBazelLabelList(t *testing.T) { - testCases := []struct { - originalLabelList LabelList - expectedUniqueLabelList LabelList - }{ - { - originalLabelList: LabelList{ - Includes: []Label{ - {Label: "a"}, - {Label: "b"}, - {Label: "a"}, - {Label: "c"}, - // namespaces - {Label: "//foo:bar", OriginalModuleName: "bar"}, // when referenced from foo namespace - {Label: "//foo:bar", OriginalModuleName: "//foo:bar"}, // when referenced from root namespace - }, - Excludes: []Label{ - {Label: "x"}, - {Label: "x"}, - {Label: "y"}, - {Label: "z"}, - }, - }, - expectedUniqueLabelList: LabelList{ - Includes: []Label{ - {Label: "a"}, - {Label: "b"}, - {Label: "c"}, - {Label: "//foo:bar", OriginalModuleName: "bar"}, - }, - Excludes: []Label{ - {Label: "x"}, - {Label: "y"}, - {Label: "z"}, - }, - }, - }, - } - for _, tc := range testCases { - actualUniqueLabelList := FirstUniqueBazelLabelList(tc.originalLabelList) - if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) { - t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList) - } - } -} - -func TestFirstUniqueBazelLabelListAttribute(t *testing.T) { - testCases := []struct { - originalLabelList LabelListAttribute - expectedUniqueLabelList LabelListAttribute - }{ - { - originalLabelList: LabelListAttribute{ - Value: makeLabelList( - []string{"a", "b", "a", "c"}, - []string{"x", "x", "y", "z"}, - ), - ConfigurableValues: configurableLabelLists{ - ArchConfigurationAxis: labelListSelectValues{ - "arm": makeLabelList([]string{"1", "2", "1"}, []string{}), - "x86": makeLabelList([]string{"3", "4", "4"}, []string{"5", "5"}), - }, - }, - }, - expectedUniqueLabelList: LabelListAttribute{ - Value: makeLabelList( - []string{"a", "b", "c"}, - []string{"x", "y", "z"}, - ), - ConfigurableValues: configurableLabelLists{ - ArchConfigurationAxis: labelListSelectValues{ - "arm": makeLabelList([]string{"1", "2"}, []string{}), - "x86": makeLabelList([]string{"3", "4"}, []string{"5"}), - }, - }, - }, - }, - } - for _, tc := range testCases { - actualUniqueLabelList := FirstUniqueBazelLabelListAttribute(tc.originalLabelList) - if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) { - t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList) - } - } -} - -func TestUniqueSortedBazelLabelList(t *testing.T) { - testCases := []struct { - originalLabelList LabelList - expectedUniqueLabelList LabelList - }{ - { - originalLabelList: LabelList{ - Includes: []Label{ - {Label: "c"}, - {Label: "a"}, - {Label: "a"}, - {Label: "b"}, - }, - Excludes: []Label{ - {Label: "y"}, - {Label: "z"}, - {Label: "x"}, - {Label: "x"}, - }, - }, - expectedUniqueLabelList: LabelList{ - Includes: []Label{ - {Label: "a"}, - {Label: "b"}, - {Label: "c"}, - }, - Excludes: []Label{ - {Label: "x"}, - {Label: "y"}, - {Label: "z"}, - }, - }, - }, - } - for _, tc := range testCases { - actualUniqueLabelList := UniqueSortedBazelLabelList(tc.originalLabelList) - if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) { - t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList) - } - } -} - -func makeLabels(labels ...string) []Label { - var ret []Label - for _, l := range labels { - ret = append(ret, Label{Label: l}) - } - return ret -} - -func makeLabelList(includes, excludes []string) LabelList { - return LabelList{ - Includes: makeLabels(includes...), - Excludes: makeLabels(excludes...), - } -} - -func TestResolveExcludes(t *testing.T) { - attr := LabelListAttribute{ - Value: makeLabelList( - []string{ - "all_include", - "arm_exclude", - "android_exclude", - "product_config_exclude", - }, - []string{"all_exclude"}, - ), - ConfigurableValues: configurableLabelLists{ - ArchConfigurationAxis: labelListSelectValues{ - "arm": makeLabelList([]string{}, []string{"arm_exclude"}), - "x86": makeLabelList([]string{"x86_include"}, []string{}), - ConditionsDefaultConfigKey: makeLabelList([]string{"default_include"}, []string{}), - }, - OsConfigurationAxis: labelListSelectValues{ - "android": makeLabelList([]string{}, []string{"android_exclude"}), - "linux": makeLabelList([]string{"linux_include"}, []string{}), - }, - OsArchConfigurationAxis: labelListSelectValues{ - "linux_x86": makeLabelList([]string{"linux_x86_include"}, []string{}), - }, - ProductVariableConfigurationAxis(false, "product_with_defaults"): labelListSelectValues{ - "a": makeLabelList([]string{}, []string{"not_in_value"}), - "b": makeLabelList([]string{"b_val"}, []string{}), - "c": makeLabelList([]string{"c_val"}, []string{}), - ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2", "all_exclude"}, []string{}), - }, - ProductVariableConfigurationAxis(false, "product_only_with_excludes"): labelListSelectValues{ - "a": makeLabelList([]string{}, []string{"product_config_exclude"}), - }, - }, - } - - attr.ResolveExcludes() - - expectedBaseIncludes := []Label{{Label: "all_include"}} - if !reflect.DeepEqual(expectedBaseIncludes, attr.Value.Includes) { - t.Errorf("Expected Value includes %q, got %q", attr.Value.Includes, expectedBaseIncludes) - } - var nilLabels []Label - expectedConfiguredIncludes := map[ConfigurationAxis]map[string][]Label{ - ArchConfigurationAxis: { - "arm": nilLabels, - "x86": makeLabels("arm_exclude", "x86_include"), - ConditionsDefaultConfigKey: makeLabels("arm_exclude", "default_include"), - }, - OsConfigurationAxis: { - "android": nilLabels, - "linux": makeLabels("android_exclude", "linux_include"), - ConditionsDefaultConfigKey: makeLabels("android_exclude"), - }, - OsArchConfigurationAxis: { - "linux_x86": makeLabels("linux_x86_include"), - ConditionsDefaultConfigKey: nilLabels, - }, - ProductVariableConfigurationAxis(false, "product_with_defaults"): { - "a": nilLabels, - "b": makeLabels("b_val"), - "c": makeLabels("c_val"), - ConditionsDefaultConfigKey: makeLabels("c_val", "default", "default2"), - }, - ProductVariableConfigurationAxis(false, "product_only_with_excludes"): { - "a": nilLabels, - ConditionsDefaultConfigKey: makeLabels("product_config_exclude"), - }, - } - for _, axis := range attr.SortedConfigurationAxes() { - if _, ok := expectedConfiguredIncludes[axis]; !ok { - t.Errorf("Found unexpected axis %s", axis) - continue - } - expectedForAxis := expectedConfiguredIncludes[axis] - gotForAxis := attr.ConfigurableValues[axis] - if len(expectedForAxis) != len(gotForAxis) { - t.Errorf("Expected %d configs for %s, got %d: %s", len(expectedForAxis), axis, len(gotForAxis), gotForAxis) - } - for config, value := range gotForAxis { - if expected, ok := expectedForAxis[config]; ok { - if !reflect.DeepEqual(expected, value.Includes) { - t.Errorf("For %s,\nexpected: %#v\ngot %#v", axis, expected, value.Includes) - } - } else { - t.Errorf("Got unexpected config %q for %s", config, axis) - } - } - } -} - -func TestLabelListAttributePartition(t *testing.T) { - testCases := []struct { - name string - input LabelListAttribute - predicated LabelListAttribute - unpredicated LabelListAttribute - predicate func(label Label) bool - }{ - { - name: "move all to predicated partition", - input: MakeLabelListAttribute(makeLabelList( - []string{"keep1", "throw1", "keep2", "throw2"}, - []string{"keep1", "throw1", "keep2", "throw2"}, - )), - predicated: MakeLabelListAttribute(makeLabelList( - []string{"keep1", "throw1", "keep2", "throw2"}, - []string{"keep1", "throw1", "keep2", "throw2"}, - )), - unpredicated: LabelListAttribute{}, - predicate: func(label Label) bool { - return true - }, - }, - { - name: "move all to unpredicated partition", - input: MakeLabelListAttribute(makeLabelList( - []string{"keep1", "throw1", "keep2", "throw2"}, - []string{"keep1", "throw1", "keep2", "throw2"}, - )), - predicated: LabelListAttribute{}, - unpredicated: MakeLabelListAttribute(makeLabelList( - []string{"keep1", "throw1", "keep2", "throw2"}, - []string{"keep1", "throw1", "keep2", "throw2"}, - )), - predicate: func(label Label) bool { - return false - }, - }, - { - name: "partition includes and excludes", - input: MakeLabelListAttribute(makeLabelList( - []string{"keep1", "throw1", "keep2", "throw2"}, - []string{"keep1", "throw1", "keep2", "throw2"}, - )), - predicated: MakeLabelListAttribute(makeLabelList( - []string{"keep1", "keep2"}, - []string{"keep1", "keep2"}, - )), - unpredicated: MakeLabelListAttribute(makeLabelList( - []string{"throw1", "throw2"}, - []string{"throw1", "throw2"}, - )), - predicate: func(label Label) bool { - return strings.HasPrefix(label.Label, "keep") - }, - }, - { - name: "partition excludes only", - input: MakeLabelListAttribute(makeLabelList( - []string{}, - []string{"keep1", "throw1", "keep2", "throw2"}, - )), - predicated: MakeLabelListAttribute(makeLabelList( - []string{}, - []string{"keep1", "keep2"}, - )), - unpredicated: MakeLabelListAttribute(makeLabelList( - []string{}, - []string{"throw1", "throw2"}, - )), - predicate: func(label Label) bool { - return strings.HasPrefix(label.Label, "keep") - }, - }, - { - name: "partition includes only", - input: MakeLabelListAttribute(makeLabelList( - []string{"keep1", "throw1", "keep2", "throw2"}, - []string{}, - )), - predicated: MakeLabelListAttribute(makeLabelList( - []string{"keep1", "keep2"}, - []string{}, - )), - unpredicated: MakeLabelListAttribute(makeLabelList( - []string{"throw1", "throw2"}, - []string{}, - )), - predicate: func(label Label) bool { - return strings.HasPrefix(label.Label, "keep") - }, - }, - { - name: "empty partition", - input: MakeLabelListAttribute(makeLabelList([]string{}, []string{})), - predicated: LabelListAttribute{}, - unpredicated: LabelListAttribute{}, - predicate: func(label Label) bool { - return true - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - predicated, unpredicated := tc.input.Partition(tc.predicate) - if !predicated.Value.Equals(tc.predicated.Value) { - t.Errorf("expected predicated labels to be %v; got %v", tc.predicated, predicated) - } - for axis, configs := range predicated.ConfigurableValues { - tcConfigs, ok := tc.predicated.ConfigurableValues[axis] - if !ok || !reflect.DeepEqual(configs, tcConfigs) { - t.Errorf("expected predicated labels to be %v; got %v", tc.predicated, predicated) - } - } - if !unpredicated.Value.Equals(tc.unpredicated.Value) { - t.Errorf("expected unpredicated labels to be %v; got %v", tc.unpredicated, unpredicated) - } - for axis, configs := range unpredicated.ConfigurableValues { - tcConfigs, ok := tc.unpredicated.ConfigurableValues[axis] - if !ok || !reflect.DeepEqual(configs, tcConfigs) { - t.Errorf("expected unpredicated labels to be %v; got %v", tc.unpredicated, unpredicated) - } - } - }) - } -} - -// labelAddSuffixForTypeMapper returns a LabelMapper that adds suffix to label name for modules of -// typ -func labelAddSuffixForTypeMapper(suffix, typ string) LabelMapper { - return func(omc OtherModuleContext, label Label) (string, bool) { - m, ok := omc.ModuleFromName(label.Label) - if !ok { - return label.Label, false - } - mTyp := omc.OtherModuleType(m) - if typ == mTyp { - return label.Label + suffix, true - } - return label.Label, false - } -} - -func TestPartitionLabelListAttribute(t *testing.T) { - testCases := []struct { - name string - ctx *OtherModuleTestContext - labelList LabelListAttribute - filters LabelPartitions - expected PartitionToLabelListAttribute - expectedErrMsg *string - }{ - { - name: "no configurable values", - ctx: &OtherModuleTestContext{}, - labelList: LabelListAttribute{ - Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}), - }, - filters: LabelPartitions{ - "A": LabelPartition{Extensions: []string{".a"}}, - "B": LabelPartition{Extensions: []string{".b"}}, - "C": LabelPartition{Extensions: []string{".c"}}, - }, - expected: PartitionToLabelListAttribute{ - "A": LabelListAttribute{Value: makeLabelList([]string{"a.a"}, []string{})}, - "B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})}, - "C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})}, - }, - }, - { - name: "no configurable values, remainder partition", - ctx: &OtherModuleTestContext{}, - labelList: LabelListAttribute{ - Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}), - }, - filters: LabelPartitions{ - "A": LabelPartition{Extensions: []string{".a"}, Keep_remainder: true}, - "B": LabelPartition{Extensions: []string{".b"}}, - "C": LabelPartition{Extensions: []string{".c"}}, - }, - expected: PartitionToLabelListAttribute{ - "A": LabelListAttribute{Value: makeLabelList([]string{"a.a", "d.d", "e.e"}, []string{})}, - "B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})}, - "C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})}, - }, - }, - { - name: "no configurable values, empty partition", - ctx: &OtherModuleTestContext{}, - labelList: LabelListAttribute{ - Value: makeLabelList([]string{"a.a", "c.c"}, []string{}), - }, - filters: LabelPartitions{ - "A": LabelPartition{Extensions: []string{".a"}}, - "B": LabelPartition{Extensions: []string{".b"}}, - "C": LabelPartition{Extensions: []string{".c"}}, - }, - expected: PartitionToLabelListAttribute{ - "A": LabelListAttribute{Value: makeLabelList([]string{"a.a"}, []string{})}, - "C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})}, - }, - }, - { - name: "no configurable values, has map", - ctx: &OtherModuleTestContext{ - Modules: []TestModuleInfo{{ModuleName: "srcs", Typ: "fg", Dir: "dir"}}, - }, - labelList: LabelListAttribute{ - Value: makeLabelList([]string{"a.a", "srcs", "b.b", "c.c"}, []string{}), - }, - filters: LabelPartitions{ - "A": LabelPartition{Extensions: []string{".a"}, LabelMapper: labelAddSuffixForTypeMapper("_a", "fg")}, - "B": LabelPartition{Extensions: []string{".b"}}, - "C": LabelPartition{Extensions: []string{".c"}}, - }, - expected: PartitionToLabelListAttribute{ - "A": LabelListAttribute{Value: makeLabelList([]string{"a.a", "srcs_a"}, []string{})}, - "B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})}, - "C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})}, - }, - }, - { - name: "configurable values, keeps empty if excludes", - ctx: &OtherModuleTestContext{}, - labelList: LabelListAttribute{ - ConfigurableValues: configurableLabelLists{ - ArchConfigurationAxis: labelListSelectValues{ - "x86": makeLabelList([]string{"a.a", "c.c"}, []string{}), - "arm": makeLabelList([]string{"b.b"}, []string{}), - "x86_64": makeLabelList([]string{"b.b"}, []string{"d.d"}), - }, - }, - }, - filters: LabelPartitions{ - "A": LabelPartition{Extensions: []string{".a"}}, - "B": LabelPartition{Extensions: []string{".b"}}, - "C": LabelPartition{Extensions: []string{".c"}}, - }, - expected: PartitionToLabelListAttribute{ - "A": LabelListAttribute{ - ConfigurableValues: configurableLabelLists{ - ArchConfigurationAxis: labelListSelectValues{ - "x86": makeLabelList([]string{"a.a"}, []string{}), - "x86_64": makeLabelList([]string{}, []string{"c.c"}), - }, - }, - }, - "B": LabelListAttribute{ - ConfigurableValues: configurableLabelLists{ - ArchConfigurationAxis: labelListSelectValues{ - "arm": makeLabelList([]string{"b.b"}, []string{}), - "x86_64": makeLabelList([]string{"b.b"}, []string{"c.c"}), - }, - }, - }, - "C": LabelListAttribute{ - ConfigurableValues: configurableLabelLists{ - ArchConfigurationAxis: labelListSelectValues{ - "x86": makeLabelList([]string{"c.c"}, []string{}), - "x86_64": makeLabelList([]string{}, []string{"c.c"}), - }, - }, - }, - }, - }, - { - name: "error for multiple partitions same value", - ctx: &OtherModuleTestContext{}, - labelList: LabelListAttribute{ - Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}), - }, - filters: LabelPartitions{ - "A": LabelPartition{Extensions: []string{".a"}}, - "other A": LabelPartition{Extensions: []string{".a"}}, - }, - expected: PartitionToLabelListAttribute{}, - expectedErrMsg: proptools.StringPtr(`"a.a" was found in multiple partitions:`), - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - got := PartitionLabelListAttribute(tc.ctx, &tc.labelList, tc.filters) - - if hasErrors, expectsErr := len(tc.ctx.errors) > 0, tc.expectedErrMsg != nil; hasErrors != expectsErr { - t.Errorf("Unexpected error(s): %q, expected: %q", tc.ctx.errors, *tc.expectedErrMsg) - } else if tc.expectedErrMsg != nil { - found := false - for _, err := range tc.ctx.errors { - if strings.Contains(err, *tc.expectedErrMsg) { - found = true - break - } - } - - if !found { - t.Errorf("Expected error message: %q, got %q", *tc.expectedErrMsg, tc.ctx.errors) - } - return - } - - if len(tc.expected) != len(got) { - t.Errorf("Expected %d partitions, got %d partitions", len(tc.expected), len(got)) - } - for partition, expectedLla := range tc.expected { - gotLla, ok := got[partition] - if !ok { - t.Errorf("Expected partition %q, but it was not found %v", partition, got) - continue - } - expectedLabelList := expectedLla.Value - gotLabelList := gotLla.Value - if !reflect.DeepEqual(expectedLabelList.Includes, gotLabelList.Includes) { - t.Errorf("Expected no config includes %v, got %v", expectedLabelList.Includes, gotLabelList.Includes) - } - expectedAxes := expectedLla.SortedConfigurationAxes() - gotAxes := gotLla.SortedConfigurationAxes() - if !reflect.DeepEqual(expectedAxes, gotAxes) { - t.Errorf("Expected axes %v, got %v (%#v)", expectedAxes, gotAxes, gotLla) - } - for _, axis := range expectedLla.SortedConfigurationAxes() { - if _, exists := gotLla.ConfigurableValues[axis]; !exists { - t.Errorf("Expected %s to be a supported axis, but it was not found", axis) - } - if expected, got := expectedLla.ConfigurableValues[axis], gotLla.ConfigurableValues[axis]; len(expected) != len(got) { - t.Errorf("For axis %q: expected configs %v, got %v", axis, expected, got) - } - for config, expectedLabelList := range expectedLla.ConfigurableValues[axis] { - gotLabelList, exists := gotLla.ConfigurableValues[axis][config] - if !exists { - t.Errorf("Expected %s to be a supported config, but config was not found", config) - continue - } - if !reflect.DeepEqual(expectedLabelList.Includes, gotLabelList.Includes) { - t.Errorf("Expected %s %s includes %v, got %v", axis, config, expectedLabelList.Includes, gotLabelList.Includes) - } - } - } - } - }) - } -} - -func TestDeduplicateAxesFromBase(t *testing.T) { - attr := StringListAttribute{ - Value: []string{ - "all_include", - "arm_include", - "android_include", - "linux_x86_include", - }, - ConfigurableValues: configurableStringLists{ - ArchConfigurationAxis: stringListSelectValues{ - "arm": []string{"arm_include"}, - "x86": []string{"x86_include"}, - }, - OsConfigurationAxis: stringListSelectValues{ - "android": []string{"android_include"}, - "linux": []string{"linux_include"}, - }, - OsArchConfigurationAxis: stringListSelectValues{ - "linux_x86": {"linux_x86_include"}, - }, - ProductVariableConfigurationAxis(false, "a"): stringListSelectValues{ - "a": []string{"not_in_value"}, - }, - }, - } - - attr.DeduplicateAxesFromBase() - - expectedBaseIncludes := []string{ - "all_include", - "arm_include", - "android_include", - "linux_x86_include", - } - if !reflect.DeepEqual(expectedBaseIncludes, attr.Value) { - t.Errorf("Expected Value includes %q, got %q", attr.Value, expectedBaseIncludes) - } - expectedConfiguredIncludes := configurableStringLists{ - ArchConfigurationAxis: stringListSelectValues{ - "x86": []string{"x86_include"}, - }, - OsConfigurationAxis: stringListSelectValues{ - "linux": []string{"linux_include"}, - }, - OsArchConfigurationAxis: stringListSelectValues{}, - ProductVariableConfigurationAxis(false, "a"): stringListSelectValues{ - "a": []string{"not_in_value"}, - }, - } - for _, axis := range attr.SortedConfigurationAxes() { - if _, ok := expectedConfiguredIncludes[axis]; !ok { - t.Errorf("Found unexpected axis %s", axis) - continue - } - expectedForAxis := expectedConfiguredIncludes[axis] - gotForAxis := attr.ConfigurableValues[axis] - if len(expectedForAxis) != len(gotForAxis) { - t.Errorf("Expected %d configs for %s, got %d: %s", len(expectedForAxis), axis, len(gotForAxis), gotForAxis) - } - for config, value := range gotForAxis { - if expected, ok := expectedForAxis[config]; ok { - if !reflect.DeepEqual(expected, value) { - t.Errorf("For %s, expected: %#v, got %#v", axis, expected, value) - } - } else { - t.Errorf("Got unexpected config %q for %s", config, axis) - } - } - } -} diff --git a/bazel/testing.go b/bazel/testing.go deleted file mode 100644 index 9a43b61d7..000000000 --- a/bazel/testing.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2021 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bazel - -import ( - "fmt" - - "github.com/google/blueprint" -) - -// TestModuleInfo implements blueprint.Module interface with sufficient information to mock a subset of -// a blueprint ModuleContext -type TestModuleInfo struct { - ModuleName string - Typ string - Dir string -} - -// Name returns name for testModuleInfo -- required to implement blueprint.Module -func (mi TestModuleInfo) Name() string { - return mi.ModuleName -} - -// GenerateBuildActions unused, but required to implmeent blueprint.Module -func (mi TestModuleInfo) GenerateBuildActions(blueprint.ModuleContext) {} - -func (mi TestModuleInfo) equals(other TestModuleInfo) bool { - return mi.ModuleName == other.ModuleName && mi.Typ == other.Typ && mi.Dir == other.Dir -} - -// ensure testModuleInfo implements blueprint.Module -var _ blueprint.Module = TestModuleInfo{} - -// OtherModuleTestContext is a mock context that implements OtherModuleContext -type OtherModuleTestContext struct { - Modules []TestModuleInfo - errors []string -} - -// ModuleFromName retrieves the testModuleInfo corresponding to name, if it exists -func (omc *OtherModuleTestContext) ModuleFromName(name string) (blueprint.Module, bool) { - for _, m := range omc.Modules { - if m.ModuleName == name { - return m, true - } - } - return TestModuleInfo{}, false -} - -// testModuleInfo returns the testModuleInfo corresponding to a blueprint.Module if it exists in omc -func (omc *OtherModuleTestContext) testModuleInfo(m blueprint.Module) (TestModuleInfo, bool) { - mi, ok := m.(TestModuleInfo) - if !ok { - return TestModuleInfo{}, false - } - for _, other := range omc.Modules { - if other.equals(mi) { - return mi, true - } - } - return TestModuleInfo{}, false -} - -// OtherModuleType returns type of m if it exists in omc -func (omc *OtherModuleTestContext) OtherModuleType(m blueprint.Module) string { - if mi, ok := omc.testModuleInfo(m); ok { - return mi.Typ - } - return "" -} - -// OtherModuleName returns name of m if it exists in omc -func (omc *OtherModuleTestContext) OtherModuleName(m blueprint.Module) string { - if mi, ok := omc.testModuleInfo(m); ok { - return mi.ModuleName - } - return "" -} - -// OtherModuleDir returns dir of m if it exists in omc -func (omc *OtherModuleTestContext) OtherModuleDir(m blueprint.Module) string { - if mi, ok := omc.testModuleInfo(m); ok { - return mi.Dir - } - return "" -} - -func (omc *OtherModuleTestContext) ModuleErrorf(format string, args ...interface{}) { - omc.errors = append(omc.errors, fmt.Sprintf(format, args...)) -} - -// Ensure otherModuleTestContext implements OtherModuleContext -var _ OtherModuleContext = &OtherModuleTestContext{} diff --git a/bp2build/Android.bp b/bp2build/Android.bp index ba1268214..28c026857 100644 --- a/bp2build/Android.bp +++ b/bp2build/Android.bp @@ -9,7 +9,6 @@ bootstrap_go_package { "androidbp_to_build_templates.go", "build_conversion.go", "bzl_conversion.go", - "configurability.go", "constants.go", "conversion.go", ], @@ -21,7 +20,6 @@ bootstrap_go_package { "soong-android-allowlists", "soong-android-soongconfig", "soong-apex", - "soong-bazel", "soong-cc", "soong-cc-config", "soong-etc", diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go index bd5676815..18213a880 100644 --- a/bp2build/build_conversion.go +++ b/bp2build/build_conversion.go @@ -26,7 +26,6 @@ import ( "strings" "android/soong/android" - "android/soong/bazel" "android/soong/starlark_fmt" "github.com/google/blueprint" "github.com/google/blueprint/proptools" @@ -182,12 +181,11 @@ type bpToBuildContext interface { } type CodegenContext struct { - config android.Config - context *android.Context - mode CodegenMode - additionalDeps []string - unconvertedDepMode unconvertedDepsMode - topDir string + config android.Config + context *android.Context + mode CodegenMode + additionalDeps []string + topDir string } func (ctx *CodegenContext) Mode() CodegenMode { @@ -207,16 +205,6 @@ const ( QueryView CodegenMode = iota ) -type unconvertedDepsMode int - -const ( - // Include a warning in conversion metrics about converted modules with unconverted direct deps - warnUnconvertedDeps unconvertedDepsMode = iota - // Error and fail conversion if encountering a module with unconverted direct deps - // Enabled by setting environment variable `BP2BUILD_ERROR_UNCONVERTED` - errorModulesUnconvertedDeps -) - func (mode CodegenMode) String() string { switch mode { case QueryView: @@ -245,13 +233,11 @@ func (ctx *CodegenContext) Context() *android.Context { return ctx.context } // NewCodegenContext creates a wrapper context that conforms to PathContext for // writing BUILD files in the output directory. func NewCodegenContext(config android.Config, context *android.Context, mode CodegenMode, topDir string) *CodegenContext { - var unconvertedDeps unconvertedDepsMode return &CodegenContext{ - context: context, - config: config, - mode: mode, - unconvertedDepMode: unconvertedDeps, - topDir: topDir, + context: context, + config: config, + mode: mode, + topDir: topDir, } } @@ -482,14 +468,6 @@ func prettyPrint(propertyValue reflect.Value, indent int, emitZeroValues bool) ( }), nil case reflect.Struct: - // Special cases where the bp2build sends additional information to the codegenerator - // by wrapping the attributes in a custom struct type. - if attr, ok := propertyValue.Interface().(bazel.Attribute); ok { - return prettyPrintAttribute(attr, indent) - } else if label, ok := propertyValue.Interface().(bazel.Label); ok { - return fmt.Sprintf("%q", label.Label), nil - } - // Sort and print the struct props by the key. structProps, err := extractStructProperties(propertyValue, indent) @@ -506,7 +484,7 @@ func prettyPrint(propertyValue reflect.Value, indent int, emitZeroValues bool) ( // Interfaces are used for for arch, multilib and target properties. return "", nil case reflect.Map: - if v, ok := propertyValue.Interface().(bazel.StringMapAttribute); ok { + if v, ok := propertyValue.Interface().(map[string]string); ok { return starlark_fmt.PrintStringStringDict(v, indent), nil } return "", fmt.Errorf("bp2build expects map of type map[string]string for field: %s", propertyValue) diff --git a/bp2build/configurability.go b/bp2build/configurability.go deleted file mode 100644 index 3d9f0a274..000000000 --- a/bp2build/configurability.go +++ /dev/null @@ -1,328 +0,0 @@ -package bp2build - -import ( - "fmt" - "reflect" - - "android/soong/android" - "android/soong/bazel" - "android/soong/starlark_fmt" -) - -// Configurability support for bp2build. - -type selects map[string]reflect.Value - -func getStringValue(str bazel.StringAttribute) (reflect.Value, []selects) { - value := reflect.ValueOf(str.Value) - - if !str.HasConfigurableValues() { - return value, []selects{} - } - - ret := selects{} - for _, axis := range str.SortedConfigurationAxes() { - configToStrs := str.ConfigurableValues[axis] - for config, strs := range configToStrs { - selectKey := axis.SelectKey(config) - ret[selectKey] = reflect.ValueOf(strs) - } - } - - // if there is a select, use the base value as the conditions default value - if len(ret) > 0 { - if _, ok := ret[bazel.ConditionsDefaultSelectKey]; !ok { - ret[bazel.ConditionsDefaultSelectKey] = value - value = reflect.Zero(value.Type()) - } - } - - return value, []selects{ret} -} - -func getStringListValues(list bazel.StringListAttribute) (reflect.Value, []selects, bool) { - value := reflect.ValueOf(list.Value) - prepend := list.Prepend - if !list.HasConfigurableValues() { - return value, []selects{}, prepend - } - - var ret []selects - for _, axis := range list.SortedConfigurationAxes() { - configToLists := list.ConfigurableValues[axis] - archSelects := map[string]reflect.Value{} - for config, labels := range configToLists { - selectKey := axis.SelectKey(config) - archSelects[selectKey] = reflect.ValueOf(labels) - } - if len(archSelects) > 0 { - ret = append(ret, archSelects) - } - } - - return value, ret, prepend -} - -func getLabelValue(label bazel.LabelAttribute) (reflect.Value, []selects) { - value := reflect.ValueOf(label.Value) - if !label.HasConfigurableValues() { - return value, []selects{} - } - - ret := selects{} - for _, axis := range label.SortedConfigurationAxes() { - configToLabels := label.ConfigurableValues[axis] - for config, labels := range configToLabels { - selectKey := axis.SelectKey(config) - ret[selectKey] = reflect.ValueOf(labels) - } - } - - // if there is a select, use the base value as the conditions default value - if len(ret) > 0 { - ret[bazel.ConditionsDefaultSelectKey] = value - value = reflect.Zero(value.Type()) - } - - return value, []selects{ret} -} - -func getBoolValue(boolAttr bazel.BoolAttribute) (reflect.Value, []selects) { - value := reflect.ValueOf(boolAttr.Value) - if !boolAttr.HasConfigurableValues() { - return value, []selects{} - } - - ret := selects{} - for _, axis := range boolAttr.SortedConfigurationAxes() { - configToBools := boolAttr.ConfigurableValues[axis] - for config, bools := range configToBools { - selectKey := axis.SelectKey(config) - ret[selectKey] = reflect.ValueOf(bools) - } - } - // if there is a select, use the base value as the conditions default value - if len(ret) > 0 { - ret[bazel.ConditionsDefaultSelectKey] = value - value = reflect.Zero(value.Type()) - } - - return value, []selects{ret} -} -func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, []selects, bool) { - value := reflect.ValueOf(list.Value.Includes) - prepend := list.Prepend - var ret []selects - for _, axis := range list.SortedConfigurationAxes() { - configToLabels := list.ConfigurableValues[axis] - if !configToLabels.HasConfigurableValues() { - continue - } - archSelects := map[string]reflect.Value{} - defaultVal := configToLabels[bazel.ConditionsDefaultConfigKey] - // Skip empty list values unless ether EmitEmptyList is true, or these values differ from the default. - emitEmptyList := list.EmitEmptyList || len(defaultVal.Includes) > 0 - for config, labels := range configToLabels { - // Omit any entries in the map which match the default value, for brevity. - if config != bazel.ConditionsDefaultConfigKey && labels.Equals(defaultVal) { - continue - } - selectKey := axis.SelectKey(config) - if use, value := labelListSelectValue(selectKey, labels, emitEmptyList); use { - archSelects[selectKey] = value - } - } - if len(archSelects) > 0 { - ret = append(ret, archSelects) - } - } - - return value, ret, prepend -} - -func labelListSelectValue(selectKey string, list bazel.LabelList, emitEmptyList bool) (bool, reflect.Value) { - if selectKey == bazel.ConditionsDefaultSelectKey || emitEmptyList || len(list.Includes) > 0 { - return true, reflect.ValueOf(list.Includes) - } else if len(list.Excludes) > 0 { - // if there is still an excludes -- we need to have an empty list for this select & use the - // value in conditions default Includes - return true, reflect.ValueOf([]string{}) - } - return false, reflect.Zero(reflect.TypeOf([]string{})) -} - -var ( - emptyBazelList = "[]" - bazelNone = "None" -) - -// prettyPrintAttribute converts an Attribute to its Bazel syntax. May contain -// select statements. -func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) { - var value reflect.Value - // configurableAttrs is the list of individual select statements to be - // concatenated together. These select statements should be along different - // axes. For example, one element may be - // `select({"//color:red": "one", "//color:green": "two"})`, and the second - // element may be `select({"//animal:cat": "three", "//animal:dog": "four"}). - // These selects should be sorted by axis identifier. - var configurableAttrs []selects - var prepend bool - var defaultSelectValue *string - var emitZeroValues bool - // If true, print the default attribute value, even if the attribute is zero. - shouldPrintDefault := false - switch list := v.(type) { - case bazel.StringAttribute: - if err := list.Collapse(); err != nil { - return "", err - } - value, configurableAttrs = getStringValue(list) - defaultSelectValue = &bazelNone - case bazel.StringListAttribute: - value, configurableAttrs, prepend = getStringListValues(list) - defaultSelectValue = &emptyBazelList - case bazel.LabelListAttribute: - value, configurableAttrs, prepend = getLabelListValues(list) - emitZeroValues = list.EmitEmptyList - defaultSelectValue = &emptyBazelList - if list.ForceSpecifyEmptyList && (!value.IsNil() || list.HasConfigurableValues()) { - shouldPrintDefault = true - } - case bazel.LabelAttribute: - if err := list.Collapse(); err != nil { - return "", err - } - value, configurableAttrs = getLabelValue(list) - defaultSelectValue = &bazelNone - case bazel.BoolAttribute: - if err := list.Collapse(); err != nil { - return "", err - } - value, configurableAttrs = getBoolValue(list) - defaultSelectValue = &bazelNone - default: - return "", fmt.Errorf("Not a supported Bazel attribute type: %s", v) - } - - var err error - ret := "" - if value.Kind() != reflect.Invalid { - s, err := prettyPrint(value, indent, false) // never emit zero values for the base value - if err != nil { - return ret, err - } - - ret += s - } - // Convenience function to prepend/append selects components to an attribute value. - concatenateSelects := func(selectsData selects, defaultValue *string, s string, prepend bool) (string, error) { - selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent, emitZeroValues) - if err != nil { - return "", err - } - var left, right string - if prepend { - left, right = selectMap, s - } else { - left, right = s, selectMap - } - if left != "" && right != "" { - left += " + " - } - left += right - - return left, nil - } - - for _, configurableAttr := range configurableAttrs { - ret, err = concatenateSelects(configurableAttr, defaultSelectValue, ret, prepend) - if err != nil { - return "", err - } - } - - if ret == "" && shouldPrintDefault { - return *defaultSelectValue, nil - } - return ret, nil -} - -// prettyPrintSelectMap converts a map of select keys to reflected Values as a generic way -// to construct a select map for any kind of attribute type. -func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue *string, indent int, emitZeroValues bool) (string, error) { - if selectMap == nil { - return "", nil - } - - var selects string - for _, selectKey := range android.SortedKeys(selectMap) { - if selectKey == bazel.ConditionsDefaultSelectKey { - // Handle default condition later. - continue - } - value := selectMap[selectKey] - if isZero(value) && !emitZeroValues && isZero(selectMap[bazel.ConditionsDefaultSelectKey]) { - // Ignore zero values to not generate empty lists. However, always note zero values if - // the default value is non-zero. - continue - } - s, err := prettyPrintSelectEntry(value, selectKey, indent, true) - if err != nil { - return "", err - } - // s could still be an empty string, e.g. unset slices of structs with - // length of 0. - if s != "" { - selects += s + ",\n" - } - } - - if len(selects) == 0 { - // If there is a default value, and there are no selects for this axis, print that without any selects. - if val, exists := selectMap[bazel.ConditionsDefaultSelectKey]; exists { - return prettyPrint(val, indent, emitZeroValues) - } - // No conditions (or all values are empty lists), so no need for a map. - return "", nil - } - - // Create the map. - ret := "select({\n" - ret += selects - - // Handle the default condition - s, err := prettyPrintSelectEntry(selectMap[bazel.ConditionsDefaultSelectKey], bazel.ConditionsDefaultSelectKey, indent, emitZeroValues) - if err != nil { - return "", err - } - if s != "" { - // Print the custom default value. - ret += s - ret += ",\n" - } else if defaultValue != nil { - // Print an explicit empty list (the default value) even if the value is - // empty, to avoid errors about not finding a configuration that matches. - ret += fmt.Sprintf("%s\"%s\": %s,\n", starlark_fmt.Indention(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue) - } - - ret += starlark_fmt.Indention(indent) - ret += "})" - - return ret, nil -} - -// prettyPrintSelectEntry converts a reflect.Value into an entry in a select map -// with a provided key. -func prettyPrintSelectEntry(value reflect.Value, key string, indent int, emitZeroValues bool) (string, error) { - s := starlark_fmt.Indention(indent + 1) - v, err := prettyPrint(value, indent+1, emitZeroValues) - if err != nil { - return "", err - } - if v == "" { - return "", nil - } - s += fmt.Sprintf("\"%s\": %s", key, v) - return s, nil -} diff --git a/bp2build/constants.go b/bp2build/constants.go index 4870dffff..76ba106c4 100644 --- a/bp2build/constants.go +++ b/bp2build/constants.go @@ -20,9 +20,4 @@ var ( // The file name used for automatically generated files. GeneratedBuildFileName = "BUILD.bazel" - - // The file name used for hand-crafted build targets. - // NOTE: It is okay that this matches GeneratedBuildFileName, since we generate BUILD files in a different directory to source files - // FIXME: Because there are hundreds of existing BUILD.bazel files in the AOSP tree, we should pick another name here, like BUILD.android - HandcraftedBuildFileName = "BUILD.bazel" ) diff --git a/bpf/bpf.go b/bpf/bpf.go index 73c8800d8..86798217f 100644 --- a/bpf/bpf.go +++ b/bpf/bpf.go @@ -56,6 +56,7 @@ var ( ) func registerBpfBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("bpf_defaults", defaultsFactory) ctx.RegisterModuleType("bpf", BpfFactory) } @@ -77,10 +78,16 @@ type BpfProperties struct { // the C/C++ module. Cflags []string - // directories (relative to the root of the source tree) that will - // be added to the include paths using -I. + // list of directories relative to the root of the source tree that + // will be added to the include paths using -I. + // If possible, don't use this. If adding paths from the current + // directory, use local_include_dirs. If adding paths from other + // modules, use export_include_dirs in that module. Include_dirs []string + // list of directories relative to the Blueprint file that will be + // added to the include path using -I. + Local_include_dirs []string // optional subdirectory under which this module is installed into. Sub_dir string @@ -94,7 +101,7 @@ type BpfProperties struct { type bpf struct { android.ModuleBase - + android.DefaultableModuleBase properties BpfProperties objs android.Paths @@ -163,6 +170,10 @@ func (bpf *bpf) GenerateAndroidBuildActions(ctx android.ModuleContext) { "-I " + ctx.ModuleDir(), } + for _, dir := range android.PathsForModuleSrc(ctx, bpf.properties.Local_include_dirs) { + cflags = append(cflags, "-I "+dir.String()) + } + for _, dir := range android.PathsForSource(ctx, bpf.properties.Include_dirs) { cflags = append(cflags, "-I "+dir.String()) } @@ -264,6 +275,26 @@ func (bpf *bpf) AndroidMk() android.AndroidMkData { } } +type Defaults struct { + android.ModuleBase + android.DefaultsModuleBase +} + +func defaultsFactory() android.Module { + return DefaultsFactory() +} + +func DefaultsFactory(props ...interface{}) android.Module { + module := &Defaults{} + + module.AddProperties(props...) + module.AddProperties(&BpfProperties{}) + + android.InitDefaultsModule(module) + + return module +} + func (bpf *bpf) SubDir() string { return bpf.properties.Sub_dir } @@ -274,5 +305,7 @@ func BpfFactory() android.Module { module.AddProperties(&module.properties) android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) + android.InitDefaultableModule(module) + return module } diff --git a/bpf/libbpf/Android.bp b/bpf/libbpf/Android.bp new file mode 100644 index 000000000..f0ba90f52 --- /dev/null +++ b/bpf/libbpf/Android.bp @@ -0,0 +1,38 @@ +// +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +bootstrap_go_package { + name: "soong-libbpf", + pkgPath: "android/soong/bpf/libbpf", + deps: [ + "blueprint", + "blueprint-proptools", + "soong-android", + "soong-cc", + "soong-cc-config", + ], + srcs: [ + "libbpf_prog.go", + ], + testSrcs: [ + "libbpf_prog_test.go", + ], + pluginFor: ["soong_build"], +} diff --git a/bpf/libbpf/libbpf_prog.go b/bpf/libbpf/libbpf_prog.go new file mode 100644 index 000000000..ac6151040 --- /dev/null +++ b/bpf/libbpf/libbpf_prog.go @@ -0,0 +1,310 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package libbpf_prog + +import ( + "fmt" + "io" + "runtime" + "strings" + + "android/soong/android" + "android/soong/cc" + "android/soong/genrule" + + "github.com/google/blueprint" +) + +type libbpfProgDepType struct { + blueprint.BaseDependencyTag +} + +func init() { + registerLibbpfProgBuildComponents(android.InitRegistrationContext) + pctx.Import("android/soong/cc/config") + pctx.StaticVariable("relPwd", cc.PwdPrefix()) +} + +var ( + pctx = android.NewPackageContext("android/soong/bpf/libbpf_prog") + + libbpfProgCcRule = pctx.AndroidStaticRule("libbpfProgCcRule", + blueprint.RuleParams{ + Depfile: "${out}.d", + Deps: blueprint.DepsGCC, + Command: "$relPwd $ccCmd --target=bpf -c $cFlags -MD -MF ${out}.d -o $out $in", + CommandDeps: []string{"$ccCmd"}, + }, + "ccCmd", "cFlags") + + libbpfProgStripRule = pctx.AndroidStaticRule("libbpfProgStripRule", + blueprint.RuleParams{ + Command: `$stripCmd --strip-unneeded --remove-section=.rel.BTF ` + + `--remove-section=.rel.BTF.ext --remove-section=.BTF.ext $in -o $out`, + CommandDeps: []string{"$stripCmd"}, + }, + "stripCmd") + + libbpfProgDepTag = libbpfProgDepType{} +) + +func registerLibbpfProgBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("libbpf_defaults", defaultsFactory) + ctx.RegisterModuleType("libbpf_prog", LibbpfProgFactory) +} + +var PrepareForTestWithLibbpfProg = android.GroupFixturePreparers( + android.FixtureRegisterWithContext(registerLibbpfProgBuildComponents), + android.FixtureAddFile("libbpf_headers/Foo.h", nil), + android.FixtureAddFile("libbpf_headers/Android.bp", []byte(` + genrule { + name: "libbpf_headers", + out: ["foo.h",], + } + `)), + genrule.PrepareForTestWithGenRuleBuildComponents, +) + +type LibbpfProgProperties struct { + // source paths to the files. + Srcs []string `android:"path"` + + // additional cflags that should be used to build the libbpf variant of + // the C/C++ module. + Cflags []string `android:"arch_variant"` + + // list of directories relative to the Blueprint file that will + // be added to the include path using -I + Local_include_dirs []string `android:"arch_variant"` + + Header_libs []string `android:"arch_variant"` + + // optional subdirectory under which this module is installed into. + Relative_install_path string +} + +type libbpfProg struct { + android.ModuleBase + android.DefaultableModuleBase + properties LibbpfProgProperties + objs android.Paths +} + +var _ android.ImageInterface = (*libbpfProg)(nil) + +func (libbpf *libbpfProg) ImageMutatorBegin(ctx android.BaseModuleContext) {} + +func (libbpf *libbpfProg) VendorVariantNeeded(ctx android.BaseModuleContext) bool { + return false +} + +func (libbpf *libbpfProg) ProductVariantNeeded(ctx android.BaseModuleContext) bool { + return false +} + +func (libbpf *libbpfProg) CoreVariantNeeded(ctx android.BaseModuleContext) bool { + return true +} + +func (libbpf *libbpfProg) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool { + return false +} + +func (libbpf *libbpfProg) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { + return false +} + +func (libbpf *libbpfProg) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { + return false +} + +func (libbpf *libbpfProg) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool { + return false +} + +func (libbpf *libbpfProg) ExtraImageVariations(ctx android.BaseModuleContext) []string { + return nil +} + +func (libbpf *libbpfProg) SetImageVariation(ctx android.BaseModuleContext, variation string) { +} + +func (libbpf *libbpfProg) DepsMutator(ctx android.BottomUpMutatorContext) { + ctx.AddDependency(ctx.Module(), libbpfProgDepTag, "libbpf_headers") + ctx.AddVariationDependencies(nil, cc.HeaderDepTag(), libbpf.properties.Header_libs...) +} + +func (libbpf *libbpfProg) GenerateAndroidBuildActions(ctx android.ModuleContext) { + var cFlagsDeps android.Paths + cflags := []string{ + "-nostdlibinc", + + // Make paths in deps files relative + "-no-canonical-prefixes", + + "-O2", + "-Wall", + "-Werror", + "-Wextra", + + "-isystem bionic/libc/include", + "-isystem bionic/libc/kernel/uapi", + // The architecture doesn't matter here, but asm/types.h is included by linux/types.h. + "-isystem bionic/libc/kernel/uapi/asm-arm64", + "-isystem bionic/libc/kernel/android/uapi", + "-I " + ctx.ModuleDir(), + "-g", //Libbpf builds require BTF data + } + + if runtime.GOOS != "darwin" { + cflags = append(cflags, "-fdebug-prefix-map=/proc/self/cwd=") + } + + ctx.VisitDirectDeps(func(dep android.Module) { + depTag := ctx.OtherModuleDependencyTag(dep) + if depTag == libbpfProgDepTag { + if genRule, ok := dep.(genrule.SourceFileGenerator); ok { + cFlagsDeps = append(cFlagsDeps, genRule.GeneratedDeps()...) + dirs := genRule.GeneratedHeaderDirs() + for _, dir := range dirs { + cflags = append(cflags, "-I "+dir.String()) + } + } else { + depName := ctx.OtherModuleName(dep) + ctx.ModuleErrorf("module %q is not a genrule", depName) + } + } else if depTag == cc.HeaderDepTag() { + depExporterInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider) + for _, dir := range depExporterInfo.IncludeDirs { + cflags = append(cflags, "-I "+dir.String()) + } + } + }) + + for _, dir := range android.PathsForModuleSrc(ctx, libbpf.properties.Local_include_dirs) { + cflags = append(cflags, "-I "+dir.String()) + } + + cflags = append(cflags, libbpf.properties.Cflags...) + + srcs := android.PathsForModuleSrc(ctx, libbpf.properties.Srcs) + + for _, src := range srcs { + if strings.ContainsRune(src.Base(), '_') { + ctx.ModuleErrorf("invalid character '_' in source name") + } + obj := android.ObjPathWithExt(ctx, "unstripped", src, "o") + + ctx.Build(pctx, android.BuildParams{ + Rule: libbpfProgCcRule, + Input: src, + Implicits: cFlagsDeps, + Output: obj, + Args: map[string]string{ + "cFlags": strings.Join(cflags, " "), + "ccCmd": "${config.ClangBin}/clang", + }, + }) + + objStripped := android.ObjPathWithExt(ctx, "", src, "o") + ctx.Build(pctx, android.BuildParams{ + Rule: libbpfProgStripRule, + Input: obj, + Output: objStripped, + Args: map[string]string{ + "stripCmd": "${config.ClangBin}/llvm-strip", + }, + }) + libbpf.objs = append(libbpf.objs, objStripped.WithoutRel()) + } + + installDir := android.PathForModuleInstall(ctx, "etc", "bpf/libbpf") + if len(libbpf.properties.Relative_install_path) > 0 { + installDir = installDir.Join(ctx, libbpf.properties.Relative_install_path) + } + for _, obj := range libbpf.objs { + ctx.PackageFile(installDir, obj.Base(), obj) + } + + android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcs.Strings()}) + + ctx.SetOutputFiles(libbpf.objs, "") +} + +func (libbpf *libbpfProg) AndroidMk() android.AndroidMkData { + return android.AndroidMkData{ + Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { + var names []string + fmt.Fprintln(w) + fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir) + fmt.Fprintln(w) + var localModulePath string + localModulePath = "LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf/libbpf" + if len(libbpf.properties.Relative_install_path) > 0 { + localModulePath += "/" + libbpf.properties.Relative_install_path + } + for _, obj := range libbpf.objs { + objName := name + "_" + obj.Base() + names = append(names, objName) + fmt.Fprintln(w, "include $(CLEAR_VARS)", " # libbpf.libbpf.obj") + fmt.Fprintln(w, "LOCAL_MODULE := ", objName) + fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", obj.String()) + fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", obj.Base()) + fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") + fmt.Fprintln(w, localModulePath) + // AconfigUpdateAndroidMkData may have added elements to Extra. Process them here. + for _, extra := range data.Extra { + extra(w, nil) + } + fmt.Fprintln(w, "include $(BUILD_PREBUILT)") + fmt.Fprintln(w) + } + fmt.Fprintln(w, "include $(CLEAR_VARS)", " # libbpf.libbpf") + fmt.Fprintln(w, "LOCAL_MODULE := ", name) + android.AndroidMkEmitAssignList(w, "LOCAL_REQUIRED_MODULES", names) + fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)") + }, + } +} + +type Defaults struct { + android.ModuleBase + android.DefaultsModuleBase +} + +func defaultsFactory() android.Module { + return DefaultsFactory() +} + +func DefaultsFactory(props ...interface{}) android.Module { + module := &Defaults{} + + module.AddProperties(props...) + module.AddProperties(&LibbpfProgProperties{}) + + android.InitDefaultsModule(module) + + return module +} + +func LibbpfProgFactory() android.Module { + module := &libbpfProg{} + + module.AddProperties(&module.properties) + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst) + android.InitDefaultableModule(module) + + return module +} diff --git a/bpf/libbpf/libbpf_prog_test.go b/bpf/libbpf/libbpf_prog_test.go new file mode 100644 index 000000000..f4f51672f --- /dev/null +++ b/bpf/libbpf/libbpf_prog_test.go @@ -0,0 +1,69 @@ +// Copyright 2024 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package libbpf_prog + +import ( + "os" + "testing" + + "android/soong/android" + "android/soong/cc" +) + +func TestMain(m *testing.M) { + os.Exit(m.Run()) +} + +var prepareForLibbpfProgTest = android.GroupFixturePreparers( + cc.PrepareForTestWithCcDefaultModules, + android.FixtureMergeMockFs( + map[string][]byte{ + "bpf.c": nil, + "bpf_invalid_name.c": nil, + "BpfTest.cpp": nil, + }, + ), + PrepareForTestWithLibbpfProg, +) + +func TestLibbpfProgDataDependency(t *testing.T) { + bp := ` + libbpf_prog { + name: "bpf.o", + srcs: ["bpf.c"], + } + + cc_test { + name: "vts_test_binary_bpf_module", + srcs: ["BpfTest.cpp"], + data: [":bpf.o"], + gtest: false, + } + ` + + prepareForLibbpfProgTest.RunTestWithBp(t, bp) +} + +func TestLibbpfProgSourceName(t *testing.T) { + bp := ` + libbpf_prog { + name: "bpf_invalid_name.o", + srcs: ["bpf_invalid_name.c"], + } + ` + prepareForLibbpfProgTest.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern( + `invalid character '_' in source name`)). + RunTestWithBp(t, bp) +} diff --git a/build_kzip.bash b/build_kzip.bash index 4c42048dd..850aedaf0 100755 --- a/build_kzip.bash +++ b/build_kzip.bash @@ -40,6 +40,7 @@ kzip_targets=( merge_zips xref_cxx xref_java + xref_kotlin # TODO: b/286390153 - reenable rust # xref_rust ) diff --git a/cc/Android.bp b/cc/Android.bp index e68e4a398..3688c8a9a 100644 --- a/cc/Android.bp +++ b/cc/Android.bp @@ -16,7 +16,6 @@ bootstrap_go_package { "soong-etc", "soong-fuzz", "soong-genrule", - "soong-multitree", "soong-testing", "soong-tradefed", ], @@ -65,7 +64,6 @@ bootstrap_go_package { "library.go", "library_headers.go", "library_sdk_member.go", - "library_stub.go", "native_bridge_sdk_trait.go", "object.go", "test.go", @@ -118,4 +116,6 @@ bootstrap_go_package { "cmake_module_cc.txt", ], pluginFor: ["soong_build"], + // Used by plugins + visibility: ["//visibility:public"], } diff --git a/cc/TEST_MAPPING b/cc/TEST_MAPPING new file mode 100644 index 000000000..be2809de0 --- /dev/null +++ b/cc/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "imports": [ + { + "path": "bionic" + } + ] +} diff --git a/cc/androidmk.go b/cc/androidmk.go index cecaae23a..6966f7692 100644 --- a/cc/androidmk.go +++ b/cc/androidmk.go @@ -21,7 +21,6 @@ import ( "strings" "android/soong/android" - "android/soong/multitree" ) var ( @@ -479,34 +478,6 @@ func (p *prebuiltBinaryLinker) AndroidMkEntries(ctx AndroidMkContext, entries *a androidMkWritePrebuiltOptions(p.baseLinker, entries) } -func (a *apiLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) { - entries.Class = "SHARED_LIBRARIES" - entries.SubName += multitree.GetApiImportSuffix() - - entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - a.libraryDecorator.androidMkWriteExportedFlags(entries) - src := *a.properties.Src - path, file := filepath.Split(src) - stem, suffix, ext := android.SplitFileExt(file) - entries.SetString("LOCAL_BUILT_MODULE_STEM", "$(LOCAL_MODULE)"+ext) - entries.SetString("LOCAL_MODULE_SUFFIX", suffix) - entries.SetString("LOCAL_MODULE_STEM", stem) - entries.SetString("LOCAL_MODULE_PATH", path) - entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true) - entries.SetString("LOCAL_SOONG_TOC", a.toc().String()) - }) -} - -func (a *apiHeadersDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) { - entries.Class = "HEADER_LIBRARIES" - entries.SubName += multitree.GetApiImportSuffix() - - entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - a.libraryDecorator.androidMkWriteExportedFlags(entries) - entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true) - }) -} - func androidMkWritePrebuiltOptions(linker *baseLinker, entries *android.AndroidMkEntries) { allow := linker.Properties.Allow_undefined_symbols if allow != nil { diff --git a/cc/binary_sdk_member.go b/cc/binary_sdk_member.go index 8a7ea8845..4063714ab 100644 --- a/cc/binary_sdk_member.go +++ b/cc/binary_sdk_member.go @@ -132,7 +132,7 @@ func (p *nativeBinaryInfoProperties) PopulateFromVariant(ctx android.SdkMemberCo if ccModule.linker != nil { specifiedDeps := specifiedDeps{} - specifiedDeps = ccModule.linker.linkerSpecifiedDeps(ctx, ccModule, specifiedDeps) + specifiedDeps = ccModule.linker.linkerSpecifiedDeps(ctx.SdkModuleContext(), ccModule, specifiedDeps) p.SharedLibs = specifiedDeps.sharedLibs p.SystemSharedLibs = specifiedDeps.systemSharedLibs @@ -34,7 +34,6 @@ import ( "android/soong/cc/config" "android/soong/fuzz" "android/soong/genrule" - "android/soong/multitree" ) func init() { @@ -60,10 +59,10 @@ func RegisterCCBuildComponents(ctx android.RegistrationContext) { san.registerMutators(ctx) } - ctx.TopDown("sanitize_runtime_deps", sanitizerRuntimeDepsMutator).Parallel() + ctx.BottomUp("sanitize_runtime_deps", sanitizerRuntimeDepsMutator).Parallel() ctx.BottomUp("sanitize_runtime", sanitizerRuntimeMutator).Parallel() - ctx.TopDown("fuzz_deps", fuzzMutatorDeps) + ctx.BottomUp("fuzz_deps", fuzzMutatorDeps) ctx.Transition("coverage", &coverageTransitionMutator{}) @@ -74,7 +73,7 @@ func RegisterCCBuildComponents(ctx android.RegistrationContext) { ctx.Transition("lto", <oTransitionMutator{}) ctx.BottomUp("check_linktype", checkLinkTypeMutator).Parallel() - ctx.TopDown("double_loadable", checkDoubleLoadableLibraries).Parallel() + ctx.BottomUp("double_loadable", checkDoubleLoadableLibraries).Parallel() }) ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) { @@ -138,7 +137,7 @@ type Deps struct { // LLNDK headers for the ABI checker to check LLNDK implementation library. // An LLNDK implementation is the core variant. LLNDK header libs are reexported by the vendor variant. - // The core variant cannot depend on the vendor variant because of the order of CreateVariations. + // The core variant cannot depend on the vendor variant because of the order of imageTransitionMutator.Split(). // Instead, the LLNDK implementation depends on the LLNDK header libs. LlndkHeaderLibs []string } @@ -613,7 +612,7 @@ type linker interface { coverageOutputFilePath() android.OptionalPath // Get the deps that have been explicitly specified in the properties. - linkerSpecifiedDeps(ctx android.ConfigAndErrorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps + linkerSpecifiedDeps(ctx android.ConfigurableEvaluatorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) } @@ -970,7 +969,7 @@ func (c *Module) HiddenFromMake() bool { return c.Properties.HideFromMake } -func (c *Module) RequiredModuleNames(ctx android.ConfigAndErrorContext) []string { +func (c *Module) RequiredModuleNames(ctx android.ConfigurableEvaluatorContext) []string { required := android.CopyOf(c.ModuleBase.RequiredModuleNames(ctx)) if c.ImageVariation().Variation == android.CoreVariation { required = append(required, c.Properties.Target.Platform.Required...) @@ -2361,24 +2360,6 @@ func AddSharedLibDependenciesWithVersions(ctx android.BottomUpMutatorContext, mo } } -func GetApiImports(c LinkableInterface, actx android.BottomUpMutatorContext) multitree.ApiImportInfo { - apiImportInfo := multitree.ApiImportInfo{} - - if c.Device() { - var apiImportModule []blueprint.Module - if actx.OtherModuleExists("api_imports") { - apiImportModule = actx.AddDependency(c, nil, "api_imports") - if len(apiImportModule) > 0 && apiImportModule[0] != nil { - apiInfo, _ := android.OtherModuleProvider(actx, apiImportModule[0], multitree.ApiImportsProvider) - apiImportInfo = apiInfo - android.SetProvider(actx, multitree.ApiImportsProvider, apiInfo) - } - } - } - - return apiImportInfo -} - func GetReplaceModuleName(lib string, replaceMap map[string]string) string { if snapshot, ok := replaceMap[lib]; ok { return snapshot @@ -2448,11 +2429,6 @@ func (c *Module) shouldUseApiSurface() bool { // NDK Variant return true } - - if c.isImportedApiLibrary() { - // API Library should depend on API headers - return true - } } return false @@ -2472,19 +2448,10 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) { ctx.ctx = ctx deps := c.deps(ctx) - apiImportInfo := GetApiImports(c, actx) apiNdkLibs := []string{} apiLateNdkLibs := []string{} - if c.shouldUseApiSurface() { - deps.SharedLibs, apiNdkLibs = rewriteLibsForApiImports(c, deps.SharedLibs, apiImportInfo.SharedLibs, ctx.Config()) - deps.LateSharedLibs, apiLateNdkLibs = rewriteLibsForApiImports(c, deps.LateSharedLibs, apiImportInfo.SharedLibs, ctx.Config()) - deps.SystemSharedLibs, _ = rewriteLibsForApiImports(c, deps.SystemSharedLibs, apiImportInfo.SharedLibs, ctx.Config()) - deps.ReexportHeaderLibHeaders, _ = rewriteLibsForApiImports(c, deps.ReexportHeaderLibHeaders, apiImportInfo.SharedLibs, ctx.Config()) - deps.ReexportSharedLibHeaders, _ = rewriteLibsForApiImports(c, deps.ReexportSharedLibHeaders, apiImportInfo.SharedLibs, ctx.Config()) - } - c.Properties.AndroidMkSystemSharedLibs = deps.SystemSharedLibs variantNdkLibs := []string{} @@ -2501,11 +2468,6 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) { depTag.reexportFlags = true } - // Check header lib replacement from API surface first, and then check again with VSDK - if c.shouldUseApiSurface() { - lib = GetReplaceModuleName(lib, apiImportInfo.HeaderLibs) - } - if c.isNDKStubLibrary() { variationExists := actx.OtherModuleDependencyVariantExists(nil, lib) if variationExists { @@ -2515,7 +2477,7 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) { // any variants. actx.AddFarVariationDependencies([]blueprint.Variation{}, depTag, lib) } - } else if c.IsStubs() && !c.isImportedApiLibrary() { + } else if c.IsStubs() { actx.AddFarVariationDependencies(append(ctx.Target().Variations(), c.ImageVariation()), depTag, lib) } else { @@ -2591,22 +2553,12 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) { } name, version := StubsLibNameAndVersion(lib) - if apiLibraryName, ok := apiImportInfo.SharedLibs[name]; ok && !ctx.OtherModuleExists(name) { - name = apiLibraryName - } sharedLibNames = append(sharedLibNames, name) variations := []blueprint.Variation{ {Mutator: "link", Variation: "shared"}, } - - if _, ok := apiImportInfo.ApexSharedLibs[name]; !ok || ctx.OtherModuleExists(name) { - AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, name, version, false) - } - - if apiLibraryName, ok := apiImportInfo.ApexSharedLibs[name]; ok { - AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, apiLibraryName, version, false) - } + AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, name, version, false) } for _, lib := range deps.LateStaticLibs { @@ -2701,7 +2653,6 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) { ) } - updateImportedLibraryDependency(ctx) } func BeginMutator(ctx android.BottomUpMutatorContext) { @@ -2730,10 +2681,6 @@ func checkLinkType(ctx android.BaseModuleContext, from LinkableInterface, to Lin return } - // TODO(b/244244438) : Remove this once all variants are implemented - if ccFrom, ok := from.(*Module); ok && ccFrom.isImportedApiLibrary() { - return - } if from.SdkVersion() == "" { // Platform code can link to anything return @@ -2756,10 +2703,6 @@ func checkLinkType(ctx android.BaseModuleContext, from LinkableInterface, to Lin // the NDK. return } - if c.isImportedApiLibrary() { - // Imported library from the API surface is a stub library built against interface definition. - return - } } if strings.HasPrefix(ctx.ModuleName(), "libclang_rt.") && to.Module().Name() == "libc++" { @@ -2840,7 +2783,7 @@ func checkLinkTypeMutator(ctx android.BottomUpMutatorContext) { // If a library has a vendor variant and is a (transitive) dependency of an LLNDK library, // it is subject to be double loaded. Such lib should be explicitly marked as double_loadable: true // or as vndk-sp (vndk: { enabled: true, support_system_process: true}). -func checkDoubleLoadableLibraries(ctx android.TopDownMutatorContext) { +func checkDoubleLoadableLibraries(ctx android.BottomUpMutatorContext) { check := func(child, parent android.Module) bool { to, ok := child.(*Module) if !ok { @@ -2935,47 +2878,6 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { skipModuleList := map[string]bool{} - var apiImportInfo multitree.ApiImportInfo - hasApiImportInfo := false - - ctx.VisitDirectDeps(func(dep android.Module) { - if dep.Name() == "api_imports" { - apiImportInfo, _ = android.OtherModuleProvider(ctx, dep, multitree.ApiImportsProvider) - hasApiImportInfo = true - } - }) - - if hasApiImportInfo { - targetStubModuleList := map[string]string{} - targetOrigModuleList := map[string]string{} - - // Search for dependency which both original module and API imported library with APEX stub exists - ctx.VisitDirectDeps(func(dep android.Module) { - depName := ctx.OtherModuleName(dep) - if apiLibrary, ok := apiImportInfo.ApexSharedLibs[depName]; ok { - targetStubModuleList[apiLibrary] = depName - } - }) - ctx.VisitDirectDeps(func(dep android.Module) { - depName := ctx.OtherModuleName(dep) - if origLibrary, ok := targetStubModuleList[depName]; ok { - targetOrigModuleList[origLibrary] = depName - } - }) - - // Decide which library should be used between original and API imported library - ctx.VisitDirectDeps(func(dep android.Module) { - depName := ctx.OtherModuleName(dep) - if apiLibrary, ok := targetOrigModuleList[depName]; ok { - if ShouldUseStubForApex(ctx, dep) { - skipModuleList[depName] = true - } else { - skipModuleList[apiLibrary] = true - } - } - }) - } - ctx.VisitDirectDeps(func(dep android.Module) { depName := ctx.OtherModuleName(dep) depTag := ctx.OtherModuleDependencyTag(dep) @@ -3404,17 +3306,7 @@ func ShouldUseStubForApex(ctx android.ModuleContext, dep android.Module) bool { // bootstrap modules, always link to non-stub variant isNotInPlatform := dep.(android.ApexModule).NotInPlatform() - isApexImportedApiLibrary := false - - if cc, ok := dep.(*Module); ok { - if apiLibrary, ok := cc.linker.(*apiLibraryDecorator); ok { - if apiLibrary.hasApexStubs() { - isApexImportedApiLibrary = true - } - } - } - - useStubs = (isNotInPlatform || isApexImportedApiLibrary) && !bootstrap + useStubs = isNotInPlatform && !bootstrap if useStubs { // Another exception: if this module is a test for an APEX, then @@ -3439,7 +3331,7 @@ func ShouldUseStubForApex(ctx android.ModuleContext, dep android.Module) bool { // 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 !isApexImportedApiLibrary && android.AvailableToSameApexes(thisModule, dep.(android.ApexModule)) { + if android.AvailableToSameApexes(thisModule, dep.(android.ApexModule)) { useStubs = false } } @@ -4023,11 +3915,6 @@ func (c *Module) IsSdkVariant() bool { return c.Properties.IsSdkVariant } -func (c *Module) isImportedApiLibrary() bool { - _, ok := c.linker.(*apiLibraryDecorator) - return ok -} - func kytheExtractAllFactory() android.Singleton { return &kytheExtractAllSingleton{} } diff --git a/cc/cc_test.go b/cc/cc_test.go index 93630dbd7..3f3347b51 100644 --- a/cc/cc_test.go +++ b/cc/cc_test.go @@ -49,17 +49,30 @@ var apexVersion = "28" func registerTestMutators(ctx android.RegistrationContext) { ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { - ctx.BottomUp("apex", testApexMutator).Parallel() + ctx.Transition("apex", &testApexTransitionMutator{}) }) } -func testApexMutator(mctx android.BottomUpMutatorContext) { - modules := mctx.CreateVariations(apexVariationName) +type testApexTransitionMutator struct{} + +func (t *testApexTransitionMutator) Split(ctx android.BaseModuleContext) []string { + return []string{apexVariationName} +} + +func (t *testApexTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string { + return sourceVariation +} + +func (t *testApexTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string { + return incomingVariation +} + +func (t *testApexTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) { apexInfo := android.ApexInfo{ ApexVariationName: apexVariationName, MinSdkVersion: android.ApiLevelForTest(apexVersion), } - mctx.SetVariationProvider(modules[0], android.ApexInfoProvider, apexInfo) + android.SetProvider(ctx, android.ApexInfoProvider, apexInfo) } // testCcWithConfig runs tests using the prepareForCcTest diff --git a/cc/check.go b/cc/check.go index fa1926dd4..8e2844f16 100644 --- a/cc/check.go +++ b/cc/check.go @@ -40,6 +40,8 @@ func CheckBadCompilerFlags(ctx BaseModuleContext, prop string, flags []string) { ctx.PropertyErrorf(prop, "Bad flag: `%s`, use native_coverage instead", flag) } else if flag == "-fwhole-program-vtables" { ctx.PropertyErrorf(prop, "Bad flag: `%s`, use whole_program_vtables instead", flag) + } else if flag == "-gsplit-dwarf" { + ctx.PropertyErrorf(prop, "Bad flag: `%s`, soong cannot track dependencies to split dwarf debuginfo", flag) } else if flag == "-fno-integrated-as" { ctx.PropertyErrorf("Bad flag: `%s` is disallowed as it may invoke the `as` from the build host", flag) } else if flag == "-Weverything" { diff --git a/cc/compiler.go b/cc/compiler.go index 396ec886b..a6f623f5b 100644 --- a/cc/compiler.go +++ b/cc/compiler.go @@ -437,18 +437,20 @@ func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags, deps flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VNDK__") if ctx.inVendor() { flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VENDOR__") - - vendorApiLevel := ctx.Config().VendorApiLevel() - if vendorApiLevel == "" { - // TODO(b/314036847): This is a fallback for UDC targets. - // This must be a build failure when UDC is no longer built - // from this source tree. - vendorApiLevel = ctx.Config().PlatformSdkVersion().String() - } - flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VENDOR_API__="+vendorApiLevel) } else if ctx.inProduct() { flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_PRODUCT__") } + + // Define __ANDROID_VENDOR_API__ for both product and vendor variants + // because they both use the same LLNDK libraries. + vendorApiLevel := ctx.Config().VendorApiLevel() + if vendorApiLevel == "" { + // TODO(b/314036847): This is a fallback for UDC targets. + // This must be a build failure when UDC is no longer built + // from this source tree. + vendorApiLevel = ctx.Config().PlatformSdkVersion().String() + } + flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VENDOR_API__="+vendorApiLevel) } if ctx.inRecovery() { diff --git a/cc/config/Android.bp b/cc/config/Android.bp index 289409fb3..f514db68d 100644 --- a/cc/config/Android.bp +++ b/cc/config/Android.bp @@ -35,4 +35,8 @@ bootstrap_go_package { testSrcs: [ "tidy_test.go", ], + visibility: [ + "//build/soong:__subpackages__", + "//prebuilts/clang/host/linux-x86/soong", + ], } diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go index 761afcf18..0dcf2cfd4 100644 --- a/cc/config/arm64_device.go +++ b/cc/config/arm64_device.go @@ -118,11 +118,9 @@ func init() { pctx.StaticVariable("Arm64Cppflags", strings.Join(arm64Cppflags, " ")) - pctx.StaticVariable("Arm64Armv8ACflags", strings.Join(arm64ArchVariantCflags["armv8-a"], " ")) - pctx.StaticVariable("Arm64Armv8ABranchProtCflags", strings.Join(arm64ArchVariantCflags["armv8-a-branchprot"], " ")) - pctx.StaticVariable("Arm64Armv82ACflags", strings.Join(arm64ArchVariantCflags["armv8-2a"], " ")) - pctx.StaticVariable("Arm64Armv82ADotprodCflags", strings.Join(arm64ArchVariantCflags["armv8-2a-dotprod"], " ")) - pctx.StaticVariable("Arm64Armv9ACflags", strings.Join(arm64ArchVariantCflags["armv9-a"], " ")) + for variant, cflags := range arm64ArchVariantCflags { + pctx.StaticVariable("Arm64"+variant+"VariantCflags", strings.Join(cflags, " ")) + } pctx.StaticVariable("Arm64CortexA53Cflags", strings.Join(arm64CpuVariantCflags["cortex-a53"], " ")) pctx.StaticVariable("Arm64CortexA55Cflags", strings.Join(arm64CpuVariantCflags["cortex-a55"], " ")) @@ -134,14 +132,6 @@ func init() { } var ( - arm64ArchVariantCflagsVar = map[string]string{ - "armv8-a": "${config.Arm64Armv8ACflags}", - "armv8-a-branchprot": "${config.Arm64Armv8ABranchProtCflags}", - "armv8-2a": "${config.Arm64Armv82ACflags}", - "armv8-2a-dotprod": "${config.Arm64Armv82ADotprodCflags}", - "armv9-a": "${config.Arm64Armv9ACflags}", - } - arm64CpuVariantCflagsVar = map[string]string{ "cortex-a53": "${config.Arm64CortexA53Cflags}", "cortex-a55": "${config.Arm64CortexA55Cflags}", @@ -211,18 +201,12 @@ func (toolchainArm64) LibclangRuntimeLibraryArch() string { } func arm64ToolchainFactory(arch android.Arch) Toolchain { - switch arch.ArchVariant { - case "armv8-a": - case "armv8-a-branchprot": - case "armv8-2a": - case "armv8-2a-dotprod": - case "armv9-a": - // Nothing extra for armv8-a/armv8-2a - default: - panic(fmt.Sprintf("Unknown ARM architecture version: %q", arch.ArchVariant)) + // Error now rather than having a confusing Ninja error + if _, ok := arm64ArchVariantCflags[arch.ArchVariant]; !ok { + panic(fmt.Sprintf("Unknown ARM64 architecture version: %q", arch.ArchVariant)) } - toolchainCflags := []string{arm64ArchVariantCflagsVar[arch.ArchVariant]} + toolchainCflags := []string{"${config.Arm64" + arch.ArchVariant + "VariantCflags}"} toolchainCflags = append(toolchainCflags, variantOrDefault(arm64CpuVariantCflagsVar, arch.CpuVariant)) diff --git a/cc/config/arm64_linux_host.go b/cc/config/arm64_linux_host.go index 438e0e6b6..a19b0ed92 100644 --- a/cc/config/arm64_linux_host.go +++ b/cc/config/arm64_linux_host.go @@ -87,8 +87,8 @@ func (toolchainLinuxBionicArm64) CrtBeginSharedBinary() []string { } func linuxBionicArm64ToolchainFactory(arch android.Arch) Toolchain { - archVariant := "armv8-a" // for host, default to armv8-a - toolchainCflags := []string{arm64ArchVariantCflagsVar[archVariant]} + // for host, default to armv8-a + toolchainCflags := []string{"-march=armv8-a"} // We don't specify CPU architecture for host. Conservatively assume // the host CPU needs the fix diff --git a/cc/config/global.go b/cc/config/global.go index c83835712..9d3de6d68 100644 --- a/cc/config/global.go +++ b/cc/config/global.go @@ -286,6 +286,8 @@ var ( // New warnings to be fixed after clang-r468909 "-Wno-error=deprecated-builtins", // http://b/241601211 "-Wno-error=deprecated", // in external/googletest/googletest + // Disabling until the warning is fixed in libc++abi header files b/366180429 + "-Wno-deprecated-dynamic-exception-spec", // New warnings to be fixed after clang-r475365 "-Wno-error=enum-constexpr-conversion", // http://b/243964282 // New warnings to be fixed after clang-r522817 diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go index 5aa2a7e3b..e7ac03863 100644 --- a/cc/config/x86_64_device.go +++ b/cc/config/x86_64_device.go @@ -40,6 +40,9 @@ var ( "-march=x86-64", }, + "alderlake": []string{ + "-march=alderlake", + }, "broadwell": []string{ "-march=broadwell", }, diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go index 4b0041c9e..a92881d91 100644 --- a/cc/config/x86_device.go +++ b/cc/config/x86_device.go @@ -42,6 +42,9 @@ var ( "x86_64": []string{ "-march=prescott", }, + "alderlake": []string{ + "-march=alderlake", + }, "atom": []string{ "-march=atom", }, diff --git a/cc/fuzz.go b/cc/fuzz.go index d9e221b16..3f21bc6e7 100644 --- a/cc/fuzz.go +++ b/cc/fuzz.go @@ -57,7 +57,7 @@ func (fuzzer *fuzzer) props() []interface{} { return []interface{}{&fuzzer.Properties} } -func fuzzMutatorDeps(mctx android.TopDownMutatorContext) { +func fuzzMutatorDeps(mctx android.BottomUpMutatorContext) { currentModule, ok := mctx.Module().(*Module) if !ok { return diff --git a/cc/generated_cc_library.go b/cc/generated_cc_library.go index b1084e4e5..709586b13 100644 --- a/cc/generated_cc_library.go +++ b/cc/generated_cc_library.go @@ -18,7 +18,7 @@ import ( "android/soong/android" ) -func GeneratedCcLibraryModuleFactory(moduleName string, callbacks Generator) android.Module { +func GeneratedCcLibraryModuleFactory(callbacks Generator) android.Module { module, _ := NewLibrary(android.HostAndDeviceSupported) // Can be used as both a static and a shared library. diff --git a/cc/libbuildversion/Android.bp b/cc/libbuildversion/Android.bp index b105a3029..c1f2c1044 100644 --- a/cc/libbuildversion/Android.bp +++ b/cc/libbuildversion/Android.bp @@ -20,4 +20,5 @@ cc_library_static { "//apex_available:anyapex", ], vendor_available: true, + visibility: ["//visibility:public"], } diff --git a/cc/library.go b/cc/library.go index 03f7174c6..3833b9846 100644 --- a/cc/library.go +++ b/cc/library.go @@ -919,7 +919,7 @@ func (library *libraryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps { return deps } -func (library *libraryDecorator) linkerSpecifiedDeps(ctx android.ConfigAndErrorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps { +func (library *libraryDecorator) linkerSpecifiedDeps(ctx android.ConfigurableEvaluatorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps { specifiedDeps = library.baseLinker.linkerSpecifiedDeps(ctx, module, specifiedDeps) var properties StaticOrSharedProperties if library.static() { @@ -2350,9 +2350,8 @@ func (versionTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, varia if library := moduleLibraryInterface(ctx.Module()); library != nil && canBeVersionVariant(m) { isLLNDK := m.IsLlndk() isVendorPublicLibrary := m.IsVendorPublicLibrary() - isImportedApiLibrary := m.isImportedApiLibrary() - if variation != "" || isLLNDK || isVendorPublicLibrary || isImportedApiLibrary { + if variation != "" || isLLNDK || isVendorPublicLibrary { // A stubs or LLNDK stubs variant. if m.sanitize != nil { m.sanitize.Properties.ForceDisable = true diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go index 053c46069..af3658d58 100644 --- a/cc/library_sdk_member.go +++ b/cc/library_sdk_member.go @@ -543,7 +543,7 @@ func (p *nativeLibInfoProperties) PopulateFromVariant(ctx android.SdkMemberConte p.ExportedFlags = exportedInfo.Flags if ccModule.linker != nil { specifiedDeps := specifiedDeps{} - specifiedDeps = ccModule.linker.linkerSpecifiedDeps(ctx, ccModule, specifiedDeps) + specifiedDeps = ccModule.linker.linkerSpecifiedDeps(ctx.SdkModuleContext(), ccModule, specifiedDeps) if lib := ccModule.library; lib != nil { if !lib.hasStubsVariants() { diff --git a/cc/library_stub.go b/cc/library_stub.go deleted file mode 100644 index 636782581..000000000 --- a/cc/library_stub.go +++ /dev/null @@ -1,512 +0,0 @@ -// Copyright 2021 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cc - -import ( - "regexp" - "strings" - - "android/soong/android" - "android/soong/multitree" - - "github.com/google/blueprint/proptools" -) - -var ( - ndkVariantRegex = regexp.MustCompile("ndk\\.([a-zA-Z0-9]+)") - stubVariantRegex = regexp.MustCompile("apex\\.([a-zA-Z0-9]+)") -) - -func init() { - RegisterLibraryStubBuildComponents(android.InitRegistrationContext) -} - -func RegisterLibraryStubBuildComponents(ctx android.RegistrationContext) { - ctx.RegisterModuleType("cc_api_library", CcApiLibraryFactory) - ctx.RegisterModuleType("cc_api_headers", CcApiHeadersFactory) - ctx.RegisterModuleType("cc_api_variant", CcApiVariantFactory) -} - -func updateImportedLibraryDependency(ctx android.BottomUpMutatorContext) { - m, ok := ctx.Module().(*Module) - if !ok { - return - } - - apiLibrary, ok := m.linker.(*apiLibraryDecorator) - if !ok { - return - } - - if m.InVendorOrProduct() && apiLibrary.hasLLNDKStubs() { - // Add LLNDK variant dependency - if inList("llndk", apiLibrary.properties.Variants) { - variantName := BuildApiVariantName(m.BaseModuleName(), "llndk", "") - ctx.AddDependency(m, nil, variantName) - } - } else if m.IsSdkVariant() { - // Add NDK variant dependencies - targetVariant := "ndk." + m.StubsVersion() - if inList(targetVariant, apiLibrary.properties.Variants) { - variantName := BuildApiVariantName(m.BaseModuleName(), targetVariant, "") - ctx.AddDependency(m, nil, variantName) - } - } else if m.IsStubs() { - targetVariant := "apex." + m.StubsVersion() - if inList(targetVariant, apiLibrary.properties.Variants) { - variantName := BuildApiVariantName(m.BaseModuleName(), targetVariant, "") - ctx.AddDependency(m, nil, variantName) - } - } -} - -// 'cc_api_library' is a module type which is from the exported API surface -// with C shared library type. The module will replace original module, and -// offer a link to the module that generates shared library object from the -// map file. -type apiLibraryProperties struct { - Src *string `android:"arch_variant"` - Variants []string -} - -type apiLibraryDecorator struct { - *libraryDecorator - properties apiLibraryProperties -} - -func CcApiLibraryFactory() android.Module { - module, decorator := NewLibrary(android.DeviceSupported) - apiLibraryDecorator := &apiLibraryDecorator{ - libraryDecorator: decorator, - } - apiLibraryDecorator.BuildOnlyShared() - - module.stl = nil - module.sanitize = nil - decorator.disableStripping() - - module.compiler = nil - module.linker = apiLibraryDecorator - module.installer = nil - module.library = apiLibraryDecorator - module.AddProperties(&module.Properties, &apiLibraryDecorator.properties) - - // Prevent default system libs (libc, libm, and libdl) from being linked - if apiLibraryDecorator.baseLinker.Properties.System_shared_libs == nil { - apiLibraryDecorator.baseLinker.Properties.System_shared_libs = []string{} - } - - apiLibraryDecorator.baseLinker.Properties.No_libcrt = BoolPtr(true) - apiLibraryDecorator.baseLinker.Properties.Nocrt = BoolPtr(true) - - module.Init() - - return module -} - -func (d *apiLibraryDecorator) Name(basename string) string { - return basename + multitree.GetApiImportSuffix() -} - -// Export include dirs without checking for existence. -// The directories are not guaranteed to exist during Soong analysis. -func (d *apiLibraryDecorator) exportIncludes(ctx ModuleContext) { - exporterProps := d.flagExporter.Properties - for _, dir := range exporterProps.Export_include_dirs.GetOrDefault(ctx, nil) { - d.dirs = append(d.dirs, android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), dir)) - } - // system headers - for _, dir := range exporterProps.Export_system_include_dirs { - d.systemDirs = append(d.systemDirs, android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), dir)) - } -} - -func (d *apiLibraryDecorator) linkerInit(ctx BaseModuleContext) { - d.baseLinker.linkerInit(ctx) - - if d.hasNDKStubs() { - // Set SDK version of module as current - ctx.Module().(*Module).Properties.Sdk_version = StringPtr("current") - - // Add NDK stub as NDK known libs - name := ctx.ModuleName() - - ndkKnownLibsLock.Lock() - ndkKnownLibs := getNDKKnownLibs(ctx.Config()) - if !inList(name, *ndkKnownLibs) { - *ndkKnownLibs = append(*ndkKnownLibs, name) - } - ndkKnownLibsLock.Unlock() - } -} - -func (d *apiLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objects Objects) android.Path { - m, _ := ctx.Module().(*Module) - - var in android.Path - - // src might not exist during the beginning of soong analysis in Multi-tree - if src := String(d.properties.Src); src != "" { - in = android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), src) - } - - libName := m.BaseModuleName() + multitree.GetApiImportSuffix() - - load_cc_variant := func(apiVariantModule string) { - var mod android.Module - - ctx.VisitDirectDeps(func(depMod android.Module) { - if depMod.Name() == apiVariantModule { - mod = depMod - libName = apiVariantModule - } - }) - - if mod != nil { - variantMod, ok := mod.(*CcApiVariant) - if ok { - in = variantMod.Src() - - // Copy LLDNK properties to cc_api_library module - exportIncludeDirs := append(d.libraryDecorator.flagExporter.Properties.Export_include_dirs.GetOrDefault(ctx, nil), - variantMod.exportProperties.Export_include_dirs...) - d.libraryDecorator.flagExporter.Properties.Export_include_dirs = proptools.NewConfigurable[[]string]( - nil, - []proptools.ConfigurableCase[[]string]{ - proptools.NewConfigurableCase[[]string](nil, &exportIncludeDirs), - }, - ) - - // Export headers as system include dirs if specified. Mostly for libc - if Bool(variantMod.exportProperties.Export_headers_as_system) { - d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs = append( - d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs, - d.libraryDecorator.flagExporter.Properties.Export_include_dirs.GetOrDefault(ctx, nil)...) - d.libraryDecorator.flagExporter.Properties.Export_include_dirs = proptools.NewConfigurable[[]string](nil, nil) - } - } - } - } - - if m.InVendorOrProduct() && d.hasLLNDKStubs() { - // LLNDK variant - load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "llndk", "")) - } else if m.IsSdkVariant() { - // NDK Variant - load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "ndk", m.StubsVersion())) - } else if m.IsStubs() { - // APEX Variant - load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "apex", m.StubsVersion())) - } - - // Flags reexported from dependencies. (e.g. vndk_prebuilt_shared) - d.exportIncludes(ctx) - d.libraryDecorator.reexportDirs(deps.ReexportedDirs...) - d.libraryDecorator.reexportSystemDirs(deps.ReexportedSystemDirs...) - d.libraryDecorator.reexportFlags(deps.ReexportedFlags...) - d.libraryDecorator.reexportDeps(deps.ReexportedDeps...) - d.libraryDecorator.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...) - - if in == nil { - ctx.PropertyErrorf("src", "Unable to locate source property") - return nil - } - - // Make the _compilation_ of rdeps have an order-only dep on cc_api_library.src (an .so file) - // The .so file itself has an order-only dependency on the headers contributed by this library. - // Creating this dependency ensures that the headers are assembled before compilation of rdeps begins. - d.libraryDecorator.reexportDeps(in) - d.libraryDecorator.flagExporter.setProvider(ctx) - - d.unstrippedOutputFile = in - libName += flags.Toolchain.ShlibSuffix() - - tocFile := android.PathForModuleOut(ctx, libName+".toc") - d.tocFile = android.OptionalPathForPath(tocFile) - TransformSharedObjectToToc(ctx, in, tocFile) - - outputFile := android.PathForModuleOut(ctx, libName) - - // TODO(b/270485584) This copies with a new name, just to avoid conflict with prebuilts. - // We can just use original input if there is any way to avoid name conflict without copy. - ctx.Build(pctx, android.BuildParams{ - Rule: android.Cp, - Description: "API surface imported library", - Input: in, - Output: outputFile, - Args: map[string]string{ - "cpFlags": "-L", - }, - }) - - android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{ - SharedLibrary: outputFile, - Target: ctx.Target(), - - TableOfContents: d.tocFile, - }) - - d.shareStubs(ctx) - - return outputFile -} - -// Share additional information about stub libraries with provider -func (d *apiLibraryDecorator) shareStubs(ctx ModuleContext) { - stubs := ctx.GetDirectDepsWithTag(stubImplDepTag) - if len(stubs) > 0 { - var stubsInfo []SharedStubLibrary - for _, stub := range stubs { - stubInfo, _ := android.OtherModuleProvider(ctx, stub, SharedLibraryInfoProvider) - flagInfo, _ := android.OtherModuleProvider(ctx, stub, FlagExporterInfoProvider) - stubsInfo = append(stubsInfo, SharedStubLibrary{ - Version: moduleLibraryInterface(stub).stubsVersion(), - SharedLibraryInfo: stubInfo, - FlagExporterInfo: flagInfo, - }) - } - android.SetProvider(ctx, SharedLibraryStubsProvider, SharedLibraryStubsInfo{ - SharedStubLibraries: stubsInfo, - - IsLLNDK: ctx.IsLlndk(), - }) - } -} - -func (d *apiLibraryDecorator) availableFor(what string) bool { - // Stub from API surface should be available for any APEX. - return true -} - -func (d *apiLibraryDecorator) hasApexStubs() bool { - for _, variant := range d.properties.Variants { - if strings.HasPrefix(variant, "apex") { - return true - } - } - return false -} - -func (d *apiLibraryDecorator) hasStubsVariants() bool { - return d.hasApexStubs() -} - -func (d *apiLibraryDecorator) stubsVersions(ctx android.BaseModuleContext) []string { - m, ok := ctx.Module().(*Module) - - if !ok { - return nil - } - - // TODO(b/244244438) Create more version information for NDK and APEX variations - // NDK variants - if m.IsSdkVariant() { - // TODO(b/249193999) Do not check if module has NDK stubs once all NDK cc_api_library contains ndk variant of cc_api_variant. - if d.hasNDKStubs() { - return d.getNdkVersions() - } - } - - if d.hasLLNDKStubs() && m.InVendorOrProduct() { - // LLNDK libraries only need a single stubs variant. - return []string{android.FutureApiLevel.String()} - } - - stubsVersions := d.getStubVersions() - - if len(stubsVersions) != 0 { - return stubsVersions - } - - if m.MinSdkVersion() == "" { - return nil - } - - firstVersion, err := nativeApiLevelFromUser(ctx, - m.MinSdkVersion()) - - if err != nil { - return nil - } - - return ndkLibraryVersions(ctx, firstVersion) -} - -func (d *apiLibraryDecorator) hasLLNDKStubs() bool { - return inList("llndk", d.properties.Variants) -} - -func (d *apiLibraryDecorator) hasNDKStubs() bool { - for _, variant := range d.properties.Variants { - if ndkVariantRegex.MatchString(variant) { - return true - } - } - return false -} - -func (d *apiLibraryDecorator) getNdkVersions() []string { - ndkVersions := []string{} - - for _, variant := range d.properties.Variants { - if match := ndkVariantRegex.FindStringSubmatch(variant); len(match) == 2 { - ndkVersions = append(ndkVersions, match[1]) - } - } - - return ndkVersions -} - -func (d *apiLibraryDecorator) getStubVersions() []string { - stubVersions := []string{} - - for _, variant := range d.properties.Variants { - if match := stubVariantRegex.FindStringSubmatch(variant); len(match) == 2 { - stubVersions = append(stubVersions, match[1]) - } - } - - return stubVersions -} - -// 'cc_api_headers' is similar with 'cc_api_library', but which replaces -// header libraries. The module will replace any dependencies to existing -// original header libraries. -type apiHeadersDecorator struct { - *libraryDecorator -} - -func CcApiHeadersFactory() android.Module { - module, decorator := NewLibrary(android.DeviceSupported) - apiHeadersDecorator := &apiHeadersDecorator{ - libraryDecorator: decorator, - } - apiHeadersDecorator.HeaderOnly() - - module.stl = nil - module.sanitize = nil - decorator.disableStripping() - - module.compiler = nil - module.linker = apiHeadersDecorator - module.installer = nil - - // Prevent default system libs (libc, libm, and libdl) from being linked - if apiHeadersDecorator.baseLinker.Properties.System_shared_libs == nil { - apiHeadersDecorator.baseLinker.Properties.System_shared_libs = []string{} - } - - apiHeadersDecorator.baseLinker.Properties.No_libcrt = BoolPtr(true) - apiHeadersDecorator.baseLinker.Properties.Nocrt = BoolPtr(true) - - module.Init() - - return module -} - -func (d *apiHeadersDecorator) Name(basename string) string { - return basename + multitree.GetApiImportSuffix() -} - -func (d *apiHeadersDecorator) availableFor(what string) bool { - // Stub from API surface should be available for any APEX. - return true -} - -type ccApiexportProperties struct { - Src *string `android:"arch_variant"` - Variant *string - Version *string -} - -type variantExporterProperties struct { - // Header directory to export - Export_include_dirs []string `android:"arch_variant"` - - // Export all headers as system include - Export_headers_as_system *bool -} - -type CcApiVariant struct { - android.ModuleBase - - properties ccApiexportProperties - exportProperties variantExporterProperties - - src android.Path -} - -var _ android.Module = (*CcApiVariant)(nil) -var _ android.ImageInterface = (*CcApiVariant)(nil) - -func CcApiVariantFactory() android.Module { - module := &CcApiVariant{} - - module.AddProperties(&module.properties) - module.AddProperties(&module.exportProperties) - - android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth) - return module -} - -func (v *CcApiVariant) GenerateAndroidBuildActions(ctx android.ModuleContext) { - // No need to build - - if String(v.properties.Src) == "" { - ctx.PropertyErrorf("src", "src is a required property") - } - - // Skip the existence check of the stub prebuilt file. - // The file is not guaranteed to exist during Soong analysis. - // Build orchestrator will be responsible for creating a connected ninja graph. - v.src = android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), String(v.properties.Src)) -} - -func (v *CcApiVariant) Name() string { - version := String(v.properties.Version) - return BuildApiVariantName(v.BaseModuleName(), *v.properties.Variant, version) -} - -func (v *CcApiVariant) Src() android.Path { - return v.src -} - -func BuildApiVariantName(baseName string, variant string, version string) string { - names := []string{baseName, variant} - if version != "" { - names = append(names, version) - } - - return strings.Join(names[:], ".") + multitree.GetApiImportSuffix() -} - -// Implement ImageInterface to generate image variants -func (v *CcApiVariant) ImageMutatorBegin(ctx android.BaseModuleContext) {} -func (v *CcApiVariant) VendorVariantNeeded(ctx android.BaseModuleContext) bool { - return String(v.properties.Variant) == "llndk" -} -func (v *CcApiVariant) ProductVariantNeeded(ctx android.BaseModuleContext) bool { - return String(v.properties.Variant) == "llndk" -} -func (v *CcApiVariant) CoreVariantNeeded(ctx android.BaseModuleContext) bool { - return inList(String(v.properties.Variant), []string{"ndk", "apex"}) -} -func (v *CcApiVariant) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false } -func (v *CcApiVariant) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false } -func (v *CcApiVariant) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false } -func (v *CcApiVariant) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool { return false } -func (v *CcApiVariant) ExtraImageVariations(ctx android.BaseModuleContext) []string { return nil } -func (v *CcApiVariant) SetImageVariation(ctx android.BaseModuleContext, variation string) { -} diff --git a/cc/linker.go b/cc/linker.go index 00568177f..1efacade8 100644 --- a/cc/linker.go +++ b/cc/linker.go @@ -645,7 +645,7 @@ func (linker *baseLinker) link(ctx ModuleContext, panic(fmt.Errorf("baseLinker doesn't know how to link")) } -func (linker *baseLinker) linkerSpecifiedDeps(ctx android.ConfigAndErrorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps { +func (linker *baseLinker) linkerSpecifiedDeps(ctx android.ConfigurableEvaluatorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps { eval := module.ConfigurableEvaluator(ctx) specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, linker.Properties.Shared_libs.GetOrDefault(eval, nil)...) diff --git a/cc/ndk_abi.go b/cc/ndk_abi.go index 8202cc05a..2706261a8 100644 --- a/cc/ndk_abi.go +++ b/cc/ndk_abi.go @@ -46,7 +46,7 @@ func (n *ndkAbiDumpSingleton) GenerateBuildActions(ctx android.SingletonContext) if m, ok := module.(*Module); ok { if installer, ok := m.installer.(*stubDecorator); ok { - if canDumpAbi(ctx.Config(), ctx.ModuleDir(module)) { + if installer.hasAbiDump { depPaths = append(depPaths, installer.abiDumpPath) } } diff --git a/cc/ndk_library.go b/cc/ndk_library.go index bd6dfa301..01551ab49 100644 --- a/cc/ndk_library.go +++ b/cc/ndk_library.go @@ -125,6 +125,7 @@ type stubDecorator struct { parsedCoverageXmlPath android.ModuleOutPath installPath android.Path abiDumpPath android.OutputPath + hasAbiDump bool abiDiffPaths android.Paths apiLevel android.ApiLevel @@ -330,11 +331,11 @@ func (this *stubDecorator) findPrebuiltAbiDump(ctx ModuleContext, } // Feature flag. -func canDumpAbi(config android.Config, moduleDir string) bool { +func (this *stubDecorator) canDumpAbi(ctx ModuleContext) bool { if runtime.GOOS == "darwin" { return false } - if strings.HasPrefix(moduleDir, "bionic/") { + if strings.HasPrefix(ctx.ModuleDir(), "bionic/") { // Bionic has enough uncommon implementation details like ifuncs and asm // code that the ABI tracking here has a ton of false positives. That's // causing pretty extreme friction for development there, so disabling @@ -343,8 +344,14 @@ func canDumpAbi(config android.Config, moduleDir string) bool { // http://b/358653811 return false } + + if this.apiLevel.IsCurrent() { + // "current" (AKA 10000) is not tracked. + return false + } + // http://b/156513478 - return config.ReleaseNdkAbiMonitored() + return ctx.Config().ReleaseNdkAbiMonitored() } // Feature flag to disable diffing against prebuilts. @@ -357,6 +364,7 @@ func (this *stubDecorator) dumpAbi(ctx ModuleContext, symbolList android.Path) { this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx, this.apiLevel.String(), ctx.Arch().ArchType.String(), this.libraryName(ctx), "abi.stg") + this.hasAbiDump = true headersList := getNdkABIHeadersFile(ctx) ctx.Build(pctx, android.BuildParams{ Rule: stg, @@ -421,41 +429,45 @@ func (this *stubDecorator) diffAbi(ctx ModuleContext) { // Also ensure that the ABI of the next API level (if there is one) matches // this API level. *New* ABI is allowed, but any changes to APIs that exist // in this API level are disallowed. - if !this.apiLevel.IsCurrent() && prebuiltAbiDump.Valid() { + if prebuiltAbiDump.Valid() { nextApiLevel := findNextApiLevel(ctx, this.apiLevel) if nextApiLevel == nil { panic(fmt.Errorf("could not determine which API level follows "+ "non-current API level %s", this.apiLevel)) } - nextAbiDiffPath := android.PathForModuleOut(ctx, - "abidiff_next.timestamp") - nextAbiDump := this.findPrebuiltAbiDump(ctx, *nextApiLevel) - missingNextPrebuiltError := fmt.Sprintf( - missingPrebuiltErrorTemplate, this.libraryName(ctx), - nextAbiDump.InvalidReason()) - if !nextAbiDump.Valid() { - ctx.Build(pctx, android.BuildParams{ - Rule: android.ErrorRule, - Output: nextAbiDiffPath, - Args: map[string]string{ - "error": missingNextPrebuiltError, - }, - }) - } else { - ctx.Build(pctx, android.BuildParams{ - Rule: stgdiff, - Description: fmt.Sprintf( - "Comparing ABI to the next API level %s %s", - prebuiltAbiDump, nextAbiDump), - Output: nextAbiDiffPath, - Inputs: android.Paths{ - prebuiltAbiDump.Path(), nextAbiDump.Path()}, - Args: map[string]string{ - "args": "--format=small --ignore=interface_addition", - }, - }) + + // "current" ABI is not tracked. + if !nextApiLevel.IsCurrent() { + nextAbiDiffPath := android.PathForModuleOut(ctx, + "abidiff_next.timestamp") + nextAbiDump := this.findPrebuiltAbiDump(ctx, *nextApiLevel) + missingNextPrebuiltError := fmt.Sprintf( + missingPrebuiltErrorTemplate, this.libraryName(ctx), + nextAbiDump.InvalidReason()) + if !nextAbiDump.Valid() { + ctx.Build(pctx, android.BuildParams{ + Rule: android.ErrorRule, + Output: nextAbiDiffPath, + Args: map[string]string{ + "error": missingNextPrebuiltError, + }, + }) + } else { + ctx.Build(pctx, android.BuildParams{ + Rule: stgdiff, + Description: fmt.Sprintf( + "Comparing ABI to the next API level %s %s", + prebuiltAbiDump, nextAbiDump), + Output: nextAbiDiffPath, + Inputs: android.Paths{ + prebuiltAbiDump.Path(), nextAbiDump.Path()}, + Args: map[string]string{ + "args": "--format=small --ignore=interface_addition", + }, + }) + } + this.abiDiffPaths = append(this.abiDiffPaths, nextAbiDiffPath) } - this.abiDiffPaths = append(this.abiDiffPaths, nextAbiDiffPath) } } @@ -478,7 +490,7 @@ func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) O nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile, c.apiLevel, "") objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc) c.versionScriptPath = nativeAbiResult.versionScript - if canDumpAbi(ctx.Config(), ctx.ModuleDir()) { + if c.canDumpAbi(ctx) { c.dumpAbi(ctx, nativeAbiResult.symbolList) if canDiffAbi(ctx.Config()) { c.diffAbi(ctx) diff --git a/cc/object.go b/cc/object.go index a4f4c8408..c89520ab7 100644 --- a/cc/object.go +++ b/cc/object.go @@ -203,7 +203,7 @@ func (object *objectLinker) link(ctx ModuleContext, return outputFile } -func (object *objectLinker) linkerSpecifiedDeps(ctx android.ConfigAndErrorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps { +func (object *objectLinker) linkerSpecifiedDeps(ctx android.ConfigurableEvaluatorContext, module *Module, specifiedDeps specifiedDeps) specifiedDeps { eval := module.ConfigurableEvaluator(ctx) specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, object.Properties.Shared_libs.GetOrDefault(eval, nil)...) diff --git a/cc/sanitize.go b/cc/sanitize.go index 7b0652c38..a8722a023 100644 --- a/cc/sanitize.go +++ b/cc/sanitize.go @@ -79,7 +79,7 @@ var ( minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined", "-fno-sanitize-recover=integer,undefined"} - memtagStackCommonFlags = []string{"-march=armv8-a+memtag"} + memtagStackCommonFlags = []string{"-Xclang -target-feature -Xclang +mte"} memtagStackLlvmFlags = []string{"-dom-tree-reachability-max-bbs-to-explore=128"} hostOnlySanitizeFlags = []string{"-fno-sanitize-recover=all"} @@ -176,7 +176,7 @@ func (t SanitizerType) registerMutators(ctx android.RegisterMutatorsContext) { switch t { case cfi, Hwasan, Asan, tsan, Fuzzer, scs, Memtag_stack: sanitizer := &sanitizerSplitMutator{t} - ctx.TopDown(t.variationName()+"_markapexes", sanitizer.markSanitizableApexesMutator) + ctx.BottomUp(t.variationName()+"_markapexes", sanitizer.markSanitizableApexesMutator) ctx.Transition(t.variationName(), sanitizer) case Memtag_heap, Memtag_globals, intOverflow: // do nothing @@ -1153,7 +1153,7 @@ type sanitizerSplitMutator struct { // If an APEX is sanitized or not depends on whether it contains at least one // sanitized module. Transition mutators cannot propagate information up the // dependency graph this way, so we need an auxiliary mutator to do so. -func (s *sanitizerSplitMutator) markSanitizableApexesMutator(ctx android.TopDownMutatorContext) { +func (s *sanitizerSplitMutator) markSanitizableApexesMutator(ctx android.BottomUpMutatorContext) { if sanitizeable, ok := ctx.Module().(Sanitizeable); ok { enabled := sanitizeable.IsSanitizerEnabled(ctx.Config(), s.sanitizer.name()) ctx.VisitDirectDeps(func(dep android.Module) { @@ -1355,7 +1355,7 @@ func (c *Module) IsSanitizerExplicitlyDisabled(t SanitizerType) bool { } // Propagate the ubsan minimal runtime dependency when there are integer overflow sanitized static dependencies. -func sanitizerRuntimeDepsMutator(mctx android.TopDownMutatorContext) { +func sanitizerRuntimeDepsMutator(mctx android.BottomUpMutatorContext) { // Change this to PlatformSanitizable when/if non-cc modules support ubsan sanitizers. if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil { if c.sanitize.Properties.ForceDisable { @@ -1437,11 +1437,11 @@ func sanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) { //"null", //"shift-base", //"signed-integer-overflow", - // TODO(danalbert): Fix UB in libc++'s __tree so we can turn this on. - // https://llvm.org/PR19302 - // http://reviews.llvm.org/D6974 - // "object-size", ) + + if mctx.Config().ReleaseBuildObjectSizeSanitizer() { + sanitizers = append(sanitizers, "object-size") + } } sanitizers = append(sanitizers, sanProps.Misc_undefined...) } @@ -51,13 +51,6 @@ func (sdkTransitionMutator) Split(ctx android.BaseModuleContext) []string { return []string{""} } } - case *CcApiVariant: - ccApiVariant, _ := ctx.Module().(*CcApiVariant) - if String(ccApiVariant.properties.Variant) == "ndk" { - return []string{"sdk"} - } else { - return []string{""} - } } return []string{""} @@ -84,11 +77,6 @@ func (sdkTransitionMutator) IncomingTransition(ctx android.IncomingTransitionCon return incomingVariation } } - case *CcApiVariant: - ccApiVariant, _ := ctx.Module().(*CcApiVariant) - if String(ccApiVariant.properties.Variant) == "ndk" { - return "sdk" - } } if ctx.IsAddingDependency() { diff --git a/cc/testing.go b/cc/testing.go index 159f86c60..14a6b7a6a 100644 --- a/cc/testing.go +++ b/cc/testing.go @@ -20,7 +20,6 @@ import ( "android/soong/android" "android/soong/genrule" - "android/soong/multitree" ) func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) { @@ -29,9 +28,6 @@ func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) { RegisterBinaryBuildComponents(ctx) RegisterLibraryBuildComponents(ctx) RegisterLibraryHeadersBuildComponents(ctx) - RegisterLibraryStubBuildComponents(ctx) - - multitree.RegisterApiImportsModule(ctx) ctx.RegisterModuleType("prebuilt_build_tool", android.NewPrebuiltBuildTool) ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory) diff --git a/cmd/extract_apks/bundle_proto/Android.bp b/cmd/extract_apks/bundle_proto/Android.bp index e56c0fb32..0abf1e251 100644 --- a/cmd/extract_apks/bundle_proto/Android.bp +++ b/cmd/extract_apks/bundle_proto/Android.bp @@ -10,4 +10,8 @@ python_library_host { proto: { canonical_path_from_root: false, }, + visibility: [ + "//build/soong:__subpackages__", + "//tools/mainline:__subpackages__", + ], } diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go index 24a44b4da..5b1ae5406 100644 --- a/cmd/soong_build/main.go +++ b/cmd/soong_build/main.go @@ -212,7 +212,14 @@ func writeDepFile(outputFile string, eventHandler *metrics.EventHandler, ninjaDe } // Check if there are changes to the environment file, product variable file and -// soong_build binary, in which case no incremental will be performed. +// soong_build binary, in which case no incremental will be performed. For env +// variables we check the used env file, which will be removed in soong ui if +// there is any changes to the env variables used last time, in which case the +// check below will fail and a full build will be attempted. If any new env +// variables are added in the new run, soong ui won't be able to detect it, the +// used env file check below will pass. But unless there is a soong build code +// change, in which case the soong build binary check will fail, otherwise the +// new env variables shouldn't have any affect. func incrementalValid(config android.Config, configCacheFile string) (*ConfigCache, bool) { var newConfigCache ConfigCache data, err := os.ReadFile(shared.JoinPath(topDir, usedEnvFile)) @@ -368,6 +375,7 @@ func main() { ctx.Register() finalOutputFile, ninjaDeps := runSoongOnlyBuild(ctx) + ninjaDeps = append(ninjaDeps, configuration.ProductVariablesFileName) ninjaDeps = append(ninjaDeps, usedEnvFile) if shared.IsDebugging() { // Add a non-existent file to the dependencies so that soong_build will rerun when the debugger is diff --git a/cmd/symbols_map/Android.bp b/cmd/symbols_map/Android.bp index e3ae6ede5..272e8061a 100644 --- a/cmd/symbols_map/Android.bp +++ b/cmd/symbols_map/Android.bp @@ -30,4 +30,5 @@ bootstrap_go_package { srcs: [ "symbols_map_proto/symbols_map.pb.go", ], + visibility: ["//visibility:public"], } diff --git a/cmd/zip2zip/Android.bp b/cmd/zip2zip/Android.bp index 3ef766865..7f9b16582 100644 --- a/cmd/zip2zip/Android.bp +++ b/cmd/zip2zip/Android.bp @@ -27,4 +27,6 @@ blueprint_go_binary { "zip2zip.go", ], testSrcs: ["zip2zip_test.go"], + // Used by genrules + visibility: ["//visibility:public"], } diff --git a/compliance/Android.bp b/compliance/Android.bp new file mode 100644 index 000000000..80f56857f --- /dev/null +++ b/compliance/Android.bp @@ -0,0 +1,40 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +bootstrap_go_package { + name: "soong-compliance", + pkgPath: "android/soong/compliance", + deps: [ + "soong-android", + ], + srcs: [ + "notice.go", + ], + testSrcs: [ + ], + pluginFor: ["soong_build"], +} + +notice_xml { + name: "notice_xml_system", + partition_name: "system", + visibility: [ + "//build/make/target/product/generic", + "//device/google/cuttlefish/system_image", + ], +} diff --git a/compliance/license_metadata_proto/Android.bp b/compliance/license_metadata_proto/Android.bp index 3c041e48b..47612854f 100644 --- a/compliance/license_metadata_proto/Android.bp +++ b/compliance/license_metadata_proto/Android.bp @@ -24,4 +24,8 @@ bootstrap_go_package { "golang-protobuf-reflect-protoreflect", "golang-protobuf-runtime-protoimpl", ], + visibility: [ + "//build/make/tools/compliance:__subpackages__", + "//build/soong:__subpackages__", + ], } diff --git a/compliance/notice.go b/compliance/notice.go new file mode 100644 index 000000000..4fc83ab70 --- /dev/null +++ b/compliance/notice.go @@ -0,0 +1,100 @@ +// Copyright 2024 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package compliance + +import ( + "path/filepath" + + "android/soong/android" + "github.com/google/blueprint" +) + +func init() { + RegisterNoticeXmlBuildComponents(android.InitRegistrationContext) +} + +var PrepareForTestWithNoticeXmlBuildComponents = android.GroupFixturePreparers( + android.FixtureRegisterWithContext(RegisterNoticeXmlBuildComponents), +) + +var PrepareForTestWithNoticeXml = android.GroupFixturePreparers( + PrepareForTestWithNoticeXmlBuildComponents, +) + +func RegisterNoticeXmlBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("notice_xml", NoticeXmlFactory) +} + +var ( + pctx = android.NewPackageContext("android/soong/compliance") + + genNoticeXml = pctx.HostBinToolVariable("genNoticeXml", "gen_notice_xml") + + // Command to generate NOTICE.xml.gz for a partition + genNoticeXmlRule = pctx.AndroidStaticRule("genNoticeXmlRule", blueprint.RuleParams{ + Command: "rm -rf $out && " + + "${genNoticeXml} --output_file ${out} --metadata ${in} --partition ${partition} --product_out ${productOut} --soong_out ${soongOut}", + CommandDeps: []string{"${genNoticeXml}"}, + }, "partition", "productOut", "soongOut") +) + +func NoticeXmlFactory() android.Module { + m := &NoticeXmlModule{} + m.AddProperties(&m.props) + android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibFirst) + return m +} + +type NoticeXmlModule struct { + android.ModuleBase + + props noticeXmlProperties + + outputFile android.OutputPath + installPath android.InstallPath +} + +type noticeXmlProperties struct { + Partition_name string +} + +func (nx *NoticeXmlModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + output := android.PathForModuleOut(ctx, "NOTICE.xml.gz") + metadataDb := android.PathForOutput(ctx, "compliance-metadata", ctx.Config().DeviceProduct(), "compliance-metadata.db") + ctx.Build(pctx, android.BuildParams{ + Rule: genNoticeXmlRule, + Input: metadataDb, + Output: output, + Args: map[string]string{ + "productOut": filepath.Join(ctx.Config().OutDir(), "target", "product", ctx.Config().DeviceName()), + "soongOut": ctx.Config().SoongOutDir(), + "partition": nx.props.Partition_name, + }, + }) + + nx.outputFile = output.OutputPath + + if android.Bool(ctx.Config().ProductVariables().UseSoongSystemImage) { + nx.installPath = android.PathForModuleInPartitionInstall(ctx, nx.props.Partition_name, "etc") + ctx.InstallFile(nx.installPath, "NOTICE.xml.gz", nx.outputFile) + } +} + +func (nx *NoticeXmlModule) AndroidMkEntries() []android.AndroidMkEntries { + return []android.AndroidMkEntries{{ + Class: "ETC", + OutputFile: android.OptionalPathForPath(nx.outputFile), + }} +} diff --git a/compliance/notice_test.go b/compliance/notice_test.go new file mode 100644 index 000000000..6187e5332 --- /dev/null +++ b/compliance/notice_test.go @@ -0,0 +1,38 @@ +// Copyright 2024 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package compliance + +import ( + "testing" + + "android/soong/android" +) + +var prepareForNoticeXmlTest = android.GroupFixturePreparers( + android.PrepareForTestWithArchMutator, + PrepareForTestWithNoticeXml, +) + +func TestPrebuiltEtcOutputFile(t *testing.T) { + result := prepareForNoticeXmlTest.RunTestWithBp(t, ` + notice_xml { + name: "notice_xml_system", + partition_name: "system", + } + `) + + m := result.Module("notice_xml_system", "android_arm64_armv8-a").(*NoticeXmlModule) + android.AssertStringEquals(t, "output file", "NOTICE.xml.gz", m.outputFile.Base()) +}
\ No newline at end of file diff --git a/compliance/project_metadata_proto/Android.bp b/compliance/project_metadata_proto/Android.bp index 56e76e79f..0c807b22f 100644 --- a/compliance/project_metadata_proto/Android.bp +++ b/compliance/project_metadata_proto/Android.bp @@ -24,4 +24,5 @@ bootstrap_go_package { "golang-protobuf-reflect-protoreflect", "golang-protobuf-runtime-protoimpl", ], + visibility: ["//build/make/tools/compliance:__subpackages__"], } diff --git a/docs/OWNERS b/docs/OWNERS new file mode 100644 index 000000000..776beca32 --- /dev/null +++ b/docs/OWNERS @@ -0,0 +1 @@ +per-file map_files.md = danalbert@google.com diff --git a/docs/map_files.md b/docs/map_files.md index e1ddefc27..8d6af879f 100644 --- a/docs/map_files.md +++ b/docs/map_files.md @@ -88,12 +88,17 @@ but is useful when developing APIs for an unknown future release. ### introduced -Indicates the version in which an API was first introduced. For example, -`introduced=21` specifies that the API was first added (or first made public) in -API level 21. This tag can be applied to either a version definition or an -individual symbol. If applied to a version, all symbols contained in the version -will have the tag applied. An `introduced` tag on a symbol overrides the value -set for the version, if both are defined. +Indicates the version in which an API was first introduced in the NDK. For +example, `introduced=21` specifies that the API was first added (or first made +public) in API level 21. This tag can be applied to either a version definition +or an individual symbol. If applied to a version, all symbols contained in the +version will have the tag applied. An `introduced` tag on a symbol overrides the +value set for the version, if both are defined. + +The `introduced` tag should only be used with NDK APIs. Other API surface tags +(such as `apex`) will override `introduced`. APIs that are in the NDK should +never use tags like `apex`, and APIs that are not in the NDK should never use +`introduced`. Note: The map file alone does not contain all the information needed to determine which API level an API was added in. The `first_version` property of diff --git a/docs/tidy.md b/docs/tidy.md index ae0ca9360..2e4c9579d 100644 --- a/docs/tidy.md +++ b/docs/tidy.md @@ -38,7 +38,7 @@ For example, in clang-tidy is enabled explicitly and with a different check list: ``` cc_defaults { - name: "bpf_defaults", + name: "bpf_cc_defaults", // snipped tidy: true, tidy_checks: [ @@ -52,7 +52,7 @@ cc_defaults { } ``` That means in normal builds, even without `WITH_TIDY=1`, -the modules that use `bpf_defaults` _should_ run clang-tidy +the modules that use `bpf_cc_defaults` _should_ run clang-tidy over C/C++ source files with the given `tidy_checks`. However since clang-tidy warnings and its runtime cost might diff --git a/etc/Android.bp b/etc/Android.bp index f02c12a2a..580c54f9b 100644 --- a/etc/Android.bp +++ b/etc/Android.bp @@ -20,4 +20,6 @@ bootstrap_go_package { "install_symlink_test.go", ], pluginFor: ["soong_build"], + // Used by plugins + visibility: ["//visibility:public"], } diff --git a/filesystem/aconfig_files.go b/filesystem/aconfig_files.go index 5c047bc83..8af2ffaab 100644 --- a/filesystem/aconfig_files.go +++ b/filesystem/aconfig_files.go @@ -79,6 +79,7 @@ func (f *filesystem) buildAconfigFlagsFiles(ctx android.ModuleContext, builder * generatePartitionAconfigStorageFile("package_map", "package.map") generatePartitionAconfigStorageFile("flag_map", "flag.map") generatePartitionAconfigStorageFile("flag_val", "flag.val") + generatePartitionAconfigStorageFile("flag_info", "flag.info") android.WriteExecutableFileRuleVerbatim(ctx, aconfigFlagsBuilderPath, sb.String()) } diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go index a8f97e3b7..a26fac7fc 100644 --- a/filesystem/filesystem.go +++ b/filesystem/filesystem.go @@ -37,7 +37,7 @@ func init() { func registerBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("android_filesystem", filesystemFactory) ctx.RegisterModuleType("android_filesystem_defaults", filesystemDefaultsFactory) - ctx.RegisterModuleType("android_system_image", systemImageFactory) + ctx.RegisterModuleType("android_system_image", SystemImageFactory) ctx.RegisterModuleType("avb_add_hash_footer", avbAddHashFooterFactory) ctx.RegisterModuleType("avb_add_hash_footer_defaults", avbAddHashFooterDefaultsFactory) ctx.RegisterModuleType("avb_gen_vbmeta_image", avbGenVbmetaImageFactory) @@ -49,7 +49,7 @@ type filesystem struct { android.PackagingBase android.DefaultableModuleBase - properties filesystemProperties + properties FilesystemProperties // Function that builds extra files under the root directory and returns the files buildExtraFiles func(ctx android.ModuleContext, root android.OutputPath) android.OutputPaths @@ -71,7 +71,7 @@ type symlinkDefinition struct { Name *string } -type filesystemProperties struct { +type FilesystemProperties struct { // When set to true, sign the image with avbtool. Default is false. Use_avb *bool @@ -136,9 +136,6 @@ type filesystemProperties struct { // Install aconfig_flags.pb file for the modules installed in this partition. Gen_aconfig_flags_pb *bool - // Update the Base_dir of the $PRODUCT_OUT directory with the packaging files. - Update_product_out *bool - Fsverity fsverityProperties } @@ -150,14 +147,14 @@ type filesystemProperties struct { func filesystemFactory() android.Module { module := &filesystem{} module.filterPackagingSpec = module.filterInstallablePackagingSpec - initFilesystemModule(module) + initFilesystemModule(module, module) return module } -func initFilesystemModule(module *filesystem) { - module.AddProperties(&module.properties) - android.InitPackageModule(module) - module.PackagingBase.DepsCollectFirstTargetOnly = true +func initFilesystemModule(module android.DefaultableModule, filesystemModule *filesystem) { + module.AddProperties(&filesystemModule.properties) + android.InitPackageModule(filesystemModule) + filesystemModule.PackagingBase.DepsCollectFirstTargetOnly = true android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) android.InitDefaultableModule(module) } @@ -335,7 +332,7 @@ func (f *filesystem) copyPackagingSpecs(ctx android.ModuleContext, builder *andr } func (f *filesystem) copyFilesToProductOut(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) { - if !proptools.Bool(f.properties.Update_product_out) { + if f.Name() != ctx.Config().SoongDefinedSystemImage() { return } installPath := android.PathForModuleInPartitionInstall(ctx, f.partitionName()) diff --git a/filesystem/system_image.go b/filesystem/system_image.go index a8fd36822..57239ae3f 100644 --- a/filesystem/system_image.go +++ b/filesystem/system_image.go @@ -27,21 +27,25 @@ type systemImage struct { type systemImageProperties struct { // Path to the input linker config json file. - Linker_config_src *string + Linker_config_src *string `android:"path"` } // android_system_image is a specialization of android_filesystem for the 'system' partition. // Currently, the only difference is the inclusion of linker.config.pb file which specifies // the provided and the required libraries to and from APEXes. -func systemImageFactory() android.Module { +func SystemImageFactory() android.Module { module := &systemImage{} module.AddProperties(&module.properties) module.filesystem.buildExtraFiles = module.buildExtraFiles module.filesystem.filterPackagingSpec = module.filterPackagingSpec - initFilesystemModule(&module.filesystem) + initFilesystemModule(module, &module.filesystem) return module } +func (s systemImage) FsProps() FilesystemProperties { + return s.filesystem.properties +} + func (s *systemImage) buildExtraFiles(ctx android.ModuleContext, root android.OutputPath) android.OutputPaths { if s.filesystem.properties.Partition_type != nil { ctx.PropertyErrorf("partition_type", "partition_type must be unset on an android_system_image module. It is assumed to be 'system'.") diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go index 3a9a64daa..1d647965a 100644 --- a/filesystem/vbmeta.go +++ b/filesystem/vbmeta.go @@ -213,6 +213,7 @@ func (v *vbmeta) GenerateAndroidBuildActions(ctx android.ModuleContext) { ctx.InstallFile(v.installDir, v.installFileName(), v.output) ctx.SetOutputFiles([]android.Path{v.output}, "") + android.SetProvider(ctx, android.AndroidMkInfoProvider, v.prepareAndroidMKProviderInfo()) } // Returns the embedded shell command that prints the rollback index @@ -265,20 +266,17 @@ func (v *vbmeta) extractPublicKeys(ctx android.ModuleContext) map[string]android return result } -var _ android.AndroidMkEntriesProvider = (*vbmeta)(nil) - -// Implements android.AndroidMkEntriesProvider -func (v *vbmeta) AndroidMkEntries() []android.AndroidMkEntries { - return []android.AndroidMkEntries{android.AndroidMkEntries{ - Class: "ETC", - OutputFile: android.OptionalPathForPath(v.output), - ExtraEntries: []android.AndroidMkExtraEntriesFunc{ - func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - entries.SetString("LOCAL_MODULE_PATH", v.installDir.String()) - entries.SetString("LOCAL_INSTALLED_MODULE_STEM", v.installFileName()) - }, +func (v *vbmeta) prepareAndroidMKProviderInfo() *android.AndroidMkProviderInfo { + providerData := android.AndroidMkProviderInfo{ + PrimaryInfo: android.AndroidMkInfo{ + Class: "ETC", + OutputFile: android.OptionalPathForPath(v.output), + EntryMap: make(map[string][]string), }, - }} + } + providerData.PrimaryInfo.SetString("LOCAL_MODULE_PATH", v.installDir.String()) + providerData.PrimaryInfo.SetString("LOCAL_INSTALLED_MODULE_STEM", v.installFileName()) + return &providerData } var _ Filesystem = (*vbmeta)(nil) diff --git a/multitree/Android.bp b/fsgen/Android.bp index 78c49627b..aa8881f8e 100644 --- a/multitree/Android.bp +++ b/fsgen/Android.bp @@ -3,18 +3,19 @@ package { } bootstrap_go_package { - name: "soong-multitree", - pkgPath: "android/soong/multitree", + name: "soong-fsgen", + pkgPath: "android/soong/fsgen", deps: [ "blueprint", + "soong", "soong-android", + "soong-filesystem", ], srcs: [ - "api_imports.go", - "api_surface.go", - "export.go", - "metadata.go", - "import.go", + "filesystem_creator.go", + ], + testSrcs: [ + "filesystem_creator_test.go", ], pluginFor: ["soong_build"], } diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go new file mode 100644 index 000000000..ca948f406 --- /dev/null +++ b/fsgen/filesystem_creator.go @@ -0,0 +1,111 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fsgen + +import ( + "android/soong/android" + "android/soong/filesystem" + "fmt" + "strconv" + + "github.com/google/blueprint/proptools" +) + +func init() { + registerBuildComponents(android.InitRegistrationContext) +} + +func registerBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("soong_filesystem_creator", filesystemCreatorFactory) +} + +type filesystemCreator struct { + android.ModuleBase +} + +func filesystemCreatorFactory() android.Module { + module := &filesystemCreator{} + + android.InitAndroidModule(module) + android.AddLoadHook(module, func(ctx android.LoadHookContext) { + module.createInternalModules(ctx) + }) + + return module +} + +func (f *filesystemCreator) createInternalModules(ctx android.LoadHookContext) { + f.createSystemImage(ctx) +} + +func (f *filesystemCreator) createSystemImage(ctx android.LoadHookContext) { + baseProps := &struct { + Name *string + }{ + Name: proptools.StringPtr(fmt.Sprintf("%s_generated_system_image", ctx.Config().DeviceProduct())), + } + + fsProps := &(filesystem.FilesystemProperties{}) + partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse + systemPartitionVars := partitionVars.PartitionQualifiedVariables["system"] + + // BOARD_AVB_ENABLE + fsProps.Use_avb = proptools.BoolPtr(partitionVars.BoardAvbEnable) + // BOARD_AVB_KEY_PATH + fsProps.Avb_private_key = proptools.StringPtr(systemPartitionVars.BoardAvbKeyPath) + // BOARD_AVB_ALGORITHM + fsProps.Avb_algorithm = proptools.StringPtr(systemPartitionVars.BoardAvbAlgorithm) + // BOARD_AVB_SYSTEM_ROLLBACK_INDEX + if rollbackIndex, err := strconv.ParseInt(systemPartitionVars.BoardAvbRollbackIndex, 10, 64); err == nil { + fsProps.Rollback_index = proptools.Int64Ptr(rollbackIndex) + } + + fsProps.Partition_name = proptools.StringPtr("system") + // BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE + fsProps.Type = proptools.StringPtr(systemPartitionVars.BoardFileSystemType) + + fsProps.Base_dir = proptools.StringPtr("system") + + fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true) + + // Identical to that of the generic_system_image + fsProps.Fsverity.Inputs = []string{ + "etc/boot-image.prof", + "etc/dirty-image-objects", + "etc/preloaded-classes", + "etc/classpaths/*.pb", + "framework/*", + "framework/*/*", // framework/{arch} + "framework/oat/*/*", // framework/oat/{arch} + } + + // system_image properties that are not set: + // - filesystemProperties.Avb_hash_algorithm + // - filesystemProperties.File_contexts + // - filesystemProperties.Dirs + // - filesystemProperties.Symlinks + // - filesystemProperties.Fake_timestamp + // - filesystemProperties.Uuid + // - filesystemProperties.Mount_point + // - filesystemProperties.Include_make_built_files + // - filesystemProperties.Build_logtags + // - filesystemProperties.Fsverity.Libs + // - systemImageProperties.Linker_config_src + ctx.CreateModule(filesystem.SystemImageFactory, baseProps, fsProps) +} + +func (f *filesystemCreator) GenerateAndroidBuildActions(ctx android.ModuleContext) { + +} diff --git a/fsgen/filesystem_creator_test.go b/fsgen/filesystem_creator_test.go new file mode 100644 index 000000000..6bb0e775b --- /dev/null +++ b/fsgen/filesystem_creator_test.go @@ -0,0 +1,87 @@ +// Copyright 2024 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fsgen + +import ( + "android/soong/android" + "android/soong/filesystem" + "testing" + + "github.com/google/blueprint/proptools" +) + +var prepareForTestWithFsgenBuildComponents = android.FixtureRegisterWithContext(registerBuildComponents) + +func TestFileSystemCreatorSystemImageProps(t *testing.T) { + result := android.GroupFixturePreparers( + android.PrepareForIntegrationTestWithAndroid, + android.PrepareForTestWithAndroidBuildComponents, + filesystem.PrepareForTestWithFilesystemBuildComponents, + prepareForTestWithFsgenBuildComponents, + android.FixtureModifyConfig(func(config android.Config) { + config.TestProductVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.BoardAvbEnable = true + config.TestProductVariables.PartitionVarsForSoongMigrationOnlyDoNotUse.PartitionQualifiedVariables = + map[string]android.PartitionQualifiedVariablesType{ + "system": { + BoardAvbKeyPath: "external/avb/test/data/testkey_rsa4096.pem", + BoardAvbAlgorithm: "SHA256_RSA4096", + BoardAvbRollbackIndex: "0", + BoardFileSystemType: "ext4", + }, + } + }), + android.FixtureMergeMockFs(android.MockFS{ + "external/avb/test/data/testkey_rsa4096.pem": nil, + }), + ).RunTestWithBp(t, ` + soong_filesystem_creator { + name: "foo", + } + `) + + fooSystem := result.ModuleForTests("test_product_generated_system_image", "android_common").Module().(interface { + FsProps() filesystem.FilesystemProperties + }) + android.AssertBoolEquals( + t, + "Property expected to match the product variable 'BOARD_AVB_ENABLE'", + true, + proptools.Bool(fooSystem.FsProps().Use_avb), + ) + android.AssertStringEquals( + t, + "Property expected to match the product variable 'BOARD_AVB_KEY_PATH'", + "external/avb/test/data/testkey_rsa4096.pem", + proptools.String(fooSystem.FsProps().Avb_private_key), + ) + android.AssertStringEquals( + t, + "Property expected to match the product variable 'BOARD_AVB_ALGORITHM'", + "SHA256_RSA4096", + proptools.String(fooSystem.FsProps().Avb_algorithm), + ) + android.AssertIntEquals( + t, + "Property expected to match the product variable 'BOARD_AVB_SYSTEM_ROLLBACK_INDEX'", + 0, + proptools.Int(fooSystem.FsProps().Rollback_index), + ) + android.AssertStringEquals( + t, + "Property expected to match the product variable 'BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE'", + "ext4", + proptools.String(fooSystem.FsProps().Type), + ) +} diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go index 306d65e29..a0598376b 100644 --- a/fuzz/fuzz_common.go +++ b/fuzz/fuzz_common.go @@ -449,7 +449,7 @@ func IsValidFrameworkForModule(targetFramework Framework, lang Lang, moduleFrame } } -func IsValid(ctx android.ConfigAndErrorContext, fuzzModule FuzzModule) bool { +func IsValid(ctx android.ConfigurableEvaluatorContext, fuzzModule FuzzModule) bool { // Discard ramdisk + vendor_ramdisk + recovery modules, they're duplicates of // fuzz targets we're going to package anyway. if !fuzzModule.Enabled(ctx) || fuzzModule.InRamdisk() || fuzzModule.InVendorRamdisk() || fuzzModule.InRecovery() { diff --git a/genrule/Android.bp b/genrule/Android.bp index 7331741de..49df48075 100644 --- a/genrule/Android.bp +++ b/genrule/Android.bp @@ -22,4 +22,56 @@ bootstrap_go_package { "genrule_test.go", ], pluginFor: ["soong_build"], + // Used by plugins + visibility: ["//visibility:public"], +} + +genrule { + name: "nsjail_genrule_test_input", + cmd: "echo nsjail_genrule_test_input > $(out)", + out: ["nsjail_genrule_test_input.txt"], +} + +// Pseudo-test that's run on checkbuilds to verify consistent directory +// structure for genrules using sbox or nsjail. +genrule_defaults { + name: "nsjail_genrule_test_gen_defaults", + // verify both relative paths and its contents + cmd: "(echo $(out) $(genDir) && sha256sum " + + "$(location get_clang_version) " + + "$(location py3-cmd) " + + "$(location genrule.go) " + + "$(location :nsjail_genrule_test_input) " + + "$(locations *.go)) | sed 's@\\./@@g' > $(out)", + tools: [ + "get_clang_version", // random tool + "py3-cmd", // random prebuilt tool + ], + tool_files: ["genrule.go"], // random local file + srcs: [ + ":nsjail_genrule_test_input", // random OutputFileProducer + "*.go", // random glob + ], + out: ["nsjail_genrule_test.txt"], +} + +genrule { + name: "nsjail_genrule_test_gen_without_nsjail", + defaults: ["nsjail_genrule_test_gen_defaults"], +} + +genrule { + name: "nsjail_genrule_test_gen_with_nsjail", + defaults: ["nsjail_genrule_test_gen_defaults"], + use_nsjail: true, +} + +genrule { + name: "nsjail_genrule_test", + srcs: [ + ":nsjail_genrule_test_gen_without_nsjail", + ":nsjail_genrule_test_gen_with_nsjail", + ], + cmd: "diff $(in) > $(out)", + out: ["nsjail_genrule_test"], } diff --git a/genrule/genrule.go b/genrule/genrule.go index fd72d3c15..e5222a432 100644 --- a/genrule/genrule.go +++ b/genrule/genrule.go @@ -21,11 +21,11 @@ package genrule import ( "fmt" "io" + "path/filepath" "strconv" "strings" "github.com/google/blueprint" - "github.com/google/blueprint/bootstrap" "github.com/google/blueprint/proptools" "android/soong/android" @@ -211,6 +211,9 @@ type generateTask struct { // For gensrsc sharding. shard int shards int + + // For nsjail tasks + useNsjail bool } func (g *Module) GeneratedSourceFiles() android.Paths { @@ -314,16 +317,14 @@ func (g *Module) generateCommonBuildActions(ctx android.ModuleContext) { if len(g.properties.Tools) > 0 { seenTools := make(map[string]bool) - ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) { + ctx.VisitDirectDepsAllowDisabled(func(module android.Module) { switch tag := ctx.OtherModuleDependencyTag(module).(type) { case hostToolDependencyTag: tool := ctx.OtherModuleName(module) - if m, ok := module.(android.Module); ok { - // Necessary to retrieve any prebuilt replacement for the tool, since - // toolDepsMutator runs too late for the prebuilt mutators to have - // replaced the dependency. - module = android.PrebuiltGetPreferred(ctx, m) - } + // Necessary to retrieve any prebuilt replacement for the tool, since + // toolDepsMutator runs too late for the prebuilt mutators to have + // replaced the dependency. + module = android.PrebuiltGetPreferred(ctx, module) switch t := module.(type) { case android.HostToolProvider: @@ -365,11 +366,6 @@ func (g *Module) generateCommonBuildActions(ctx android.ModuleContext) { tools = append(tools, path.Path()) addLocationLabel(tag.label, toolLocation{android.Paths{path.Path()}}) } - case bootstrap.GoBinaryTool: - // A GoBinaryTool provides the install path to a tool, which will be copied. - p := android.PathForGoBinary(ctx, t) - tools = append(tools, p) - addLocationLabel(tag.label, toolLocation{android.Paths{p}}) default: ctx.ModuleErrorf("%q is not a host tool provider", tool) return @@ -460,21 +456,26 @@ func (g *Module) generateCommonBuildActions(ctx android.ModuleContext) { // Pick a unique path outside the task.genDir for the sbox manifest textproto, // a unique rule name, and the user-visible description. - manifestName := "genrule.sbox.textproto" + var rule *android.RuleBuilder desc := "generate" name := "generator" - if task.shards > 0 { - manifestName = "genrule_" + strconv.Itoa(task.shard) + ".sbox.textproto" - desc += " " + strconv.Itoa(task.shard) - name += strconv.Itoa(task.shard) - } else if len(task.out) == 1 { - desc += " " + task.out[0].Base() - } + if task.useNsjail { + rule = android.NewRuleBuilder(pctx, ctx).Nsjail(task.genDir, android.PathForModuleOut(ctx, "nsjail_build_sandbox")) + } else { + manifestName := "genrule.sbox.textproto" + if task.shards > 0 { + manifestName = "genrule_" + strconv.Itoa(task.shard) + ".sbox.textproto" + desc += " " + strconv.Itoa(task.shard) + name += strconv.Itoa(task.shard) + } else if len(task.out) == 1 { + desc += " " + task.out[0].Base() + } - manifestPath := android.PathForModuleOut(ctx, manifestName) + manifestPath := android.PathForModuleOut(ctx, manifestName) - // Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox. - rule := getSandboxedRuleBuilder(ctx, android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath)) + // Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox. + rule = getSandboxedRuleBuilder(ctx, android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath)) + } if Bool(g.properties.Write_if_changed) { rule.Restat() } @@ -575,6 +576,15 @@ func (g *Module) generateCommonBuildActions(ctx android.ModuleContext) { cmd.OrderOnly(ctx.Config().BuildNumberFile(ctx)) } + if task.useNsjail { + for _, input := range task.in { + // can fail if input is a file. + if paths, err := ctx.GlobWithDeps(filepath.Join(input.String(), "**/*"), nil); err == nil { + rule.NsjailImplicits(android.PathsForSource(ctx, paths)) + } + } + } + // Create the rule to run the genrule command inside sbox. rule.Build(name, desc) @@ -838,15 +848,18 @@ func NewGenRule() *Module { properties := &genRuleProperties{} taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask { + useNsjail := Bool(properties.Use_nsjail) + outs := make(android.WritablePaths, len(properties.Out)) for i, out := range properties.Out { outs[i] = android.PathForModuleGen(ctx, out) } return []generateTask{{ - in: srcFiles, - out: outs, - genDir: android.PathForModuleGen(ctx), - cmd: rawCommand, + in: srcFiles, + out: outs, + genDir: android.PathForModuleGen(ctx), + cmd: rawCommand, + useNsjail: useNsjail, }} } @@ -861,6 +874,8 @@ func GenRuleFactory() android.Module { } type genRuleProperties struct { + Use_nsjail *bool + // names of the output files that will be generated Out []string `android:"arch_variant"` } diff --git a/golang/Android.bp b/golang/Android.bp new file mode 100644 index 000000000..3eae94fa2 --- /dev/null +++ b/golang/Android.bp @@ -0,0 +1,22 @@ +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +bootstrap_go_package { + name: "soong-golang", + pkgPath: "android/soong/golang", + deps: [ + "blueprint", + "blueprint-pathtools", + "blueprint-bootstrap", + "soong", + "soong-android", + ], + srcs: [ + "golang.go", + ], + testSrcs: [ + "golang_test.go", + ], + pluginFor: ["soong_build"], +} diff --git a/golang/golang.go b/golang/golang.go new file mode 100644 index 000000000..618a0852b --- /dev/null +++ b/golang/golang.go @@ -0,0 +1,136 @@ +// Copyright 2024 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package golang wraps the blueprint blueprint_go_binary and bootstrap_go_binary module types in versions +// that implement android.Module that are used when building in Soong. This simplifies the code in Soong +// so it can always assume modules are an android.Module. +// The original blueprint blueprint_go_binary and bootstrap_go_binary module types are still used during +// bootstrapping, so the Android.bp entries for these module types must be compatible with both the +// original blueprint module types and these wrapped module types. +package golang + +import ( + "android/soong/android" + "github.com/google/blueprint" + "github.com/google/blueprint/bootstrap" +) + +func init() { + // Wrap the blueprint Go module types with Soong ones that interoperate with the rest of the Soong modules. + bootstrap.GoModuleTypesAreWrapped() + RegisterGoModuleTypes(android.InitRegistrationContext) +} + +func RegisterGoModuleTypes(ctx android.RegistrationContext) { + ctx.RegisterModuleType("bootstrap_go_package", goPackageModuleFactory) + ctx.RegisterModuleType("blueprint_go_binary", goBinaryModuleFactory) +} + +// A GoPackage is a module for building Go packages. +type GoPackage struct { + android.ModuleBase + bootstrap.GoPackage +} + +func goPackageModuleFactory() android.Module { + module := &GoPackage{} + module.AddProperties(module.Properties()...) + android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst) + return module +} + +func (g *GoPackage) GenerateBuildActions(ctx blueprint.ModuleContext) { + // The embedded ModuleBase and bootstrap.GoPackage each implement GenerateBuildActions, + // the delegation has to be implemented manually to disambiguate. Call ModuleBase's + // GenerateBuildActions, which will call GenerateAndroidBuildActions, which will call + // bootstrap.GoPackage.GenerateBuildActions. + g.ModuleBase.GenerateBuildActions(ctx) +} + +func (g *GoPackage) GenerateAndroidBuildActions(ctx android.ModuleContext) { + g.GoPackage.GenerateBuildActions(ctx.BlueprintModuleContext()) +} + +// A GoBinary is a module for building executable binaries from Go sources. +type GoBinary struct { + android.ModuleBase + bootstrap.GoBinary + + outputFile android.Path +} + +func goBinaryModuleFactory() android.Module { + module := &GoBinary{} + module.AddProperties(module.Properties()...) + android.InitAndroidArchModule(module, android.HostSupportedNoCross, android.MultilibFirst) + return module +} + +func (g *GoBinary) GenerateBuildActions(ctx blueprint.ModuleContext) { + // The embedded ModuleBase and bootstrap.GoBinary each implement GenerateBuildActions, + // the delegation has to be implemented manually to disambiguate. Call ModuleBase's + // GenerateBuildActions, which will call GenerateAndroidBuildActions, which will call + // bootstrap.GoBinary.GenerateBuildActions. + g.ModuleBase.GenerateBuildActions(ctx) +} + +func (g *GoBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) { + // Install the file in Soong instead of blueprint so that Soong knows about the install rules. + g.GoBinary.SetSkipInstall() + + // Run the build actions from the wrapped blueprint bootstrap module. + g.GoBinary.GenerateBuildActions(ctx.BlueprintModuleContext()) + + // Translate the bootstrap module's string path into a Path + outputFile := android.PathForArbitraryOutput(ctx, android.Rel(ctx, ctx.Config().OutDir(), g.IntermediateFile())).WithoutRel() + g.outputFile = outputFile + + // Don't create install rules for modules used by bootstrap, the install command line will differ from + // what was used during bootstrap, which will cause ninja to rebuild the module on the next run, + // triggering reanalysis. + if !usedByBootstrap(ctx.ModuleName()) { + installPath := ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), outputFile) + + // Modules in an unexported namespace have no install rule, only add modules in the exported namespaces + // to the blueprint_tools phony rules. + if !ctx.Config().KatiEnabled() || g.ExportedToMake() { + ctx.Phony("blueprint_tools", installPath) + } + } + + ctx.SetOutputFiles(android.Paths{outputFile}, "") +} + +func usedByBootstrap(name string) bool { + switch name { + case "loadplugins", "soong_build": + return true + default: + return false + } +} + +func (g *GoBinary) HostToolPath() android.OptionalPath { + return android.OptionalPathForPath(g.outputFile) +} + +func (g *GoBinary) AndroidMkEntries() []android.AndroidMkEntries { + return []android.AndroidMkEntries{ + { + Class: "EXECUTABLES", + OutputFile: android.OptionalPathForPath(g.outputFile), + Include: "$(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk", + }, + } +} diff --git a/golang/golang_test.go b/golang/golang_test.go new file mode 100644 index 000000000..b51214402 --- /dev/null +++ b/golang/golang_test.go @@ -0,0 +1,51 @@ +// Copyright 2024 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package golang + +import ( + "android/soong/android" + "github.com/google/blueprint/bootstrap" + "path/filepath" + "testing" +) + +func TestGolang(t *testing.T) { + bp := ` + bootstrap_go_package { + name: "gopkg", + pkgPath: "test/pkg", + } + + blueprint_go_binary { + name: "gobin", + deps: ["gopkg"], + } + ` + + result := android.GroupFixturePreparers( + android.PrepareForTestWithArchMutator, + android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { + RegisterGoModuleTypes(ctx) + ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) { + ctx.BottomUpBlueprint("bootstrap_deps", bootstrap.BootstrapDeps) + }) + }), + ).RunTestWithBp(t, bp) + + bin := result.ModuleForTests("gobin", result.Config.BuildOSTarget.String()) + + expected := filepath.Join("out/soong/host", result.Config.PrebuiltOS(), "bin/go/gobin/obj/gobin") + android.AssertPathsRelativeToTopEquals(t, "output files", []string{expected}, bin.OutputFiles(result.TestContext, t, "")) +} diff --git a/java/Android.bp b/java/Android.bp index 9603815a1..1101d7a33 100644 --- a/java/Android.bp +++ b/java/Android.bp @@ -72,7 +72,7 @@ bootstrap_go_package { "rro.go", "sdk.go", "sdk_library.go", - "sdk_library_external.go", + "sdk_library_internal.go", "support_libraries.go", "system_modules.go", "systemserver_classpath_fragment.go", @@ -120,4 +120,5 @@ bootstrap_go_package { "test_spec_test.go", ], pluginFor: ["soong_build"], + visibility: ["//visibility:public"], } diff --git a/java/aar.go b/java/aar.go index b5e24c4b2..7d73b03e4 100644 --- a/java/aar.go +++ b/java/aar.go @@ -45,7 +45,7 @@ func RegisterAARBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("android_library_import", AARImportFactory) ctx.RegisterModuleType("android_library", AndroidLibraryFactory) ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { - ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator) + ctx.Transition("propagate_rro_enforcement", &propagateRROEnforcementTransitionMutator{}) }) } @@ -151,15 +151,67 @@ type split struct { path android.Path } -// Propagate RRO enforcement flag to static lib dependencies transitively. -func propagateRROEnforcementMutator(ctx android.TopDownMutatorContext) { +// Propagate RRO enforcement flag to static lib dependencies transitively. If EnforceRROGlobally is set then +// all modules will use the "" variant. If specific modules have RRO enforced, then modules (usually apps) with +// RRO enabled will use the "" variation for themselves, but use the "rro" variant of direct and transitive static +// android_library dependencies. +type propagateRROEnforcementTransitionMutator struct{} + +func (p propagateRROEnforcementTransitionMutator) Split(ctx android.BaseModuleContext) []string { + // Never split modules, apps with or without RRO enabled use the "" variant, static android_library dependencies + // will use create the "rro" variant from incoming tranisitons. + return []string{""} +} + +func (p propagateRROEnforcementTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string { + // Non-static dependencies are not involved in RRO and always use the empty variant. + if ctx.DepTag() != staticLibTag { + return "" + } + m := ctx.Module() - if d, ok := m.(AndroidLibraryDependency); ok && d.IsRROEnforced(ctx) { - ctx.VisitDirectDepsWithTag(staticLibTag, func(d android.Module) { - if a, ok := d.(AndroidLibraryDependency); ok { - a.SetRROEnforcedForDependent(true) - } - }) + if _, ok := m.(AndroidLibraryDependency); ok { + // If RRO is enforced globally don't bother using "rro" variants, the empty variant will have RRO enabled. + if ctx.Config().EnforceRROGlobally() { + return "" + } + + // If RRO is enabled for this module use the "rro" variants of static dependencies. IncomingTransition will + // rewrite this back to "" if the dependency is not an android_library. + if ctx.Config().EnforceRROForModule(ctx.Module().Name()) { + return "rro" + } + } + + return sourceVariation +} + +func (p propagateRROEnforcementTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string { + // Propagate the "rro" variant to android_library modules, but use the empty variant for everything else. + if incomingVariation == "rro" { + m := ctx.Module() + if _, ok := m.(AndroidLibraryDependency); ok { + return "rro" + } + return "" + } + + return "" +} + +func (p propagateRROEnforcementTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) { + m := ctx.Module() + if d, ok := m.(AndroidLibraryDependency); ok { + if variation == "rro" { + // This is the "rro" variant of a module that has both variants, mark this one as RRO enabled and + // hide it from make to avoid collisions with the non-RRO empty variant. + d.SetRROEnforcedForDependent(true) + m.HideFromMake() + } else if ctx.Config().EnforceRROGlobally() { + // RRO is enabled globally, mark it enabled for this module, but there is only one variant so no + // need to hide it from make. + d.SetRROEnforcedForDependent(true) + } } } diff --git a/java/androidmk.go b/java/androidmk.go index a1bc90494..0539d25aa 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -415,7 +415,7 @@ func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries { } else { var names []string for _, jniLib := range app.jniLibs { - names = append(names, jniLib.name) + names = append(names, jniLib.name+":"+jniLib.target.Arch.ArchType.Bitness()) } entries.AddStrings("LOCAL_REQUIRED_MODULES", names...) } diff --git a/java/androidmk_test.go b/java/androidmk_test.go index 243a2791e..1d98b180d 100644 --- a/java/androidmk_test.go +++ b/java/androidmk_test.go @@ -286,7 +286,7 @@ func TestJniAsRequiredDeps(t *testing.T) { }{ { name: "app", - expected: []string{"libjni"}, + expected: []string{"libjni:64"}, }, { name: "app_embedded", diff --git a/java/app.go b/java/app.go index 4ac42a750..381808a0d 100644 --- a/java/app.go +++ b/java/app.go @@ -83,7 +83,7 @@ type appProperties struct { Package_splits []string // list of native libraries that will be provided in or alongside the resulting jar - Jni_libs []string `android:"arch_variant"` + Jni_libs proptools.Configurable[[]string] `android:"arch_variant"` // if true, use JNI libraries that link against platform APIs even if this module sets // sdk_version. @@ -311,7 +311,7 @@ func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) { } else { tag = jniInstallTag } - ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...) + ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs.GetOrDefault(ctx, nil)...) } for _, aconfig_declaration := range a.aaptProperties.Flags_packages { ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration) @@ -428,7 +428,7 @@ func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) { func (a *AndroidApp) checkEmbedJnis(ctx android.BaseModuleContext) { apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) apkInApex := !apexInfo.IsForPlatform() - hasJnis := len(a.appProperties.Jni_libs) > 0 + hasJnis := len(a.appProperties.Jni_libs.GetOrDefault(ctx, nil)) > 0 if apkInApex && hasJnis && !Bool(a.appProperties.Use_embedded_native_libs) { ctx.ModuleErrorf("APK in APEX should have use_embedded_native_libs: true") @@ -586,7 +586,7 @@ func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) { if override := ctx.Config().Getenv("OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION"); override != "" { a.aapt.defaultManifestVersion = override } else { - a.aapt.defaultManifestVersion = android.DefaultUpdatableModuleVersion + a.aapt.defaultManifestVersion = ctx.Config().ReleaseDefaultUpdatableModuleVersion() } } @@ -1135,7 +1135,7 @@ func collectJniDeps(ctx android.ModuleContext, return jniLibs, prebuiltJniPackages } -func (a *AndroidApp) WalkPayloadDeps(ctx android.ModuleContext, do android.PayloadDepsCallback) { +func (a *AndroidApp) WalkPayloadDeps(ctx android.BaseModuleContext, do android.PayloadDepsCallback) { ctx.WalkDeps(func(child, parent android.Module) bool { isExternal := !a.DepIsInSameApex(ctx, child) if am, ok := child.(android.ApexModule); ok { @@ -1153,7 +1153,7 @@ func (a *AndroidApp) buildAppDependencyInfo(ctx android.ModuleContext) { } depsInfo := android.DepNameToDepInfoMap{} - a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool { + a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool { depName := to.Name() // Skip dependencies that are only available to APEXes; they are developed with updatability @@ -1417,7 +1417,8 @@ func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) { } testConfig := tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config, - a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites, a.testProperties.Auto_gen_config, configs) + a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites, + a.testProperties.Auto_gen_config, configs, a.testProperties.Test_options.Test_runner_options) a.testConfig = a.FixTestConfig(ctx, testConfig) a.extraTestConfigs = android.PathsForModuleSrc(ctx, a.testProperties.Test_options.Extra_test_configs) a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data) @@ -1781,16 +1782,15 @@ func (u *usesLibrary) classLoaderContextForUsesLibDeps(ctx android.ModuleContext } } - // Skip java_sdk_library dependencies that provide stubs, but not an implementation. - // This will be restricted to optional_uses_libs - if sdklib, ok := m.(SdkLibraryDependency); ok { - if tag == usesLibOptTag && sdklib.DexJarBuildPath(ctx).PathOrNil() == nil { - u.shouldDisableDexpreopt = true - return - } - } - if lib, ok := m.(UsesLibraryDependency); ok { + if _, ok := android.OtherModuleProvider(ctx, m, SdkLibraryInfoProvider); ok { + // Skip java_sdk_library dependencies that provide stubs, but not an implementation. + // This will be restricted to optional_uses_libs + if tag == usesLibOptTag && lib.DexJarBuildPath(ctx).PathOrNil() == nil { + u.shouldDisableDexpreopt = true + return + } + } libName := dep if ulib, ok := m.(ProvidesUsesLib); ok && ulib.ProvidesUsesLib() != nil { libName = *ulib.ProvidesUsesLib() diff --git a/java/app_import.go b/java/app_import.go index 045a89a34..a54cf2fc3 100644 --- a/java/app_import.go +++ b/java/app_import.go @@ -533,10 +533,6 @@ func (a *AndroidAppImport) MinSdkVersion(ctx android.EarlyModuleContext) android return android.SdkSpecPrivate.ApiLevel } -func (a *AndroidAppImport) LintDepSets() LintDepSets { - return LintDepSets{} -} - var _ android.ApexModule = (*AndroidAppImport)(nil) // Implements android.ApexModule diff --git a/java/app_test.go b/java/app_test.go index ec97a553f..dd672a095 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -530,9 +530,9 @@ func TestUpdatableApps_ApplyDefaultUpdatableModuleVersion(t *testing.T) { `) foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer") android.AssertStringDoesContain(t, - "com.android.foo: expected manifest fixer to set override-placeholder-version to android.DefaultUpdatableModuleVersion", + "com.android.foo: expected manifest fixer to set override-placeholder-version to RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION", foo.BuildParams.Args["args"], - fmt.Sprintf("--override-placeholder-version %s", android.DefaultUpdatableModuleVersion), + fmt.Sprintf("--override-placeholder-version %s", testDefaultUpdatableModuleVersion), ) } @@ -1419,26 +1419,31 @@ func TestAndroidResourceProcessor(t *testing.T) { } func TestAndroidResourceOverlays(t *testing.T) { + type moduleAndVariant struct { + module string + variant string + } + testCases := []struct { name string enforceRROTargets []string enforceRROExcludedOverlays []string - resourceFiles map[string][]string - overlayFiles map[string][]string - rroDirs map[string][]string + resourceFiles map[moduleAndVariant][]string + overlayFiles map[moduleAndVariant][]string + rroDirs map[moduleAndVariant][]string }{ { name: "no RRO", enforceRROTargets: nil, enforceRROExcludedOverlays: nil, - resourceFiles: map[string][]string{ - "foo": nil, - "bar": {"bar/res/res/values/strings.xml"}, - "lib": nil, - "lib2": {"lib2/res/res/values/strings.xml"}, + resourceFiles: map[moduleAndVariant][]string{ + {"foo", "android_common"}: nil, + {"bar", "android_common"}: {"bar/res/res/values/strings.xml"}, + {"lib", "android_common"}: nil, + {"lib2", "android_common"}: {"lib2/res/res/values/strings.xml"}, }, - overlayFiles: map[string][]string{ - "foo": { + overlayFiles: map[moduleAndVariant][]string{ + {"foo", "android_common"}: { "out/soong/.intermediates/lib2/android_common/package-res.apk", "out/soong/.intermediates/lib/android_common/package-res.apk", "out/soong/.intermediates/lib3/android_common/package-res.apk", @@ -1447,57 +1452,65 @@ func TestAndroidResourceOverlays(t *testing.T) { "device/vendor/blah/overlay/foo/res/values/strings.xml", "product/vendor/blah/overlay/foo/res/values/strings.xml", }, - "bar": { + {"bar", "android_common"}: { "device/vendor/blah/static_overlay/bar/res/values/strings.xml", "device/vendor/blah/overlay/bar/res/values/strings.xml", }, - "lib": { + {"lib", "android_common"}: { "out/soong/.intermediates/lib2/android_common/package-res.apk", "lib/res/res/values/strings.xml", "device/vendor/blah/overlay/lib/res/values/strings.xml", }, }, - rroDirs: map[string][]string{ - "foo": nil, - "bar": nil, + rroDirs: map[moduleAndVariant][]string{ + {"foo", "android_common"}: nil, + {"bar", "android_common"}: nil, }, }, { name: "enforce RRO on foo", enforceRROTargets: []string{"foo"}, enforceRROExcludedOverlays: []string{"device/vendor/blah/static_overlay"}, - resourceFiles: map[string][]string{ - "foo": nil, - "bar": {"bar/res/res/values/strings.xml"}, - "lib": nil, - "lib2": {"lib2/res/res/values/strings.xml"}, + resourceFiles: map[moduleAndVariant][]string{ + {"foo", "android_common"}: nil, + {"bar", "android_common"}: {"bar/res/res/values/strings.xml"}, + {"lib", "android_common"}: nil, + {"lib", "android_common_rro"}: nil, + {"lib2", "android_common"}: {"lib2/res/res/values/strings.xml"}, + {"lib2", "android_common_rro"}: {"lib2/res/res/values/strings.xml"}, }, - overlayFiles: map[string][]string{ - "foo": { - "out/soong/.intermediates/lib2/android_common/package-res.apk", - "out/soong/.intermediates/lib/android_common/package-res.apk", - "out/soong/.intermediates/lib3/android_common/package-res.apk", + overlayFiles: map[moduleAndVariant][]string{ + {"foo", "android_common"}: { + "out/soong/.intermediates/lib2/android_common_rro/package-res.apk", + "out/soong/.intermediates/lib/android_common_rro/package-res.apk", + "out/soong/.intermediates/lib3/android_common_rro/package-res.apk", "foo/res/res/values/strings.xml", "device/vendor/blah/static_overlay/foo/res/values/strings.xml", }, - "bar": { + {"bar", "android_common"}: { "device/vendor/blah/static_overlay/bar/res/values/strings.xml", "device/vendor/blah/overlay/bar/res/values/strings.xml", }, - "lib": { + {"lib", "android_common"}: { "out/soong/.intermediates/lib2/android_common/package-res.apk", "lib/res/res/values/strings.xml", + "device/vendor/blah/overlay/lib/res/values/strings.xml", + }, + {"lib", "android_common_rro"}: { + "out/soong/.intermediates/lib2/android_common_rro/package-res.apk", + "lib/res/res/values/strings.xml", }, }, - rroDirs: map[string][]string{ - "foo": { + rroDirs: map[moduleAndVariant][]string{ + {"foo", "android_common"}: { "device:device/vendor/blah/overlay/foo/res", "product:product/vendor/blah/overlay/foo/res", "device:device/vendor/blah/overlay/lib/res", }, - "bar": nil, - "lib": {"device:device/vendor/blah/overlay/lib/res"}, + {"bar", "android_common"}: nil, + {"lib", "android_common"}: nil, + {"lib", "android_common_rro"}: {"device:device/vendor/blah/overlay/lib/res"}, }, }, { @@ -1508,35 +1521,35 @@ func TestAndroidResourceOverlays(t *testing.T) { "device/vendor/blah/static_overlay/foo", "device/vendor/blah/static_overlay/bar/res", }, - resourceFiles: map[string][]string{ - "foo": nil, - "bar": {"bar/res/res/values/strings.xml"}, - "lib": nil, - "lib2": {"lib2/res/res/values/strings.xml"}, + resourceFiles: map[moduleAndVariant][]string{ + {"foo", "android_common"}: nil, + {"bar", "android_common"}: {"bar/res/res/values/strings.xml"}, + {"lib", "android_common"}: nil, + {"lib2", "android_common"}: {"lib2/res/res/values/strings.xml"}, }, - overlayFiles: map[string][]string{ - "foo": { + overlayFiles: map[moduleAndVariant][]string{ + {"foo", "android_common"}: { "out/soong/.intermediates/lib2/android_common/package-res.apk", "out/soong/.intermediates/lib/android_common/package-res.apk", "out/soong/.intermediates/lib3/android_common/package-res.apk", "foo/res/res/values/strings.xml", "device/vendor/blah/static_overlay/foo/res/values/strings.xml", }, - "bar": {"device/vendor/blah/static_overlay/bar/res/values/strings.xml"}, - "lib": { + {"bar", "android_common"}: {"device/vendor/blah/static_overlay/bar/res/values/strings.xml"}, + {"lib", "android_common"}: { "out/soong/.intermediates/lib2/android_common/package-res.apk", "lib/res/res/values/strings.xml", }, }, - rroDirs: map[string][]string{ - "foo": { + rroDirs: map[moduleAndVariant][]string{ + {"foo", "android_common"}: { "device:device/vendor/blah/overlay/foo/res", "product:product/vendor/blah/overlay/foo/res", // Lib dep comes after the direct deps "device:device/vendor/blah/overlay/lib/res", }, - "bar": {"device:device/vendor/blah/overlay/bar/res"}, - "lib": {"device:device/vendor/blah/overlay/lib/res"}, + {"bar", "android_common"}: {"device:device/vendor/blah/overlay/bar/res"}, + {"lib", "android_common"}: {"device:device/vendor/blah/overlay/lib/res"}, }, }, } @@ -1621,19 +1634,19 @@ func TestAndroidResourceOverlays(t *testing.T) { for _, o := range list { res := module.MaybeOutput(o) if res.Rule != nil { - // If the overlay is compiled as part of this module (i.e. a .arsc.flat file), + // If the overlay is compiled as part of this moduleAndVariant (i.e. a .arsc.flat file), // verify the inputs to the .arsc.flat rule. files = append(files, res.Inputs.Strings()...) } else { - // Otherwise, verify the full path to the output of the other module + // Otherwise, verify the full path to the output of the other moduleAndVariant files = append(files, o) } } return files } - getResources := func(moduleName string) (resourceFiles, overlayFiles, rroDirs []string) { - module := result.ModuleForTests(moduleName, "android_common") + getResources := func(moduleName, variantName string) (resourceFiles, overlayFiles, rroDirs []string) { + module := result.ModuleForTests(moduleName, variantName) resourceList := module.MaybeOutput("aapt2/res.list") if resourceList.Rule != nil { resourceFiles = resourceListToFiles(module, android.PathsRelativeToTop(resourceList.Inputs)) @@ -1658,21 +1671,33 @@ func TestAndroidResourceOverlays(t *testing.T) { return resourceFiles, overlayFiles, rroDirs } - modules := []string{"foo", "bar", "lib", "lib2"} - for _, module := range modules { - resourceFiles, overlayFiles, rroDirs := getResources(module) + modules := []moduleAndVariant{ + {"foo", "android_common"}, + {"foo", "android_common_rro"}, + {"bar", "android_common"}, + {"bar", "android_common_rro"}, + {"lib", "android_common"}, + {"lib", "android_common_rro"}, + {"lib2", "android_common"}, + {"lib2", "android_common_rro"}, + } + for _, moduleAndVariant := range modules { + if _, exists := testCase.resourceFiles[moduleAndVariant]; !exists { + continue + } + resourceFiles, overlayFiles, rroDirs := getResources(moduleAndVariant.module, moduleAndVariant.variant) - if !reflect.DeepEqual(resourceFiles, testCase.resourceFiles[module]) { + if !reflect.DeepEqual(resourceFiles, testCase.resourceFiles[moduleAndVariant]) { t.Errorf("expected %s resource files:\n %#v\n got:\n %#v", - module, testCase.resourceFiles[module], resourceFiles) + moduleAndVariant, testCase.resourceFiles[moduleAndVariant], resourceFiles) } - if !reflect.DeepEqual(overlayFiles, testCase.overlayFiles[module]) { + if !reflect.DeepEqual(overlayFiles, testCase.overlayFiles[moduleAndVariant]) { t.Errorf("expected %s overlay files:\n %#v\n got:\n %#v", - module, testCase.overlayFiles[module], overlayFiles) + moduleAndVariant, testCase.overlayFiles[moduleAndVariant], overlayFiles) } - if !reflect.DeepEqual(rroDirs, testCase.rroDirs[module]) { + if !reflect.DeepEqual(rroDirs, testCase.rroDirs[moduleAndVariant]) { t.Errorf("expected %s rroDirs: %#v\n got:\n %#v", - module, testCase.rroDirs[module], rroDirs) + moduleAndVariant, testCase.rroDirs[moduleAndVariant], rroDirs) } } }) @@ -3241,7 +3266,7 @@ func TestUsesLibraries(t *testing.T) { java_library { name: "static-runtime-helper", srcs: ["a.java"], - libs: ["runtime-library"], + libs: ["runtime-library.impl"], sdk_version: "current", } @@ -3305,7 +3330,7 @@ func TestUsesLibraries(t *testing.T) { name: "app", srcs: ["a.java"], libs: [ - "qux", + "qux.impl", "quuz.stubs" ], static_libs: [ diff --git a/java/base.go b/java/base.go index ef299b279..32bfc17f9 100644 --- a/java/base.go +++ b/java/base.go @@ -83,9 +83,6 @@ type CommonProperties struct { // list of java libraries that will be compiled into the resulting jar Static_libs proptools.Configurable[[]string] `android:"arch_variant"` - // list of java libraries that should not be used to build this module - Exclude_static_libs []string `android:"arch_variant"` - // manifest file to be included in resulting jar Manifest *string `android:"path"` @@ -535,7 +532,8 @@ type Module struct { linter // list of the xref extraction files - kytheFiles android.Paths + kytheFiles android.Paths + kytheKotlinFiles android.Paths hideApexVariantFromMake bool @@ -707,9 +705,6 @@ func setOutputFiles(ctx android.ModuleContext, m Module) { ctx.SetOutputFiles(android.Paths{m.dexer.proguardDictionary.Path()}, ".proguard_map") } ctx.SetOutputFiles(m.properties.Generated_srcjars, ".generated_srcjars") - if m.linter.outputs.xml != nil { - ctx.SetOutputFiles(android.Paths{m.linter.outputs.xml}, ".lint") - } } func InitJavaModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) { @@ -829,7 +824,7 @@ func (j *Module) AvailableFor(what string) bool { } func (j *Module) staticLibs(ctx android.BaseModuleContext) []string { - return android.RemoveListFromList(j.properties.Static_libs.GetOrDefault(ctx, nil), j.properties.Exclude_static_libs) + return j.properties.Static_libs.GetOrDefault(ctx, nil) } func (j *Module) deps(ctx android.BottomUpMutatorContext) { @@ -853,33 +848,6 @@ func (j *Module) deps(ctx android.BottomUpMutatorContext) { // Add dependency on libraries that provide additional hidden api annotations. ctx.AddVariationDependencies(nil, hiddenApiAnnotationsTag, j.properties.Hiddenapi_additional_annotations...) - if ctx.Config().EnforceInterPartitionJavaSdkLibrary() { - // Require java_sdk_library at inter-partition java dependency to ensure stable - // interface between partitions. If inter-partition java_library dependency is detected, - // raise build error because java_library doesn't have a stable interface. - // - // Inputs: - // PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY - // if true, enable enforcement - // PRODUCT_INTER_PARTITION_JAVA_LIBRARY_ALLOWLIST - // exception list of java_library names to allow inter-partition dependency - for idx := range j.properties.Libs { - if libDeps[idx] == nil { - continue - } - - if javaDep, ok := libDeps[idx].(javaSdkLibraryEnforceContext); ok { - // java_sdk_library is always allowed at inter-partition dependency. - // So, skip check. - if _, ok := javaDep.(*SdkLibrary); ok { - continue - } - - j.checkPartitionsForJavaDependency(ctx, "libs", javaDep) - } - } - } - // For library dependencies that are component libraries (like stubs), add the implementation // as a dependency (dexpreopt needs to be against the implementation library, not stubs). for _, dep := range libDeps { @@ -1370,7 +1338,7 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath kotlinJar := android.PathForModuleOut(ctx, "kotlin", jarName) kotlinHeaderJar := android.PathForModuleOut(ctx, "kotlin_headers", jarName) - kotlinCompile(ctx, kotlinJar, kotlinHeaderJar, uniqueSrcFiles, kotlinCommonSrcFiles, srcJars, flags) + j.kotlinCompile(ctx, kotlinJar, kotlinHeaderJar, uniqueSrcFiles, kotlinCommonSrcFiles, srcJars, flags) if ctx.Failed() { return } @@ -1735,8 +1703,12 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath return } + completeStaticLibsImplementationJarsToCombine := completeStaticLibsImplementationJars + if j.shouldInstrument(ctx) { - outputFile = j.instrument(ctx, flags, outputFile, jarName, specs) + instrumentedOutputFile := j.instrument(ctx, flags, outputFile, jarName, specs) + completeStaticLibsImplementationJarsToCombine = android.NewDepSet(android.PREORDER, android.Paths{instrumentedOutputFile}, nil) + outputFile = instrumentedOutputFile } // merge implementation jar with resources if necessary @@ -1744,7 +1716,7 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath if ctx.Config().UseTransitiveJarsInClasspath() { resourceJars := completeStaticLibsResourceJars.ToList() if len(resourceJars) > 0 { - implementationAndResourcesJarsToCombine = append(resourceJars, completeStaticLibsImplementationJars.ToList()...) + implementationAndResourcesJarsToCombine = append(resourceJars, completeStaticLibsImplementationJarsToCombine.ToList()...) implementationAndResourcesJarsToCombine = append(implementationAndResourcesJarsToCombine, extraDepCombinedJars...) } } else { @@ -1789,14 +1761,14 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath classesJar: outputFile, jarName: jarName, } - if j.GetProfileGuided() && j.optimizeOrObfuscateEnabled() && !j.EnableProfileRewriting() { + if j.GetProfileGuided(ctx) && j.optimizeOrObfuscateEnabled() && !j.EnableProfileRewriting(ctx) { ctx.PropertyErrorf("enable_profile_rewriting", "Enable_profile_rewriting must be true when profile_guided dexpreopt and R8 optimization/obfuscation is turned on. The attached profile should be sourced from an unoptimized/unobfuscated APK.", ) } - if j.EnableProfileRewriting() { - profile := j.GetProfile() - if profile == "" || !j.GetProfileGuided() { + if j.EnableProfileRewriting(ctx) { + profile := j.GetProfile(ctx) + if profile == "" || !j.GetProfileGuided(ctx) { ctx.PropertyErrorf("enable_profile_rewriting", "Profile and Profile_guided must be set when enable_profile_rewriting is true") } params.artProfileInput = &profile @@ -2413,18 +2385,12 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { return } - if dep, ok := module.(SdkLibraryDependency); ok { + if sdkInfo, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok { switch tag { - case sdkLibTag, libTag: - depHeaderJars := dep.SdkHeaderJars(ctx, j.SdkVersion(ctx)) - deps.classpath = append(deps.classpath, depHeaderJars...) - deps.dexClasspath = append(deps.dexClasspath, depHeaderJars...) - - // TODO: SDK libraries should export a provider with TransitiveClasspathHeaderJars - depHeaderJarsSet := android.NewDepSet(android.PREORDER, depHeaderJars, nil) - transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars, depHeaderJarsSet) - case staticLibTag: - ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName) + case sdkLibTag, libTag, staticLibTag: + generatingLibsString := android.PrettyConcat( + getGeneratingLibs(ctx, j.SdkVersion(ctx), module.Name(), sdkInfo), true, "or") + ctx.ModuleErrorf("cannot depend directly on java_sdk_library %q; try depending on %s instead", module.Name(), generatingLibsString) } } else if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok { if sdkLinkType != javaPlatform { @@ -2730,7 +2696,7 @@ func collectDirectDepsProviders(ctx android.ModuleContext) (result *JarJarProvid module := ctx.Module() moduleName := module.Name() - ctx.VisitDirectDepsIgnoreBlueprint(func(m android.Module) { + ctx.VisitDirectDeps(func(m android.Module) { tag := ctx.OtherModuleDependencyTag(m) // This logic mirrors that in (*Module).collectDeps above. There are several places // where we explicitly return RenameUseExclude, even though it is the default, to @@ -2753,7 +2719,7 @@ func collectDirectDepsProviders(ctx android.ModuleContext) (result *JarJarProvid if IsJniDepTag(tag) || tag == certificateTag || tag == proguardRaiseTag { return RenameUseExclude, "tags" } - if _, ok := m.(SdkLibraryDependency); ok { + if _, ok := android.OtherModuleProvider(ctx, m, SdkLibraryInfoProvider); ok { switch tag { case sdkLibTag, libTag: return RenameUseExclude, "sdklibdep" // matches collectDeps() diff --git a/java/boot_jars.go b/java/boot_jars.go index 6223dede8..3c3bd550c 100644 --- a/java/boot_jars.go +++ b/java/boot_jars.go @@ -21,7 +21,7 @@ import ( // isActiveModule returns true if the given module should be considered for boot // jars, i.e. if it's enabled and the preferred one in case of source and // prebuilt alternatives. -func isActiveModule(ctx android.ConfigAndErrorContext, module android.Module) bool { +func isActiveModule(ctx android.ConfigurableEvaluatorContext, module android.Module) bool { if !module.Enabled(ctx) { return false } diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index fe4cc7685..4fcd40bd3 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -414,6 +414,12 @@ func (b *BootclasspathFragmentModule) DepIsInSameApex(ctx android.BaseModuleCont // Cross-cutting metadata dependencies are metadata. return false } + // Dependency to the bootclasspath fragment of another apex + // e.g. concsrypt-bootclasspath-fragment --> art-bootclasspath-fragment + if tag == bootclasspathFragmentDepTag { + return false + + } panic(fmt.Errorf("boot_image module %q should not have a dependency on %q via tag %s", b, dep, android.PrettyPrintTag(tag))) } @@ -1099,22 +1105,10 @@ func (module *PrebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx an return &output } +// DEPRECATED: this information is now generated in the context of the top level prebuilt apex. // produceBootImageProfile extracts the boot image profile from the APEX if available. func (module *PrebuiltBootclasspathFragmentModule) produceBootImageProfile(ctx android.ModuleContext) android.WritablePath { - // This module does not provide a boot image profile. - if module.getProfileProviderApex(ctx) == "" { - return nil - } - - di, err := android.FindDeapexerProviderForModule(ctx) - if err != nil { - // An error was found, possibly due to multiple apexes in the tree that export this library - // Defer the error till a client tries to call getProfilePath - module.profilePathErr = err - return nil // An error has been reported by FindDeapexerProviderForModule. - } - - return di.PrebuiltExportPath(ProfileInstallPathInApex) + return android.PathForModuleInstall(ctx, "intentionally_no_longer_supported") } func (b *PrebuiltBootclasspathFragmentModule) getProfilePath() android.Path { diff --git a/java/config/Android.bp b/java/config/Android.bp index bfe83ab8c..6217390bb 100644 --- a/java/config/Android.bp +++ b/java/config/Android.bp @@ -17,4 +17,8 @@ bootstrap_go_package { "kotlin.go", "makevars.go", ], + visibility: [ + "//build/soong:__subpackages__", + "//external/error_prone/soong", + ], } diff --git a/java/config/config.go b/java/config/config.go index c28e07032..87703d821 100644 --- a/java/config/config.go +++ b/java/config/config.go @@ -97,11 +97,19 @@ func init() { "-JDcom.android.tools.r8.emitRecordAnnotationsInDex", "-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex", }, dexerJavaVmFlagsList...), " ")) - pctx.StaticVariable("R8Flags", strings.Join(append([]string{ - "-JXmx4096M", - "-JDcom.android.tools.r8.emitRecordAnnotationsInDex", - "-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex", - }, dexerJavaVmFlagsList...), " ")) + + pctx.VariableFunc("R8Flags", func(ctx android.PackageVarContext) string { + r8flags := append([]string{ + "-JXmx4096M", + "-JDcom.android.tools.r8.emitRecordAnnotationsInDex", + "-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex", + }, dexerJavaVmFlagsList...) + if r8DumpDir := ctx.Config().Getenv("R8_DUMP_DIRECTORY"); r8DumpDir != "" { + r8flags = append(r8flags, "-JDcom.android.tools.r8.dumpinputtodirectory="+r8DumpDir) + } + return strings.Join(r8flags, " ") + + }) pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{ `-Xmaxerrs 9999999`, @@ -145,6 +153,7 @@ func init() { pctx.SourcePathVariable("JmodCmd", "${JavaToolchain}/jmod") pctx.SourcePathVariable("JrtFsJar", "${JavaHome}/lib/jrt-fs.jar") pctx.SourcePathVariable("JavaKytheExtractorJar", "prebuilts/build-tools/common/framework/javac_extractor.jar") + pctx.SourcePathVariable("KotlinKytheExtractor", "prebuilts/build-tools/${hostPrebuiltTag}/bin/kotlinc_extractor") pctx.SourcePathVariable("Ziptime", "prebuilts/build-tools/${hostPrebuiltTag}/bin/ziptime") pctx.SourcePathVariable("ResourceProcessorBusyBox", "prebuilts/bazel/common/android_tools/android_tools/all_android_tools_deploy.jar") diff --git a/java/config/kotlin.go b/java/config/kotlin.go index e5e187cad..302d021db 100644 --- a/java/config/kotlin.go +++ b/java/config/kotlin.go @@ -50,4 +50,11 @@ func init() { }, " ")) pctx.StaticVariable("KotlincGlobalFlags", strings.Join([]string{}, " ")) + // Use KotlincKytheGlobalFlags to prevent kotlinc version skew issues between android and + // g3 kythe indexers. + // This is necessary because there might be instances of kotlin code in android + // platform that are not fully compatible with the kotlinc used in g3 kythe indexers. + // e.g. uninitialized variables are a warning in 1.*, but an error in 2.* + // https://github.com/JetBrains/kotlin/blob/master/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt#L748 + pctx.StaticVariable("KotlincKytheGlobalFlags", strings.Join([]string{"-language-version 1.9"}, " ")) } diff --git a/java/dex.go b/java/dex.go index 7d42efc9c..e16b05208 100644 --- a/java/dex.go +++ b/java/dex.go @@ -120,7 +120,7 @@ func (d *DexProperties) resourceShrinkingEnabled(ctx android.ModuleContext) bool } func (d *DexProperties) optimizedResourceShrinkingEnabled(ctx android.ModuleContext) bool { - return d.resourceShrinkingEnabled(ctx) && Bool(d.Optimize.Optimized_shrink_resources) + return d.resourceShrinkingEnabled(ctx) && BoolDefault(d.Optimize.Optimized_shrink_resources, ctx.Config().UseOptimizedResourceShrinkingByDefault()) } func (d *dexer) optimizeOrObfuscateEnabled() bool { @@ -220,14 +220,21 @@ func (d *dexer) dexCommonFlags(ctx android.ModuleContext, deps = append(deps, f) } - if ctx.Config().Getenv("NO_OPTIMIZE_DX") != "" { + var requestReleaseMode bool + requestReleaseMode, flags = android.RemoveFromList("--release", flags) + + if ctx.Config().Getenv("NO_OPTIMIZE_DX") != "" || ctx.Config().Getenv("GENERATE_DEX_DEBUG") != "" { flags = append(flags, "--debug") + requestReleaseMode = false } - if ctx.Config().Getenv("GENERATE_DEX_DEBUG") != "" { - flags = append(flags, - "--debug", - "--verbose") + // Don't strip out debug information for eng builds, unless the target + // explicitly provided the `--release` build flag. This allows certain + // test targets to remain optimized as part of eng test_suites builds. + if requestReleaseMode { + flags = append(flags, "--release") + } else if ctx.Config().Eng() { + flags = append(flags, "--debug") } // Supplying the platform build flag disables various features like API modeling and desugaring. @@ -245,6 +252,16 @@ func (d *dexer) dexCommonFlags(ctx android.ModuleContext, if err != nil { ctx.PropertyErrorf("min_sdk_version", "%s", err) } + if !Bool(d.dexProperties.No_dex_container) && effectiveVersion.FinalOrFutureInt() >= 36 { + // W is 36, but we have not bumped the SDK version yet, so check for both. + if ctx.Config().PlatformSdkVersion().FinalInt() >= 36 || + ctx.Config().PlatformSdkCodename() == "Wear" { + // TODO(b/329465418): Skip this module since it causes issue with app DRM + if ctx.ModuleName() != "framework-minus-apex" { + flags = append([]string{"-JDcom.android.tools.r8.dexContainerExperiment"}, flags...) + } + } + } // If the specified SDK level is 10000, then configure the compiler to use the // current platform SDK level and to compile the build as a platform build. @@ -374,11 +391,6 @@ func (d *dexer) r8Flags(ctx android.ModuleContext, dexParams *compileDexParams) // TODO(ccross): if this is an instrumentation test of an obfuscated app, use the // dictionary of the app and move the app from libraryjars to injars. - // Don't strip out debug information for eng builds. - if ctx.Config().Eng() { - r8Flags = append(r8Flags, "--debug") - } - // TODO(b/180878971): missing classes should be added to the relevant builds. // TODO(b/229727645): do not use true as default for Android platform builds. if proptools.BoolDefault(opt.Ignore_warnings, true) { @@ -390,7 +402,7 @@ func (d *dexer) r8Flags(ctx android.ModuleContext, dexParams *compileDexParams) r8Flags = append(r8Flags, "--resource-input", d.resourcesInput.Path().String()) r8Deps = append(r8Deps, d.resourcesInput.Path()) r8Flags = append(r8Flags, "--resource-output", d.resourcesOutput.Path().String()) - if Bool(opt.Optimized_shrink_resources) { + if d.dexProperties.optimizedResourceShrinkingEnabled(ctx) { r8Flags = append(r8Flags, "--optimized-resource-shrinking") } } diff --git a/java/dex_test.go b/java/dex_test.go index 4862d06c9..8bc28e678 100644 --- a/java/dex_test.go +++ b/java/dex_test.go @@ -713,3 +713,97 @@ android_app { } }`) } + +func TestDebugReleaseFlags(t *testing.T) { + bp := ` + android_app { + name: "app", + srcs: ["foo.java"], + platform_apis: true, + dxflags: ["%s"] + } + ` + + testcases := []struct { + name string + envVar string + isEng bool + dxFlags string + expectedFlags string + }{ + { + name: "app_no_optimize_dx", + envVar: "NO_OPTIMIZE_DX", + expectedFlags: "--debug", + }, + { + name: "app_release_no_optimize_dx", + envVar: "NO_OPTIMIZE_DX", + dxFlags: "--release", + // Global env vars override explicit dxflags. + expectedFlags: "--debug", + }, + { + name: "app_generate_dex_debug", + envVar: "GENERATE_DEX_DEBUG", + expectedFlags: "--debug", + }, + { + name: "app_release_generate_dex_debug", + envVar: "GENERATE_DEX_DEBUG", + dxFlags: "--release", + // Global env vars override explicit dxflags. + expectedFlags: "--debug", + }, + { + name: "app_eng", + isEng: true, + expectedFlags: "--debug", + }, + { + name: "app_release_eng", + isEng: true, + dxFlags: "--release", + // Eng mode does *not* override explicit dxflags. + expectedFlags: "--release", + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + fixturePreparer := PrepareForTestWithJavaDefaultModules + fixturePreparer = android.GroupFixturePreparers( + fixturePreparer, + android.FixtureModifyProductVariables( + func(variables android.FixtureProductVariables) { + variables.Eng = proptools.BoolPtr(tc.isEng) + }, + ), + ) + if tc.envVar != "" { + fixturePreparer = android.GroupFixturePreparers( + fixturePreparer, + android.FixtureMergeEnv(map[string]string{ + tc.envVar: "true", + }), + ) + } + result := fixturePreparer.RunTestWithBp(t, fmt.Sprintf(bp, tc.dxFlags)) + + appR8 := result.ModuleForTests("app", "android_common").Rule("r8") + android.AssertStringDoesContain(t, "expected flag in R8 flags", + appR8.Args["r8Flags"], tc.expectedFlags) + + var unexpectedFlags string + if tc.expectedFlags == "--debug" { + unexpectedFlags = "--release" + } else if tc.expectedFlags == "--release" { + unexpectedFlags = "--debug" + } + if unexpectedFlags != "" { + android.AssertStringDoesNotContain(t, "unexpected flag in R8 flags", + appR8.Args["r8Flags"], unexpectedFlags) + } + }) + } +} diff --git a/java/dexpreopt.go b/java/dexpreopt.go index 4734357ab..63a863497 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -147,25 +147,25 @@ type dexpreopter struct { type DexpreoptProperties struct { Dex_preopt struct { // If false, prevent dexpreopting. Defaults to true. - Enabled *bool + Enabled proptools.Configurable[bool] `android:"replace_instead_of_append"` // If true, generate an app image (.art file) for this module. - App_image *bool + App_image proptools.Configurable[bool] `android:"replace_instead_of_append"` // If true, use a checked-in profile to guide optimization. Defaults to false unless // a matching profile is set or a profile is found in PRODUCT_DEX_PREOPT_PROFILE_DIR // that matches the name of this module, in which case it is defaulted to true. - Profile_guided *bool + Profile_guided proptools.Configurable[bool] `android:"replace_instead_of_append"` // If set, provides the path to profile relative to the Android.bp file. If not set, // defaults to searching for a file that matches the name of this module in the default // profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found. - Profile *string `android:"path"` + Profile proptools.Configurable[string] `android:"path,replace_instead_of_append"` // If set to true, r8/d8 will use `profile` as input to generate a new profile that matches // the optimized dex. // The new profile will be subsequently used as the profile to dexpreopt the dex file. - Enable_profile_rewriting *bool + Enable_profile_rewriting proptools.Configurable[bool] `android:"replace_instead_of_append"` } Dex_preopt_result struct { @@ -244,7 +244,7 @@ func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext, libName s return true } - if !BoolDefault(d.dexpreoptProperties.Dex_preopt.Enabled, true) { + if !d.dexpreoptProperties.Dex_preopt.Enabled.GetOrDefault(ctx, true) { return true } @@ -433,12 +433,12 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, libName string, dexJa if d.inputProfilePathOnHost != nil { profileClassListing = android.OptionalPathForPath(d.inputProfilePathOnHost) - } else if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) && !forPrebuiltApex(ctx) { + } else if d.dexpreoptProperties.Dex_preopt.Profile_guided.GetOrDefault(ctx, true) && !forPrebuiltApex(ctx) { // If enable_profile_rewriting is set, use the rewritten profile instead of the checked-in profile - if d.EnableProfileRewriting() { + if d.EnableProfileRewriting(ctx) { profileClassListing = android.OptionalPathForPath(d.GetRewrittenProfile()) profileIsTextListing = true - } else if profile := d.GetProfile(); profile != "" { + } else if profile := d.GetProfile(ctx); profile != "" { // If dex_preopt.profile_guided is not set, default it based on the existence of the // dexprepot.profile option or the profile class listing. profileClassListing = android.OptionalPathForPath( @@ -458,6 +458,8 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, libName string, dexJa // Use the dexJar to create a unique scope for each dexJarStem := strings.TrimSuffix(dexJarFile.Base(), dexJarFile.Ext()) + appImage := d.dexpreoptProperties.Dex_preopt.App_image.Get(ctx) + // Full dexpreopt config, used to create dexpreopt build rules. dexpreoptConfig := &dexpreopt.ModuleConfig{ Name: libName, @@ -486,8 +488,8 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, libName string, dexJa PreoptBootClassPathDexFiles: dexFiles.Paths(), PreoptBootClassPathDexLocations: dexLocations, - NoCreateAppImage: !BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, true), - ForceCreateAppImage: BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, false), + NoCreateAppImage: !appImage.GetOrDefault(true), + ForceCreateAppImage: appImage.GetOrDefault(false), PresignedPrebuilt: d.isPresignedPrebuilt, } @@ -657,16 +659,16 @@ func (d *dexpreopter) disableDexpreopt() { d.shouldDisableDexpreopt = true } -func (d *dexpreopter) EnableProfileRewriting() bool { - return proptools.Bool(d.dexpreoptProperties.Dex_preopt.Enable_profile_rewriting) +func (d *dexpreopter) EnableProfileRewriting(ctx android.BaseModuleContext) bool { + return d.dexpreoptProperties.Dex_preopt.Enable_profile_rewriting.GetOrDefault(ctx, false) } -func (d *dexpreopter) GetProfile() string { - return proptools.String(d.dexpreoptProperties.Dex_preopt.Profile) +func (d *dexpreopter) GetProfile(ctx android.BaseModuleContext) string { + return d.dexpreoptProperties.Dex_preopt.Profile.GetOrDefault(ctx, "") } -func (d *dexpreopter) GetProfileGuided() bool { - return proptools.Bool(d.dexpreoptProperties.Dex_preopt.Profile_guided) +func (d *dexpreopter) GetProfileGuided(ctx android.BaseModuleContext) bool { + return d.dexpreoptProperties.Dex_preopt.Profile_guided.GetOrDefault(ctx, false) } func (d *dexpreopter) GetRewrittenProfile() android.Path { diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index a2e473469..5c69ff151 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -463,6 +463,7 @@ func dexpreoptBootJarsFactory() android.SingletonModule { func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) { ctx.RegisterParallelSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory) + ctx.RegisterModuleType("art_boot_images", artBootImagesFactory) ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) { ctx.BottomUp("dex_bootjars_deps", DexpreoptBootJarsMutator).Parallel() }) @@ -601,6 +602,7 @@ func (d *dexpreoptBootJars) GenerateAndroidBuildActions(ctx android.ModuleContex d.defaultBootImage = defaultBootImageConfig(ctx) d.otherImages = make([]*bootImageConfig, 0, len(imageConfigs)-1) var profileInstalls android.RuleBuilderInstalls + var artBootImageHostInstalls android.RuleBuilderInstalls for _, name := range getImageNames() { config := imageConfigs[name] if config != d.defaultBootImage { @@ -616,6 +618,18 @@ func (d *dexpreoptBootJars) GenerateAndroidBuildActions(ctx android.ModuleContex d.bootFrameworkProfile = bootProfile profileInstalls = append(profileInstalls, installs...) } + // Gather the install files of the host variant of the ART boot image. + // These installed files will be used in ART tests. + if config.name == "art" { + for _, variant := range config.variants { + if variant.target.Os != ctx.Config().BuildOS { + // not a host variant + continue + } + artBootImageHostInstalls = append(artBootImageHostInstalls, variant.installs...) + artBootImageHostInstalls = append(artBootImageHostInstalls, variant.vdexInstalls...) + } + } } if len(profileInstalls) > 0 { android.SetProvider(ctx, profileInstallInfoProvider, profileInstallInfo{ @@ -626,6 +640,15 @@ func (d *dexpreoptBootJars) GenerateAndroidBuildActions(ctx android.ModuleContex installFile(ctx, install) } } + // Set a provider containing the install files of the host variant of the ART boot image. + // The actual install rules will be created by `art_boot_images` + android.SetProvider( + ctx, + artBootImageHostInfoProvider, + artBootImageHostInfo{ + installs: artBootImageHostInstalls, + }, + ) } // GenerateSingletonBuildActions generates build rules for the dexpreopt config for Make. @@ -968,6 +991,14 @@ func packageFileForTargetImage(ctx android.ModuleContext, image *bootImageVarian } } +var artBootImageHostInfoProvider = blueprint.NewProvider[artBootImageHostInfo]() + +// artBootImageHostInfo contains the install locations of the host variant of ART boot image +// this contains both the primary and secondary arch locations +type artBootImageHostInfo struct { + installs android.RuleBuilderInstalls +} + // Generate boot image build rules for a specific target. func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) bootImageVariantOutputs { @@ -1186,8 +1217,13 @@ func bootImageProfileRuleCommon(ctx android.ModuleContext, name string, dexFiles return nil } - defaultProfile := "frameworks/base/config/boot-image-profile.txt" - extraProfile := "frameworks/base/config/boot-image-profile-extra.txt" + defaultProfile := "frameworks/base/boot/boot-image-profile.txt" + // If ART is prebuilt, primarily in next release configs, this will still use + // the profile from source which represent the latest code, so it may not + // correspond to the BCP jars in the prebuilt APEX, but this is the profile we + // have access to. + artProfile := "art/build/boot/boot-image-profile.txt" + extraProfile := "frameworks/base/boot/boot-image-profile-extra.txt" rule := android.NewRuleBuilder(pctx, ctx) @@ -1202,6 +1238,9 @@ func bootImageProfileRuleCommon(ctx android.ModuleContext, name string, dexFiles // Return nil and continue without profile. return nil } + if path := android.ExistentPathForSource(ctx, artProfile); path.Valid() { + profiles = append(profiles, path.Path()) + } if path := android.ExistentPathForSource(ctx, extraProfile); path.Valid() { profiles = append(profiles, path.Path()) } @@ -1259,7 +1298,7 @@ func bootFrameworkProfileRule(ctx android.ModuleContext, image *bootImageConfig) return nil, nil } - defaultProfile := "frameworks/base/config/boot-profile.txt" + defaultProfile := "frameworks/base/boot/boot-profile.txt" bootFrameworkProfile := android.PathForSource(ctx, defaultProfile) profile := image.dir.Join(ctx, "boot.bprof") @@ -1344,18 +1383,7 @@ func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) { } image := d.defaultBootImage - if image != nil { - if profileInstallInfo, ok := android.OtherModuleProvider(ctx, d, profileInstallInfoProvider); ok { - ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", profileInstallInfo.profileInstalls.String()) - if profileInstallInfo.profileLicenseMetadataFile.Valid() { - ctx.Strict("DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA", profileInstallInfo.profileLicenseMetadataFile.String()) - } - } - - if SkipDexpreoptBootJars(ctx) { - return - } - + if image != nil && !SkipDexpreoptBootJars(ctx) { global := dexpreopt.GetGlobalConfig(ctx) dexPaths, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp) ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(dexPaths.Strings(), " ")) @@ -1396,3 +1424,70 @@ func (d *dexpreoptBootJars) AndroidMkEntries() []android.AndroidMkEntries { OutputFile: android.OptionalPathForPath(d.bootFrameworkProfile), }} } + +// artBootImages is a thin wrapper around `dex_bootjars`. +// it creates the installation rules for the host variant of the ART boot image. +type artBootImages struct { + android.ModuleBase + + // A non-empty file that will be written as `LOCAL_SOONG_INSTALLED_MODULE` in out/soong/Android-*.mk + outputFile android.OptionalPath +} + +func artBootImagesFactory() android.Module { + m := &artBootImages{} + android.InitAndroidMultiTargetsArchModule(m, android.HostSupported, android.MultilibCommon) + return m +} + +func (dbj *artBootImages) DepsMutator(ctx android.BottomUpMutatorContext) { + // Create a dependency on `dex_bootjars` to access the intermediate locations of host art boot image. + ctx.AddDependency(ctx.Module(), dexpreoptBootJarDepTag, "dex_bootjars") +} + +func (d *artBootImages) GenerateAndroidBuildActions(ctx android.ModuleContext) { + ctx.VisitDirectDepsWithTag(dexpreoptBootJarDepTag, func(m android.Module) { + hostInstallsInfo, ok := android.OtherModuleProvider(ctx, m, artBootImageHostInfoProvider) + if !ok { + ctx.ModuleErrorf("Could not find information about the host variant of ART boot image") + } + installs := d.installFile(ctx, hostInstallsInfo.installs) + if len(installs) > 0 { + d.outputFile = android.OptionalPathForPath(installs[0]) + // Create a phony target that can ART run-tests can depend on. + ctx.Phony(d.Name(), installs...) + } else { + // this might be true e.g. when building with `WITH_DEXPREOPT=false` + // create an empty file so that the `art_boot_images` is known to the packaging system. + d.outputFile = android.OptionalPathForPath(android.PathForModuleOut(ctx, "undefined_art_boot_images")) + } + }) +} + +// Creates an installation rule for host variant of ART boot image files. +// Returns the list of install locations (out/host/linux-x86/...) +func (d *artBootImages) installFile(ctx android.ModuleContext, ruleBuilderInstalls android.RuleBuilderInstalls) android.Paths { + var ret android.Paths + for _, ruleBuilderInstall := range ruleBuilderInstalls { + installDir := android.PathForModuleInstall( + ctx, + strings.TrimPrefix(filepath.Dir(ruleBuilderInstall.To), "/"), + ) + filename := filepath.Base(ruleBuilderInstall.To) + ctx.InstallFile( + installDir, + filename, + ruleBuilderInstall.From, + ) + ret = append(ret, installDir.Join(ctx, filename)) + } + return ret +} + +// Set `OutputFile` expclitly so that this module does not get elided when generating out/soong/Android-*.mk +func (d *artBootImages) AndroidMkEntries() []android.AndroidMkEntries { + return []android.AndroidMkEntries{{ + Class: "ETC", + OutputFile: d.outputFile, + }} +} diff --git a/java/dexpreopt_config_testing.go b/java/dexpreopt_config_testing.go index 37c54b915..33c682bfa 100644 --- a/java/dexpreopt_config_testing.go +++ b/java/dexpreopt_config_testing.go @@ -1335,8 +1335,6 @@ DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTart=out/soong/dexpreopt_arm64/dex_artjars/andro DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTboot=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/boot.art DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTmainline=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/boot.art:out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/boot-framework-foo.art DEXPREOPT_IMAGE_NAMES=art boot mainline -DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED=out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof:/system/etc/boot-image.prof out/soong/dexpreopt_arm64/dex_bootjars/boot.bprof:/system/etc/boot-image.bprof -DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-extra1.oat:/apex/art_boot_images/javalib/arm/boot-extra1.oat DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-extra1.oat:/apex/art_boot_images/javalib/arm64/boot-extra1.oat DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.oat:/apex/art_boot_images/javalib/x86/boot-extra1.oat diff --git a/java/droiddoc.go b/java/droiddoc.go index 2929bb8d9..2980d91de 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -373,8 +373,10 @@ func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps { panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName())) } case libTag, sdkLibTag: - if dep, ok := module.(SdkLibraryDependency); ok { - deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...) + if sdkInfo, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok { + generatingLibsString := android.PrettyConcat( + getGeneratingLibs(ctx, j.SdkVersion(ctx), module.Name(), sdkInfo), true, "or") + ctx.ModuleErrorf("cannot depend directly on java_sdk_library %q; try depending on %s instead", module.Name(), generatingLibsString) } else if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok { deps.classpath = append(deps.classpath, dep.HeaderJars...) deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...) diff --git a/java/fuzz.go b/java/fuzz.go index d37c55804..e5f1f04ee 100644 --- a/java/fuzz.go +++ b/java/fuzz.go @@ -85,10 +85,11 @@ func JavaFuzzFactory() android.Module { func (j *JavaFuzzTest) DepsMutator(ctx android.BottomUpMutatorContext) { if j.Os().Class.String() == deviceString { - j.testProperties.Jni_libs = append(j.testProperties.Jni_libs, artDeps...) + j.testProperties.Jni_libs.AppendSimpleValue(artDeps) } - if len(j.testProperties.Jni_libs) > 0 { + jniLibs := j.testProperties.Jni_libs.GetOrDefault(ctx, nil) + if len(jniLibs) > 0 { if j.fuzzPackagedModule.FuzzProperties.Fuzz_config == nil { config := &fuzz.FuzzConfig{} j.fuzzPackagedModule.FuzzProperties.Fuzz_config = config @@ -98,7 +99,7 @@ func (j *JavaFuzzTest) DepsMutator(ctx android.BottomUpMutatorContext) { j.fuzzPackagedModule.FuzzProperties.Fuzz_config.IsJni = proptools.BoolPtr(true) for _, target := range ctx.MultiTargets() { sharedLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"}) - ctx.AddFarVariationDependencies(sharedLibVariations, jniLibTag, j.testProperties.Jni_libs...) + ctx.AddFarVariationDependencies(sharedLibVariations, jniLibTag, jniLibs...) } } diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go index 4144de82b..365005835 100644 --- a/java/hiddenapi_modular.go +++ b/java/hiddenapi_modular.go @@ -298,13 +298,12 @@ func hiddenAPIAddStubLibDependencies(ctx android.BottomUpMutatorContext, apiScop // available, or reports an error. func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android.Module, kind android.SdkKind) android.Path { var dexJar OptionalDexJarPath - if sdkLibrary, ok := module.(SdkLibraryDependency); ok { + if sdkLibrary, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok { if ctx.Config().ReleaseHiddenApiExportableStubs() { - dexJar = sdkLibrary.SdkApiExportableStubDexJar(ctx, kind) + dexJar = sdkLibrary.ExportableStubDexJarPaths[kind] } else { - dexJar = sdkLibrary.SdkApiStubDexJar(ctx, kind) + dexJar = sdkLibrary.EverythingStubDexJarPaths[kind] } - } else if j, ok := module.(UsesLibraryDependency); ok { dexJar = j.DexJarBuildPath(ctx) } else { @@ -853,15 +852,15 @@ func (i *HiddenAPIFlagInput) gatherStubLibInfo(ctx android.ModuleContext, conten i.StubDexJarsByScope.addStubDexJar(ctx, module, apiScope, dexJar) } - if sdkLibrary, ok := module.(SdkLibraryDependency); ok { - removedTxtFile := sdkLibrary.SdkRemovedTxtFile(ctx, sdkKind) + if sdkLibrary, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok { + removedTxtFile := sdkLibrary.RemovedTxtFiles[sdkKind] i.RemovedTxtFiles = append(i.RemovedTxtFiles, removedTxtFile.AsPaths()...) } } // If the contents includes any java_sdk_library modules then add them to the stubs. for _, module := range contents { - if _, ok := module.(SdkLibraryDependency); ok { + if _, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok { // Add information for every possible API scope needed by hidden API. for _, apiScope := range hiddenAPISdkLibrarySupportedScopes { addFromModule(ctx, module, apiScope) diff --git a/java/java.go b/java/java.go index 95f4fd892..661422b54 100644 --- a/java/java.go +++ b/java/java.go @@ -356,12 +356,17 @@ type UsesLibraryDependency interface { // TODO(jungjw): Move this to kythe.go once it's created. type xref interface { XrefJavaFiles() android.Paths + XrefKotlinFiles() android.Paths } func (j *Module) XrefJavaFiles() android.Paths { return j.kytheFiles } +func (j *Module) XrefKotlinFiles() android.Paths { + return j.kytheKotlinFiles +} + func (d dependencyTag) PropagateAconfigValidation() bool { return d.static } @@ -1296,7 +1301,7 @@ type testProperties struct { Test_options TestOptions // Names of modules containing JNI libraries that should be installed alongside the test. - Jni_libs []string + Jni_libs proptools.Configurable[[]string] // Install the test into a folder named for the module in all test suites. Per_testcase_directory *bool @@ -1480,10 +1485,11 @@ func (j *TestHost) DepsMutator(ctx android.BottomUpMutatorContext) { } } - if len(j.testProperties.Jni_libs) > 0 { + jniLibs := j.testProperties.Jni_libs.GetOrDefault(ctx, nil) + if len(jniLibs) > 0 { for _, target := range ctx.MultiTargets() { sharedLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"}) - ctx.AddFarVariationDependencies(sharedLibVariations, jniLibTag, j.testProperties.Jni_libs...) + ctx.AddFarVariationDependencies(sharedLibVariations, jniLibTag, jniLibs...) } } @@ -2607,17 +2613,6 @@ func (a *Import) JacocoReportClassesFile() android.Path { return nil } -func (j *Import) LintDepSets() LintDepSets { - return LintDepSets{} -} - -func (j *Import) getStrictUpdatabilityLinting() bool { - return false -} - -func (j *Import) setStrictUpdatabilityLinting(bool) { -} - func (j *Import) DepsMutator(ctx android.BottomUpMutatorContext) { ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...) ctx.AddVariationDependencies(nil, staticLibTag, j.properties.Static_libs.GetOrDefault(ctx, nil)...) @@ -2695,13 +2690,13 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { transitiveBootClasspathHeaderJars = append(transitiveBootClasspathHeaderJars, dep.TransitiveStaticLibsHeaderJars) } } - } else if dep, ok := module.(SdkLibraryDependency); ok { + } else if _, ok := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider); ok { switch tag { case libTag, sdkLibTag: - depHeaderJars := dep.SdkHeaderJars(ctx, j.SdkVersion(ctx)) - flags.classpath = append(flags.classpath, depHeaderJars...) - transitiveClasspathHeaderJars = append(transitiveClasspathHeaderJars, - android.NewDepSet(android.PREORDER, depHeaderJars, nil)) + sdkInfo, _ := android.OtherModuleProvider(ctx, module, SdkLibraryInfoProvider) + generatingLibsString := android.PrettyConcat( + getGeneratingLibs(ctx, j.SdkVersion(ctx), module.Name(), sdkInfo), true, "or") + ctx.ModuleErrorf("cannot depend directly on java_sdk_library %q; try depending on %s instead", module.Name(), generatingLibsString) } } @@ -2813,41 +2808,14 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { } if ctx.Device() { - // If this is a variant created for a prebuilt_apex then use the dex implementation jar - // obtained from the associated deapexer module. + // Shared libraries deapexed from prebuilt apexes are no longer supported. + // Set the dexJarBuildPath to a fake path. + // This allows soong analysis pass, but will be an error during ninja execution if there are + // any rdeps. ai, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) if ai.ForPrebuiltApex { - // Get the path of the dex implementation jar from the `deapexer` module. - di, err := android.FindDeapexerProviderForModule(ctx) - if err != nil { - // An error was found, possibly due to multiple apexes in the tree that export this library - // Defer the error till a client tries to call DexJarBuildPath - j.dexJarFileErr = err - j.initHiddenAPIError(err) - return - } - dexJarFileApexRootRelative := ApexRootRelativePathToJavaLib(j.BaseModuleName()) - if dexOutputPath := di.PrebuiltExportPath(dexJarFileApexRootRelative); dexOutputPath != nil { - dexJarFile := makeDexJarPathFromPath(dexOutputPath) - j.dexJarFile = dexJarFile - installPath := android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, ApexRootRelativePathToJavaLib(j.BaseModuleName())) - j.dexJarInstallFile = installPath - - j.dexpreopter.installPath = j.dexpreopter.getInstallPath(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), installPath) - setUncompressDex(ctx, &j.dexpreopter, &j.dexer) - j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex - - if profilePath := di.PrebuiltExportPath(dexJarFileApexRootRelative + ".prof"); profilePath != nil { - j.dexpreopter.inputProfilePathOnHost = profilePath - } - - // Initialize the hiddenapi structure. - j.initHiddenAPI(ctx, dexJarFile, outputFile, j.dexProperties.Uncompress_dex) - } else { - // This should never happen as a variant for a prebuilt_apex is only created if the - // prebuilt_apex has been configured to export the java library dex file. - ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt APEX %s", di.ApexModuleName()) - } + j.dexJarFile = makeDexJarPathFromPath(android.PathForModuleInstall(ctx, "intentionally_no_longer_supported")) + j.initHiddenAPI(ctx, j.dexJarFile, outputFile, j.dexProperties.Uncompress_dex) } else if Bool(j.dexProperties.Compile_dex) { sdkDep := decodeSdkDep(ctx, android.SdkContext(j)) if sdkDep.invalidVersion { @@ -3120,21 +3088,10 @@ func (a *DexImport) JacocoReportClassesFile() android.Path { return nil } -func (a *DexImport) LintDepSets() LintDepSets { - return LintDepSets{} -} - func (j *DexImport) IsInstallable() bool { return true } -func (j *DexImport) getStrictUpdatabilityLinting() bool { - return false -} - -func (j *DexImport) setStrictUpdatabilityLinting(bool) { -} - func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { if len(j.properties.Jars) != 1 { ctx.PropertyErrorf("jars", "exactly one jar must be provided") @@ -3304,15 +3261,20 @@ type kytheExtractJavaSingleton struct { func (ks *kytheExtractJavaSingleton) GenerateBuildActions(ctx android.SingletonContext) { var xrefTargets android.Paths + var xrefKotlinTargets android.Paths ctx.VisitAllModules(func(module android.Module) { if javaModule, ok := module.(xref); ok { xrefTargets = append(xrefTargets, javaModule.XrefJavaFiles()...) + xrefKotlinTargets = append(xrefKotlinTargets, javaModule.XrefKotlinFiles()...) } }) // TODO(asmundak): perhaps emit a rule to output a warning if there were no xrefTargets if len(xrefTargets) > 0 { ctx.Phony("xref_java", xrefTargets...) } + if len(xrefKotlinTargets) > 0 { + ctx.Phony("xref_kotlin", xrefKotlinTargets...) + } } var Bool = proptools.Bool @@ -3332,9 +3294,13 @@ func addCLCFromDep(ctx android.ModuleContext, depModule android.Module, depName := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(depModule)) var sdkLib *string - if lib, ok := depModule.(SdkLibraryDependency); ok && lib.sharedLibrary() { + if lib, ok := android.OtherModuleProvider(ctx, depModule, SdkLibraryInfoProvider); ok && lib.SharedLibrary { // A shared SDK library. This should be added as a top-level CLC element. sdkLib = &depName + } else if lib, ok := depModule.(SdkLibraryComponentDependency); ok && lib.OptionalSdkLibraryImplementation() != nil { + if depModule.Name() == proptools.String(lib.OptionalSdkLibraryImplementation())+".impl" { + sdkLib = lib.OptionalSdkLibraryImplementation() + } } else if ulib, ok := depModule.(ProvidesUsesLib); ok { // A non-SDK library disguised as an SDK library by the means of `provides_uses_lib` // property. This should be handled in the same way as a shared SDK library. diff --git a/java/java_test.go b/java/java_test.go index e4e6bca9c..db154ce98 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -670,7 +670,7 @@ func TestPrebuilts(t *testing.T) { java_library { name: "foo", srcs: ["a.java", ":stubs-source"], - libs: ["bar", "sdklib"], + libs: ["bar", "sdklib.stubs"], static_libs: ["baz"], } @@ -2454,37 +2454,6 @@ java_test_host { } } -func TestJavaExcludeStaticLib(t *testing.T) { - ctx, _ := testJava(t, ` - java_library { - name: "bar", - } - java_library { - name: "foo", - } - java_library { - name: "baz", - static_libs: [ - "foo", - "bar", - ], - exclude_static_libs: [ - "bar", - ], - } - `) - - // "bar" not included as dependency of "baz" - CheckModuleDependencies(t, ctx, "baz", "android_common", []string{ - `core-lambda-stubs`, - `ext`, - `foo`, - `framework`, - `stable-core-platform-api-stubs-system-modules`, - `stable.core.platform.api.stubs`, - }) -} - func TestJavaLibraryWithResourcesStem(t *testing.T) { ctx, _ := testJavaWithFS(t, ` java_library { @@ -2674,7 +2643,7 @@ func TestDisableFromTextStubForCoverageBuild(t *testing.T) { android.AssertBoolEquals(t, "stub module expected to depend on from-source stub", true, CheckModuleHasDependency(t, result.TestContext, apiScopePublic.stubsLibraryModuleName("foo"), "android_common", - apiScopePublic.sourceStubLibraryModuleName("foo"))) + apiScopePublic.sourceStubsLibraryModuleName("foo"))) android.AssertBoolEquals(t, "stub module expected to not depend on from-text stub", false, CheckModuleHasDependency(t, result.TestContext, @@ -3065,6 +3034,43 @@ func TestJavaLibraryOutputFilesRel(t *testing.T) { "baz.jar", bazOutputPaths[0].Rel()) } +func TestCoverage(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + prepareForTestWithFrameworkJacocoInstrumentation, + PrepareForTestWithTransitiveClasspathEnabled, + ).RunTestWithBp(t, ` + android_app { + name: "foo", + srcs: ["foo.java"], + static_libs: ["android.car"], + platform_apis: true, + } + + // A library in InstrumentFrameworkModules + java_library { + name: "android.car", + srcs: ["android.car.java"], + } + `) + + foo := result.ModuleForTests("foo", "android_common") + androidCar := result.ModuleForTests("android.car", "android_common") + + fooJacoco := foo.Rule("jacoco") + fooCombine := foo.Description("for javac") + + androidCarJacoco := androidCar.Rule("jacoco") + androidCarJavac := androidCar.Rule("javac") + + android.AssertStringEquals(t, "foo instrumentation rule inputs", fooJacoco.Input.String(), fooCombine.Output.String()) + android.AssertStringEquals(t, "android.car instrumentation rule inputs", androidCarJacoco.Input.String(), androidCarJavac.Output.String()) + + // The input to instrumentation for the `foo` app contains the non-instrumented android.car classes. + android.AssertStringListContains(t, "foo combined inputs", fooCombine.Inputs.Strings(), androidCarJavac.Output.String()) + android.AssertStringListDoesNotContain(t, "foo combined inputs", fooCombine.Inputs.Strings(), androidCarJacoco.Output.String()) +} + func assertTestOnlyAndTopLevel(t *testing.T, ctx *android.TestResult, expectedTestOnly []string, expectedTopLevel []string) { t.Helper() actualTrueModules := []string{} @@ -3095,3 +3101,37 @@ func assertTestOnlyAndTopLevel(t *testing.T, ctx *android.TestResult, expectedTe t.Errorf("top-level: Expected but not found: %v, Found but not expected: %v", left, right) } } + +// Test that a dependency edge is created to the "first" variant of a native library listed in `required` of java_binary +func TestNativeRequiredDepOfJavaBinary(t *testing.T) { + findDepsOfModule := func(ctx *android.TestContext, module android.Module, depName string) []blueprint.Module { + var ret []blueprint.Module + ctx.VisitDirectDeps(module, func(dep blueprint.Module) { + if dep.Name() == depName { + ret = append(ret, dep) + } + }) + return ret + } + + bp := cc.GatherRequiredDepsForTest(android.Android) + ` +java_binary { + name: "myjavabin", + main_class: "com.android.MyJava", + required: ["mynativelib"], +} +cc_library_shared { + name: "mynativelib", +} +` + res, _ := testJava(t, bp) + // The first variant installs the native library via the common variant, so check the deps of both variants. + nativeVariantDepsWithDups := findDepsOfModule(res, res.ModuleForTests("myjavabin", "android_arm64_armv8-a").Module(), "mynativelib") + nativeVariantDepsWithDups = append(nativeVariantDepsWithDups, findDepsOfModule(res, res.ModuleForTests("myjavabin", "android_common").Module(), "mynativelib")...) + + nativeVariantDepsUnique := map[blueprint.Module]bool{} + for _, dep := range nativeVariantDepsWithDups { + nativeVariantDepsUnique[dep] = true + } + android.AssertIntEquals(t, "Create a dep on the first variant", 1, len(nativeVariantDepsUnique)) +} diff --git a/java/kotlin.go b/java/kotlin.go index c28bc3f54..f42d16304 100644 --- a/java/kotlin.go +++ b/java/kotlin.go @@ -64,6 +64,28 @@ var kotlinc = pctx.AndroidRemoteStaticRule("kotlinc", android.RemoteRuleSupports "kotlincFlags", "classpath", "srcJars", "commonSrcFilesArg", "srcJarDir", "classesDir", "headerClassesDir", "headerJar", "kotlinJvmTarget", "kotlinBuildFile", "emptyDir", "name") +var kotlinKytheExtract = pctx.AndroidStaticRule("kotlinKythe", + blueprint.RuleParams{ + Command: `rm -rf "$srcJarDir" && mkdir -p "$srcJarDir" && ` + + `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" -f "*.kt" $srcJars && ` + + `${config.KotlinKytheExtractor} -corpus ${kytheCorpus} --srcs @$out.rsp --srcs @"$srcJarDir/list" $commonSrcFilesList --cp @$classpath -o $out --kotlin_out $outJar ` + + // wrap the additional kotlin args. + // Skip Xbuild file, pass the cp explicitly. + // Skip header jars, those should not have an effect on kythe results. + ` --args '${config.KotlincGlobalFlags} ` + + ` ${config.KotlincSuppressJDK9Warnings} ${config.JavacHeapFlags} ` + + ` $kotlincFlags -jvm-target $kotlinJvmTarget ` + + `${config.KotlincKytheGlobalFlags}'`, + CommandDeps: []string{ + "${config.KotlinKytheExtractor}", + "${config.ZipSyncCmd}", + }, + Rspfile: "$out.rsp", + RspfileContent: "$in", + }, + "classpath", "kotlincFlags", "commonSrcFilesList", "kotlinJvmTarget", "outJar", "srcJars", "srcJarDir", +) + func kotlinCommonSrcsList(ctx android.ModuleContext, commonSrcFiles android.Paths) android.OptionalPath { if len(commonSrcFiles) > 0 { // The list of common_srcs may be too long to put on the command line, but @@ -81,7 +103,7 @@ func kotlinCommonSrcsList(ctx android.ModuleContext, commonSrcFiles android.Path } // kotlinCompile takes .java and .kt sources and srcJars, and compiles the .kt sources into a classes jar in outputFile. -func kotlinCompile(ctx android.ModuleContext, outputFile, headerOutputFile android.WritablePath, +func (j *Module) kotlinCompile(ctx android.ModuleContext, outputFile, headerOutputFile android.WritablePath, srcFiles, commonSrcFiles, srcJars android.Paths, flags javaBuilderFlags) { @@ -127,6 +149,31 @@ func kotlinCompile(ctx android.ModuleContext, outputFile, headerOutputFile andro "name": kotlinName, }, }) + + // Emit kythe xref rule + if (ctx.Config().EmitXrefRules()) && ctx.Module() == ctx.PrimaryModule() { + extractionFile := outputFile.ReplaceExtension(ctx, "kzip") + args := map[string]string{ + "classpath": classpathRspFile.String(), + "kotlincFlags": flags.kotlincFlags, + "kotlinJvmTarget": flags.javaVersion.StringForKotlinc(), + "outJar": outputFile.String(), + "srcJars": strings.Join(srcJars.Strings(), " "), + "srcJarDir": android.PathForModuleOut(ctx, "kotlinc", "srcJars.xref").String(), + } + if commonSrcsList.Valid() { + args["commonSrcFilesList"] = "--common_srcs @" + commonSrcsList.String() + } + ctx.Build(pctx, android.BuildParams{ + Rule: kotlinKytheExtract, + Description: "kotlinKythe", + Output: extractionFile, + Inputs: srcFiles, + Implicits: deps, + Args: args, + }) + j.kytheKotlinFiles = append(j.kytheKotlinFiles, extractionFile) + } } var kaptStubs = pctx.AndroidRemoteStaticRule("kaptStubs", android.RemoteRuleSupports{Goma: true}, diff --git a/java/lint.go b/java/lint.go index 6782adc5f..2cbefc3bb 100644 --- a/java/lint.go +++ b/java/lint.go @@ -19,6 +19,7 @@ import ( "sort" "strings" + "github.com/google/blueprint" "github.com/google/blueprint/proptools" "android/soong/android" @@ -90,7 +91,6 @@ type linter struct { compileSdkKind android.SdkKind javaLanguageLevel string kotlinLanguageLevel string - outputs lintOutputs properties LintProperties extraMainlineLintErrors []string compile_data android.Paths @@ -100,68 +100,55 @@ type linter struct { buildModuleReportZip bool } -type lintOutputs struct { - html android.Path - text android.Path - xml android.Path - referenceBaseline android.Path - - depSets LintDepSets -} - -type lintOutputsIntf interface { - lintOutputs() *lintOutputs -} - -type LintDepSetsIntf interface { - LintDepSets() LintDepSets - - // Methods used to propagate strict_updatability_linting values. - GetStrictUpdatabilityLinting() bool - SetStrictUpdatabilityLinting(bool) -} - type LintDepSets struct { - HTML, Text, XML *android.DepSet[android.Path] + HTML, Text, XML, Baseline *android.DepSet[android.Path] } type LintDepSetsBuilder struct { - HTML, Text, XML *android.DepSetBuilder[android.Path] + HTML, Text, XML, Baseline *android.DepSetBuilder[android.Path] } func NewLintDepSetBuilder() LintDepSetsBuilder { return LintDepSetsBuilder{ - HTML: android.NewDepSetBuilder[android.Path](android.POSTORDER), - Text: android.NewDepSetBuilder[android.Path](android.POSTORDER), - XML: android.NewDepSetBuilder[android.Path](android.POSTORDER), + HTML: android.NewDepSetBuilder[android.Path](android.POSTORDER), + Text: android.NewDepSetBuilder[android.Path](android.POSTORDER), + XML: android.NewDepSetBuilder[android.Path](android.POSTORDER), + Baseline: android.NewDepSetBuilder[android.Path](android.POSTORDER), } } -func (l LintDepSetsBuilder) Direct(html, text, xml android.Path) LintDepSetsBuilder { +func (l LintDepSetsBuilder) Direct(html, text, xml android.Path, baseline android.OptionalPath) LintDepSetsBuilder { l.HTML.Direct(html) l.Text.Direct(text) l.XML.Direct(xml) + if baseline.Valid() { + l.Baseline.Direct(baseline.Path()) + } return l } -func (l LintDepSetsBuilder) Transitive(depSets LintDepSets) LintDepSetsBuilder { - if depSets.HTML != nil { - l.HTML.Transitive(depSets.HTML) +func (l LintDepSetsBuilder) Transitive(info *LintInfo) LintDepSetsBuilder { + if info.TransitiveHTML != nil { + l.HTML.Transitive(info.TransitiveHTML) + } + if info.TransitiveText != nil { + l.Text.Transitive(info.TransitiveText) } - if depSets.Text != nil { - l.Text.Transitive(depSets.Text) + if info.TransitiveXML != nil { + l.XML.Transitive(info.TransitiveXML) } - if depSets.XML != nil { - l.XML.Transitive(depSets.XML) + if info.TransitiveBaseline != nil { + l.Baseline.Transitive(info.TransitiveBaseline) } return l } func (l LintDepSetsBuilder) Build() LintDepSets { return LintDepSets{ - HTML: l.HTML.Build(), - Text: l.Text.Build(), - XML: l.XML.Build(), + HTML: l.HTML.Build(), + Text: l.Text.Build(), + XML: l.XML.Build(), + Baseline: l.Baseline.Build(), } } @@ -209,24 +196,18 @@ var allLintDatabasefiles = map[android.SdkKind]lintDatabaseFiles{ }, } -func (l *linter) LintDepSets() LintDepSets { - return l.outputs.depSets -} +var LintProvider = blueprint.NewProvider[*LintInfo]() -func (l *linter) GetStrictUpdatabilityLinting() bool { - return BoolDefault(l.properties.Lint.Strict_updatability_linting, false) -} +type LintInfo struct { + HTML android.Path + Text android.Path + XML android.Path + ReferenceBaseline android.Path -func (l *linter) SetStrictUpdatabilityLinting(strictLinting bool) { - l.properties.Lint.Strict_updatability_linting = &strictLinting -} - -var _ LintDepSetsIntf = (*linter)(nil) - -var _ lintOutputsIntf = (*linter)(nil) - -func (l *linter) lintOutputs() *lintOutputs { - return &l.outputs + TransitiveHTML *android.DepSet[android.Path] + TransitiveText *android.DepSet[android.Path] + TransitiveXML *android.DepSet[android.Path] + TransitiveBaseline *android.DepSet[android.Path] } func (l *linter) enabled() bool { @@ -262,7 +243,9 @@ func lintRBEExecStrategy(ctx android.ModuleContext) string { return ctx.Config().GetenvWithDefault("RBE_LINT_EXEC_STRATEGY", remoteexec.LocalExecStrategy) } -func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder, srcsList android.Path) lintPaths { +func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder, srcsList android.Path, + baselines android.Paths) lintPaths { + projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml") // Lint looks for a lint.xml file next to the project.xml file, give it one. configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml") @@ -325,12 +308,10 @@ func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.Ru cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks) cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks) - if l.GetStrictUpdatabilityLinting() { + if Bool(l.properties.Lint.Strict_updatability_linting) && len(baselines) > 0 { // Verify the module does not baseline issues that endanger safe updatability. - if l.properties.Lint.Baseline_filename != nil { - cmd.FlagWithInput("--baseline ", android.PathForModuleSrc(ctx, *l.properties.Lint.Baseline_filename)) - cmd.FlagForEachArg("--disallowed_issues ", updatabilityChecks) - } + strictUpdatabilityChecksOutputFile := VerifyStrictUpdatabilityChecks(ctx, baselines) + cmd.Validation(strictUpdatabilityChecksOutputFile) } return lintPaths{ @@ -342,6 +323,22 @@ func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.Ru } +func VerifyStrictUpdatabilityChecks(ctx android.ModuleContext, baselines android.Paths) android.Path { + rule := android.NewRuleBuilder(pctx, ctx) + baselineRspFile := android.PathForModuleOut(ctx, "lint_strict_updatability_check_baselines.rsp") + outputFile := android.PathForModuleOut(ctx, "lint_strict_updatability_check.stamp") + rule.Command().Text("rm -f").Output(outputFile) + rule.Command(). + BuiltTool("lint_strict_updatability_checks"). + FlagWithArg("--name ", ctx.ModuleName()). + FlagWithRspFileInputList("--baselines ", baselineRspFile, baselines). + FlagForEachArg("--disallowed_issues ", updatabilityChecks) + rule.Command().Text("touch").Output(outputFile) + rule.Build("lint_strict_updatability_checks", "lint strict updatability checks") + + return outputFile +} + // generateManifest adds a command to the rule to write a simple manifest that contains the // minSdkVersion and targetSdkVersion for modules (like java_library) that don't have a manifest. func (l *linter) generateManifest(ctx android.ModuleContext, rule *android.RuleBuilder) android.WritablePath { @@ -411,6 +408,26 @@ func (l *linter) lint(ctx android.ModuleContext) { l.extraLintCheckJars = append(l.extraLintCheckJars, android.PathForSource(ctx, "prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar")) + var baseline android.OptionalPath + if l.properties.Lint.Baseline_filename != nil { + baseline = android.OptionalPathForPath(android.PathForModuleSrc(ctx, *l.properties.Lint.Baseline_filename)) + } + + html := android.PathForModuleOut(ctx, "lint", "lint-report.html") + text := android.PathForModuleOut(ctx, "lint", "lint-report.txt") + xml := android.PathForModuleOut(ctx, "lint", "lint-report.xml") + referenceBaseline := android.PathForModuleOut(ctx, "lint", "lint-baseline.xml") + + depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml, baseline) + + ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) { + if info, ok := android.OtherModuleProvider(ctx, dep, LintProvider); ok { + depSetsBuilder.Transitive(info) + } + }) + + depSets := depSetsBuilder.Build() + rule := android.NewRuleBuilder(pctx, ctx). Sbox(android.PathForModuleOut(ctx, "lint"), android.PathForModuleOut(ctx, "lint.sbox.textproto")). @@ -437,20 +454,9 @@ func (l *linter) lint(ctx android.ModuleContext) { srcsListRsp := android.PathForModuleOut(ctx, "lint-srcs.list.rsp") rule.Command().Text("cp").FlagWithRspFileInputList("", srcsListRsp, l.srcs).Output(srcsList).Implicits(l.compile_data) - lintPaths := l.writeLintProjectXML(ctx, rule, srcsList) + baselines := depSets.Baseline.ToList() - html := android.PathForModuleOut(ctx, "lint", "lint-report.html") - text := android.PathForModuleOut(ctx, "lint", "lint-report.txt") - xml := android.PathForModuleOut(ctx, "lint", "lint-report.xml") - referenceBaseline := android.PathForModuleOut(ctx, "lint", "lint-baseline.xml") - - depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml) - - ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) { - if depLint, ok := dep.(LintDepSetsIntf); ok { - depSetsBuilder.Transitive(depLint.LintDepSets()) - } - }) + lintPaths := l.writeLintProjectXML(ctx, rule, srcsList, baselines) rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String()) rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String()) @@ -505,8 +511,8 @@ func (l *linter) lint(ctx android.ModuleContext) { cmd.FlagWithArg("--check ", checkOnly) } - if l.properties.Lint.Baseline_filename != nil { - cmd.FlagWithInput("--baseline ", android.PathForModuleSrc(ctx, *l.properties.Lint.Baseline_filename)) + if baseline.Valid() { + cmd.FlagWithInput("--baseline ", baseline.Path()) } cmd.FlagWithOutput("--write-reference-baseline ", referenceBaseline) @@ -530,25 +536,30 @@ func (l *linter) lint(ctx android.ModuleContext) { rule.Build("lint", "lint") - l.outputs = lintOutputs{ - html: html, - text: text, - xml: xml, - referenceBaseline: referenceBaseline, + android.SetProvider(ctx, LintProvider, &LintInfo{ + HTML: html, + Text: text, + XML: xml, + ReferenceBaseline: referenceBaseline, - depSets: depSetsBuilder.Build(), - } + TransitiveHTML: depSets.HTML, + TransitiveText: depSets.Text, + TransitiveXML: depSets.XML, + TransitiveBaseline: depSets.Baseline, + }) if l.buildModuleReportZip { - l.reports = BuildModuleLintReportZips(ctx, l.LintDepSets()) + l.reports = BuildModuleLintReportZips(ctx, depSets, nil) } // Create a per-module phony target to run the lint check. phonyName := ctx.ModuleName() + "-lint" ctx.Phony(phonyName, xml) + + ctx.SetOutputFiles(android.Paths{xml}, ".lint") } -func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets) android.Paths { +func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets, validations android.Paths) android.Paths { htmlList := android.SortedUniquePaths(depSets.HTML.ToList()) textList := android.SortedUniquePaths(depSets.Text.ToList()) xmlList := android.SortedUniquePaths(depSets.XML.ToList()) @@ -558,13 +569,13 @@ func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets) a } htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip") - lintZip(ctx, htmlList, htmlZip) + lintZip(ctx, htmlList, htmlZip, validations) textZip := android.PathForModuleOut(ctx, "lint-report-text.zip") - lintZip(ctx, textList, textZip) + lintZip(ctx, textList, textZip, validations) xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip") - lintZip(ctx, xmlList, xmlZip) + lintZip(ctx, xmlList, xmlZip, validations) return android.Paths{htmlZip, textZip, xmlZip} } @@ -642,7 +653,7 @@ func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) { return } - var outputs []*lintOutputs + var outputs []*LintInfo var dirs []string ctx.VisitAllModules(func(m android.Module) { if ctx.Config().KatiEnabled() && !m.ExportedToMake() { @@ -658,14 +669,14 @@ func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) { } } - if l, ok := m.(lintOutputsIntf); ok { - outputs = append(outputs, l.lintOutputs()) + if lintInfo, ok := android.OtherModuleProvider(ctx, m, LintProvider); ok { + outputs = append(outputs, lintInfo) } }) dirs = android.SortedUniqueStrings(dirs) - zip := func(outputPath android.WritablePath, get func(*lintOutputs) android.Path) { + zip := func(outputPath android.WritablePath, get func(*LintInfo) android.Path) { var paths android.Paths for _, output := range outputs { @@ -674,20 +685,20 @@ func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) { } } - lintZip(ctx, paths, outputPath) + lintZip(ctx, paths, outputPath, nil) } l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip") - zip(l.htmlZip, func(l *lintOutputs) android.Path { return l.html }) + zip(l.htmlZip, func(l *LintInfo) android.Path { return l.HTML }) l.textZip = android.PathForOutput(ctx, "lint-report-text.zip") - zip(l.textZip, func(l *lintOutputs) android.Path { return l.text }) + zip(l.textZip, func(l *LintInfo) android.Path { return l.Text }) l.xmlZip = android.PathForOutput(ctx, "lint-report-xml.zip") - zip(l.xmlZip, func(l *lintOutputs) android.Path { return l.xml }) + zip(l.xmlZip, func(l *LintInfo) android.Path { return l.XML }) l.referenceBaselineZip = android.PathForOutput(ctx, "lint-report-reference-baselines.zip") - zip(l.referenceBaselineZip, func(l *lintOutputs) android.Path { return l.referenceBaseline }) + zip(l.referenceBaselineZip, func(l *LintInfo) android.Path { return l.ReferenceBaseline }) ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip) } @@ -703,17 +714,9 @@ var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil) func init() { android.RegisterParallelSingletonType("lint", func() android.Singleton { return &lintSingleton{} }) - - registerLintBuildComponents(android.InitRegistrationContext) -} - -func registerLintBuildComponents(ctx android.RegistrationContext) { - ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { - ctx.TopDown("enforce_strict_updatability_linting", enforceStrictUpdatabilityLintingMutator).Parallel() - }) } -func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath) { +func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath, validations android.Paths) { paths = android.SortedUniquePaths(android.CopyOfPaths(paths)) sort.Slice(paths, func(i, j int) bool { @@ -725,19 +728,8 @@ func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android rule.Command().BuiltTool("soong_zip"). FlagWithOutput("-o ", outputPath). FlagWithArg("-C ", android.PathForIntermediates(ctx).String()). - FlagWithRspFileInputList("-r ", outputPath.ReplaceExtension(ctx, "rsp"), paths) + FlagWithRspFileInputList("-r ", outputPath.ReplaceExtension(ctx, "rsp"), paths). + Validations(validations) rule.Build(outputPath.Base(), outputPath.Base()) } - -// Enforce the strict updatability linting to all applicable transitive dependencies. -func enforceStrictUpdatabilityLintingMutator(ctx android.TopDownMutatorContext) { - m := ctx.Module() - if d, ok := m.(LintDepSetsIntf); ok && d.GetStrictUpdatabilityLinting() { - ctx.VisitDirectDepsWithTag(staticLibTag, func(d android.Module) { - if a, ok := d.(LintDepSetsIntf); ok { - a.SetStrictUpdatabilityLinting(true) - } - }) - } -} diff --git a/java/lint_test.go b/java/lint_test.go index b51753f71..afe3914ff 100644 --- a/java/lint_test.go +++ b/java/lint_test.go @@ -164,7 +164,7 @@ func TestJavaLintStrictUpdatabilityLinting(t *testing.T) { sdk_version: "current", lint: { strict_updatability_linting: true, - baseline_filename: "lint-baseline.xml", + baseline_filename: "foo_lint_baseline.xml", }, } @@ -176,7 +176,7 @@ func TestJavaLintStrictUpdatabilityLinting(t *testing.T) { min_sdk_version: "29", sdk_version: "current", lint: { - baseline_filename: "lint-baseline.xml", + baseline_filename: "bar_lint_baseline.xml", } } ` @@ -188,18 +188,13 @@ func TestJavaLintStrictUpdatabilityLinting(t *testing.T) { RunTestWithBp(t, bp) foo := result.ModuleForTests("foo", "android_common") - sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, foo.Output("lint.sbox.textproto")) - if !strings.Contains(*sboxProto.Commands[0].Command, - "--baseline lint-baseline.xml --disallowed_issues NewApi") { - t.Error("did not restrict baselining NewApi") - } - - bar := result.ModuleForTests("bar", "android_common") - sboxProto = android.RuleBuilderSboxProtoForTests(t, result.TestContext, bar.Output("lint.sbox.textproto")) - if !strings.Contains(*sboxProto.Commands[0].Command, - "--baseline lint-baseline.xml --disallowed_issues NewApi") { + strictUpdatabilityCheck := foo.Output("lint_strict_updatability_check.stamp") + if !strings.Contains(strictUpdatabilityCheck.RuleParams.Command, + "--disallowed_issues NewApi") { t.Error("did not restrict baselining NewApi") } + android.AssertStringListContains(t, "strict updatability check baseline inputs", strictUpdatabilityCheck.Inputs.Strings(), "foo_lint_baseline.xml") + android.AssertStringListContains(t, "strict updatability check baseline inputs", strictUpdatabilityCheck.Inputs.Strings(), "bar_lint_baseline.xml") } func TestJavaLintDatabaseSelectionFull(t *testing.T) { diff --git a/java/metalava/Android.bp b/java/metalava/Android.bp index ccbd191d3..6bf183296 100644 --- a/java/metalava/Android.bp +++ b/java/metalava/Android.bp @@ -15,4 +15,5 @@ filegroup { name: "metalava-config-files", srcs: ["*-config.xml"], + visibility: ["//visibility:public"], } diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index d794e511b..5bb77542c 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -33,6 +33,7 @@ var ( platformBootclasspathArtBootJarDepTag = bootclasspathDependencyTag{name: "art-boot-jar"} platformBootclasspathBootJarDepTag = bootclasspathDependencyTag{name: "platform-boot-jar"} platformBootclasspathApexBootJarDepTag = bootclasspathDependencyTag{name: "apex-boot-jar"} + platformBootclasspathImplLibDepTag = dependencyTag{name: "impl-lib-tag"} ) type platformBootclasspathModule struct { @@ -111,12 +112,18 @@ func (b *platformBootclasspathModule) BootclasspathDepsMutator(ctx android.Botto // Add dependencies on all the ART jars. global := dexpreopt.GetGlobalConfig(ctx) addDependenciesOntoSelectedBootImageApexes(ctx, "com.android.art") + + var bootImageModuleNames []string + // TODO: b/308174306 - Remove the mechanism of depending on the java_sdk_library(_import) directly addDependenciesOntoBootImageModules(ctx, global.ArtApexJars, platformBootclasspathArtBootJarDepTag) + bootImageModuleNames = append(bootImageModuleNames, global.ArtApexJars.CopyOfJars()...) // Add dependencies on all the non-updatable jars, which are on the platform or in non-updatable // APEXes. - addDependenciesOntoBootImageModules(ctx, b.platformJars(ctx), platformBootclasspathBootJarDepTag) + platformJars := b.platformJars(ctx) + addDependenciesOntoBootImageModules(ctx, platformJars, platformBootclasspathBootJarDepTag) + bootImageModuleNames = append(bootImageModuleNames, platformJars.CopyOfJars()...) // Add dependencies on all the updatable jars, except the ART jars. apexJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars @@ -127,9 +134,17 @@ func (b *platformBootclasspathModule) BootclasspathDepsMutator(ctx android.Botto addDependenciesOntoSelectedBootImageApexes(ctx, android.FirstUniqueStrings(apexes)...) // TODO: b/308174306 - Remove the mechanism of depending on the java_sdk_library(_import) directly addDependenciesOntoBootImageModules(ctx, apexJars, platformBootclasspathApexBootJarDepTag) + bootImageModuleNames = append(bootImageModuleNames, apexJars.CopyOfJars()...) // Add dependencies on all the fragments. b.properties.BootclasspathFragmentsDepsProperties.addDependenciesOntoFragments(ctx) + + for _, bootImageModuleName := range bootImageModuleNames { + implLibName := implLibraryModuleName(bootImageModuleName) + if ctx.OtherModuleExists(implLibName) { + ctx.AddFarVariationDependencies(nil, platformBootclasspathImplLibDepTag, implLibName) + } + } } func addDependenciesOntoBootImageModules(ctx android.BottomUpMutatorContext, modules android.ConfiguredJarList, tag bootclasspathDependencyTag) { @@ -166,8 +181,15 @@ func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.Mo allModules = append(allModules, apexModules...) b.configuredModules = allModules + // Do not add implLibModule to allModules as the impl lib is only used to collect the + // transitive source files + var implLibModule []android.Module + ctx.VisitDirectDepsWithTag(implLibraryTag, func(m android.Module) { + implLibModule = append(implLibModule, m) + }) + var transitiveSrcFiles android.Paths - for _, module := range allModules { + for _, module := range append(allModules, implLibModule...) { if depInfo, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok { if depInfo.TransitiveSrcFiles != nil { transitiveSrcFiles = append(transitiveSrcFiles, depInfo.TransitiveSrcFiles.ToList()...) diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go index 67ed84e1d..5b145c658 100644 --- a/java/platform_compat_config.go +++ b/java/platform_compat_config.go @@ -110,6 +110,7 @@ func (p *platformCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleCon p.installConfigFile = android.PathForModuleInstall(ctx, "etc", "compatconfig", p.configFile.Base()) rule.Build(configFileName, "Extract compat/compat_config.xml and install it") ctx.InstallFile(p.installDirPath, p.configFile.Base(), p.configFile) + ctx.SetOutputFiles(android.Paths{p.configFile}, "") } func (p *platformCompatConfig) AndroidMkEntries() []android.AndroidMkEntries { diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go index 00613eee3..527e479f2 100644 --- a/java/prebuilt_apis.go +++ b/java/prebuilt_apis.go @@ -124,8 +124,8 @@ func parseFinalizedPrebuiltPath(ctx android.LoadHookContext, p string, allowIncr return } -func prebuiltApiModuleName(mctx android.LoadHookContext, module, scope, version string) string { - return fmt.Sprintf("%s_%s_%s_%s", mctx.ModuleName(), scope, version, module) +func prebuiltApiModuleName(moduleName, module, scope, version string) string { + return fmt.Sprintf("%s_%s_%s_%s", moduleName, scope, version, module) } func createImport(mctx android.LoadHookContext, module, scope, version, path, sdkVersion string, compileDex bool) { props := struct { @@ -135,7 +135,7 @@ func createImport(mctx android.LoadHookContext, module, scope, version, path, sd Installable *bool Compile_dex *bool }{ - Name: proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, version)), + Name: proptools.StringPtr(prebuiltApiModuleName(mctx.ModuleName(), module, scope, version)), Jars: []string{path}, Sdk_version: proptools.StringPtr(sdkVersion), Installable: proptools.BoolPtr(false), @@ -257,8 +257,8 @@ func createSystemModules(mctx android.LoadHookContext, version, scope string) { Name *string Libs []string }{} - props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", scope, version)) - props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", scope, version)) + props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx.ModuleName(), "system_modules", scope, version)) + props.Libs = append(props.Libs, prebuiltApiModuleName(mctx.ModuleName(), "core-for-system-modules", scope, version)) mctx.CreateModule(systemModulesImportFactory, &props) } diff --git a/java/ravenwood.go b/java/ravenwood.go index bb136cf6e..4c9fdc212 100644 --- a/java/ravenwood.go +++ b/java/ravenwood.go @@ -33,8 +33,8 @@ func RegisterRavenwoodBuildComponents(ctx android.RegistrationContext) { var ravenwoodLibContentTag = dependencyTag{name: "ravenwoodlibcontent"} var ravenwoodUtilsTag = dependencyTag{name: "ravenwoodutils"} var ravenwoodRuntimeTag = dependencyTag{name: "ravenwoodruntime"} -var ravenwoodDataTag = dependencyTag{name: "ravenwooddata"} var ravenwoodTestResourceApkTag = dependencyTag{name: "ravenwoodtestresapk"} +var ravenwoodTestInstResourceApkTag = dependencyTag{name: "ravenwoodtest-inst-res-apk"} const ravenwoodUtilsName = "ravenwood-utils" const ravenwoodRuntimeName = "ravenwood-runtime" @@ -54,14 +54,20 @@ func getLibPath(archType android.ArchType) string { } type ravenwoodTestProperties struct { - Jni_libs []string + Jni_libs proptools.Configurable[[]string] // Specify another android_app module here to copy it to the test directory, so that - // the ravenwood test can access it. + // the ravenwood test can access it. This APK will be loaded as resources of the test + // target app. // TODO: For now, we simply refer to another android_app module and copy it to the // test directory. Eventually, android_ravenwood_test should support all the resource // related properties and build resources from the `res/` directory. Resource_apk *string + + // Specify another android_app module here to copy it to the test directory, so that + // the ravenwood test can access it. This APK will be loaded as resources of the test + // instrumentation app itself. + Inst_resource_apk *string } type ravenwoodTest struct { @@ -120,7 +126,7 @@ func (r *ravenwoodTest) DepsMutator(ctx android.BottomUpMutatorContext) { } // Add jni libs - for _, lib := range r.ravenwoodTestProperties.Jni_libs { + for _, lib := range r.ravenwoodTestProperties.Jni_libs.GetOrDefault(ctx, nil) { ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib) } @@ -128,6 +134,10 @@ func (r *ravenwoodTest) DepsMutator(ctx android.BottomUpMutatorContext) { if resourceApk := proptools.String(r.ravenwoodTestProperties.Resource_apk); resourceApk != "" { ctx.AddVariationDependencies(nil, ravenwoodTestResourceApkTag, resourceApk) } + + if resourceApk := proptools.String(r.ravenwoodTestProperties.Inst_resource_apk); resourceApk != "" { + ctx.AddVariationDependencies(nil, ravenwoodTestInstResourceApkTag, resourceApk) + } } func (r *ravenwoodTest) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -195,13 +205,16 @@ func (r *ravenwoodTest) GenerateAndroidBuildActions(ctx android.ModuleContext) { } resApkInstallPath := installPath.Join(ctx, "ravenwood-res-apks") - if resApk := ctx.GetDirectDepsWithTag(ravenwoodTestResourceApkTag); len(resApk) > 0 { - for _, installFile := range android.OtherModuleProviderOrDefault( - ctx, resApk[0], android.InstallFilesProvider).InstallFiles { - installResApk := ctx.InstallFile(resApkInstallPath, "ravenwood-res.apk", installFile) + + copyResApk := func(tag blueprint.DependencyTag, toFileName string) { + if resApk := ctx.GetDirectDepsWithTag(tag); len(resApk) > 0 { + installFile := android.OutputFileForModule(ctx, resApk[0], "") + installResApk := ctx.InstallFile(resApkInstallPath, toFileName, installFile) installDeps = append(installDeps, installResApk) } } + copyResApk(ravenwoodTestResourceApkTag, "ravenwood-res.apk") + copyResApk(ravenwoodTestInstResourceApkTag, "ravenwood-inst-res.apk") // Install our JAR with all dependencies ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.outputFile, installDeps...) @@ -225,10 +238,13 @@ func (r *ravenwoodTest) AndroidMkEntries() []android.AndroidMkEntries { type ravenwoodLibgroupProperties struct { Libs []string - Jni_libs []string + Jni_libs proptools.Configurable[[]string] // We use this to copy framework-res.apk to the ravenwood runtime directory. - Data []string + Data []string `android:"path,arch_variant"` + + // We use this to copy font files to the ravenwood runtime directory. + Fonts []string `android:"path,arch_variant"` } type ravenwoodLibgroup struct { @@ -264,12 +280,9 @@ func (r *ravenwoodLibgroup) DepsMutator(ctx android.BottomUpMutatorContext) { for _, lib := range r.ravenwoodLibgroupProperties.Libs { ctx.AddVariationDependencies(nil, ravenwoodLibContentTag, lib) } - for _, lib := range r.ravenwoodLibgroupProperties.Jni_libs { + for _, lib := range r.ravenwoodLibgroupProperties.Jni_libs.GetOrDefault(ctx, nil) { ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib) } - for _, data := range r.ravenwoodLibgroupProperties.Data { - ctx.AddVariationDependencies(nil, ravenwoodDataTag, data) - } } func (r *ravenwoodLibgroup) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -309,12 +322,17 @@ func (r *ravenwoodLibgroup) GenerateAndroidBuildActions(ctx android.ModuleContex } dataInstallPath := installPath.Join(ctx, "ravenwood-data") - for _, data := range r.ravenwoodLibgroupProperties.Data { - libModule := ctx.GetDirectDepWithTag(data, ravenwoodDataTag) - file := android.OutputFileForModule(ctx, libModule, "") + data := android.PathsForModuleSrc(ctx, r.ravenwoodLibgroupProperties.Data) + for _, file := range data { ctx.InstallFile(dataInstallPath, file.Base(), file) } + fontsInstallPath := installPath.Join(ctx, "fonts") + fonts := android.PathsForModuleSrc(ctx, r.ravenwoodLibgroupProperties.Fonts) + for _, file := range fonts { + ctx.InstallFile(fontsInstallPath, file.Base(), file) + } + // Normal build should perform install steps ctx.Phony(r.BaseModuleName(), android.PathForPhony(ctx, r.BaseModuleName()+"-install")) } diff --git a/java/ravenwood_test.go b/java/ravenwood_test.go index d26db930d..753a118e9 100644 --- a/java/ravenwood_test.go +++ b/java/ravenwood_test.go @@ -19,6 +19,7 @@ import ( "testing" "android/soong/android" + "android/soong/etc" ) var prepareRavenwoodRuntime = android.GroupFixturePreparers( @@ -59,11 +60,19 @@ var prepareRavenwoodRuntime = android.GroupFixturePreparers( } android_app { name: "app1", - sdk_version: "current", + sdk_version: "current", } android_app { name: "app2", - sdk_version: "current", + sdk_version: "current", + } + android_app { + name: "app3", + sdk_version: "current", + } + prebuilt_font { + name: "Font.ttf", + src: "Font.ttf", } android_ravenwood_libgroup { name: "ravenwood-runtime", @@ -76,7 +85,10 @@ var prepareRavenwoodRuntime = android.GroupFixturePreparers( "ravenwood-runtime-jni2", ], data: [ - "app1", + ":app1", + ], + fonts: [ + ":Font.ttf" ], } android_ravenwood_libgroup { @@ -97,6 +109,7 @@ func TestRavenwoodRuntime(t *testing.T) { ctx := android.GroupFixturePreparers( PrepareForIntegrationTestWithJava, + etc.PrepareForTestWithPrebuiltEtc, prepareRavenwoodRuntime, ).RunTest(t) @@ -114,6 +127,7 @@ func TestRavenwoodRuntime(t *testing.T) { runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/libred.so") runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/ravenwood-runtime-jni3.so") runtime.Output(installPathPrefix + "/ravenwood-runtime/ravenwood-data/app1.apk") + runtime.Output(installPathPrefix + "/ravenwood-runtime/fonts/Font.ttf") utils := ctx.ModuleForTests("ravenwood-utils", "android_common") utils.Output(installPathPrefix + "/ravenwood-utils/framework-rules.ravenwood.jar") } @@ -125,29 +139,30 @@ func TestRavenwoodTest(t *testing.T) { ctx := android.GroupFixturePreparers( PrepareForIntegrationTestWithJava, + etc.PrepareForTestWithPrebuiltEtc, prepareRavenwoodRuntime, ).RunTestWithBp(t, ` - cc_library_shared { - name: "jni-lib1", - host_supported: true, - srcs: ["jni.cpp"], - } - cc_library_shared { - name: "jni-lib2", - host_supported: true, - srcs: ["jni.cpp"], - stem: "libblue", - shared_libs: [ - "jni-lib3", - ], - } - cc_library_shared { - name: "jni-lib3", - host_supported: true, - srcs: ["jni.cpp"], - stem: "libpink", - } - android_ravenwood_test { + cc_library_shared { + name: "jni-lib1", + host_supported: true, + srcs: ["jni.cpp"], + } + cc_library_shared { + name: "jni-lib2", + host_supported: true, + srcs: ["jni.cpp"], + stem: "libblue", + shared_libs: [ + "jni-lib3", + ], + } + cc_library_shared { + name: "jni-lib3", + host_supported: true, + srcs: ["jni.cpp"], + stem: "libpink", + } + android_ravenwood_test { name: "ravenwood-test", srcs: ["Test.java"], jni_libs: [ @@ -156,6 +171,7 @@ func TestRavenwoodTest(t *testing.T) { "ravenwood-runtime-jni2", ], resource_apk: "app2", + inst_resource_apk: "app3", sdk_version: "test_current", } `) @@ -183,6 +199,7 @@ func TestRavenwoodTest(t *testing.T) { module.Output(installPathPrefix + "/ravenwood-test/lib64/libblue.so") module.Output(installPathPrefix + "/ravenwood-test/lib64/libpink.so") module.Output(installPathPrefix + "/ravenwood-test/ravenwood-res-apks/ravenwood-res.apk") + module.Output(installPathPrefix + "/ravenwood-test/ravenwood-res-apks/ravenwood-inst-res.apk") // ravenwood-runtime*.so are included in the runtime, so it shouldn't be emitted. for _, o := range module.AllOutputs() { diff --git a/java/robolectric.go b/java/robolectric.go index 374fc5f78..30c720352 100644 --- a/java/robolectric.go +++ b/java/robolectric.go @@ -76,7 +76,7 @@ type robolectricProperties struct { // Use strict mode to limit access of Robolectric API directly. See go/roboStrictMode Strict_mode *bool - Jni_libs []string + Jni_libs proptools.Configurable[[]string] } type robolectricTest struct { @@ -131,7 +131,7 @@ func (r *robolectricTest) DepsMutator(ctx android.BottomUpMutatorContext) { ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), roboRuntimesTag, "robolectric-android-all-prebuilts") - for _, lib := range r.robolectricProperties.Jni_libs { + for _, lib := range r.robolectricProperties.Jni_libs.GetOrDefault(ctx, nil) { ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib) } } diff --git a/java/rro_test.go b/java/rro_test.go index 742c83982..4d791305e 100644 --- a/java/rro_test.go +++ b/java/rro_test.go @@ -282,7 +282,7 @@ func TestEnforceRRO_propagatesToDependencies(t *testing.T) { enforceRROTargets: []string{"foo"}, rroDirs: map[string][]string{ "foo": {"product/vendor/blah/overlay/lib2/res"}, - "bar": {"product/vendor/blah/overlay/lib2/res"}, + "bar": nil, }, }, } diff --git a/java/sdk_library.go b/java/sdk_library.go index b7aa4e56d..f30877258 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -29,11 +29,6 @@ import ( "android/soong/android" "android/soong/dexpreopt" - "android/soong/etc" -) - -const ( - sdkXmlFileSuffix = ".xml" ) // A tag to associated a dependency with a specific api scope. @@ -248,7 +243,7 @@ func (scope *apiScope) apiLibraryModuleName(baseName string) string { return scope.stubsLibraryModuleName(baseName) + ".from-text" } -func (scope *apiScope) sourceStubLibraryModuleName(baseName string) string { +func (scope *apiScope) sourceStubsLibraryModuleName(baseName string) string { return scope.stubsLibraryModuleName(baseName) + ".from-source" } @@ -268,10 +263,6 @@ func (scope *apiScope) stubsSourceModuleName(baseName string) string { return baseName + ".stubs.source" + scope.moduleSuffix } -func (scope *apiScope) apiModuleName(baseName string) string { - return baseName + ".api" + scope.moduleSuffix -} - func (scope *apiScope) String() string { return scope.name } @@ -830,16 +821,6 @@ func (paths *scopePaths) extractLatestRemovedApiPath(ctx android.ModuleContext, } type commonToSdkLibraryAndImportProperties struct { - // The naming scheme to use for the components that this module creates. - // - // If not specified then it defaults to "default". - // - // This is a temporary mechanism to simplify conversion from separate modules for each - // component that follow a different naming pattern to the default one. - // - // TODO(b/155480189) - Remove once naming inconsistencies have been resolved. - Naming_scheme *string - // Specifies whether this module can be used as an Android shared library; defaults // to true. // @@ -915,8 +896,6 @@ type commonToSdkLibraryAndImport struct { scopePaths map[*apiScope]*scopePaths - namingScheme sdkLibraryComponentNamingScheme - commonSdkLibraryProperties commonToSdkLibraryAndImportProperties // Paths to commonSdkLibraryProperties.Doctag_files @@ -943,16 +922,7 @@ func (c *commonToSdkLibraryAndImport) initCommon(module commonSdkLibraryAndImpor c.initSdkLibraryComponent(module) } -func (c *commonToSdkLibraryAndImport) initCommonAfterDefaultsApplied(ctx android.DefaultableHookContext) bool { - schemeProperty := proptools.StringDefault(c.commonSdkLibraryProperties.Naming_scheme, "default") - switch schemeProperty { - case "default": - c.namingScheme = &defaultNamingScheme{} - default: - ctx.PropertyErrorf("naming_scheme", "expected 'default' but was %q", schemeProperty) - return false - } - +func (c *commonToSdkLibraryAndImport) initCommonAfterDefaultsApplied() bool { namePtr := proptools.StringPtr(c.module.RootLibraryName()) c.sdkLibraryComponentProperties.SdkLibraryName = namePtr @@ -974,62 +944,32 @@ func (c *commonToSdkLibraryAndImport) uniqueApexVariations() bool { return c.sharedLibrary() } -func (c *commonToSdkLibraryAndImport) generateCommonBuildActions(ctx android.ModuleContext) { +func (c *commonToSdkLibraryAndImport) generateCommonBuildActions(ctx android.ModuleContext) SdkLibraryInfo { c.doctagPaths = android.PathsForModuleSrc(ctx, c.commonSdkLibraryProperties.Doctag_files) -} - -func (c *commonToSdkLibraryAndImport) getImplLibraryModule() *Library { - return c.implLibraryModule -} - -// Module name of the runtime implementation library -func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string { - return c.module.RootLibraryName() + ".impl" -} - -// Module name of the XML file for the lib -func (c *commonToSdkLibraryAndImport) xmlPermissionsModuleName() string { - return c.module.RootLibraryName() + sdkXmlFileSuffix -} - -// Name of the java_library module that compiles the stubs source. -func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string { - baseName := c.module.RootLibraryName() - return c.namingScheme.stubsLibraryModuleName(apiScope, baseName) -} -// Name of the java_library module that compiles the exportable stubs source. -func (c *commonToSdkLibraryAndImport) exportableStubsLibraryModuleName(apiScope *apiScope) string { - baseName := c.module.RootLibraryName() - return c.namingScheme.exportableStubsLibraryModuleName(apiScope, baseName) -} - -// Name of the droidstubs module that generates the stubs source and may also -// generate/check the API. -func (c *commonToSdkLibraryAndImport) stubsSourceModuleName(apiScope *apiScope) string { - baseName := c.module.RootLibraryName() - return c.namingScheme.stubsSourceModuleName(apiScope, baseName) -} - -// Name of the java_api_library module that generates the from-text stubs source -// and compiles to a jar file. -func (c *commonToSdkLibraryAndImport) apiLibraryModuleName(apiScope *apiScope) string { - baseName := c.module.RootLibraryName() - return c.namingScheme.apiLibraryModuleName(apiScope, baseName) -} - -// Name of the java_library module that compiles the stubs -// generated from source Java files. -func (c *commonToSdkLibraryAndImport) sourceStubsLibraryModuleName(apiScope *apiScope) string { - baseName := c.module.RootLibraryName() - return c.namingScheme.sourceStubsLibraryModuleName(apiScope, baseName) -} + everythingStubPaths := make(map[android.SdkKind]OptionalDexJarPath) + exportableStubPaths := make(map[android.SdkKind]OptionalDexJarPath) + removedApiFilePaths := make(map[android.SdkKind]android.OptionalPath) + for kind := android.SdkNone; kind <= android.SdkPrivate; kind += 1 { + everythingStubPath := makeUnsetDexJarPath() + exportableStubPath := makeUnsetDexJarPath() + removedApiFilePath := android.OptionalPath{} + if scopePath := c.findClosestScopePath(sdkKindToApiScope(kind)); scopePath != nil { + everythingStubPath = scopePath.stubsDexJarPath + exportableStubPath = scopePath.exportableStubsDexJarPath + removedApiFilePath = scopePath.removedApiFilePath + } + everythingStubPaths[kind] = everythingStubPath + exportableStubPaths[kind] = exportableStubPath + removedApiFilePaths[kind] = removedApiFilePath + } -// Name of the java_library module that compiles the exportable stubs -// generated from source Java files. -func (c *commonToSdkLibraryAndImport) exportableSourceStubsLibraryModuleName(apiScope *apiScope) string { - baseName := c.module.RootLibraryName() - return c.namingScheme.exportableSourceStubsLibraryModuleName(apiScope, baseName) + return SdkLibraryInfo{ + EverythingStubDexJarPaths: everythingStubPaths, + ExportableStubDexJarPaths: exportableStubPaths, + RemovedTxtFiles: removedApiFilePaths, + SharedLibrary: c.sharedLibrary(), + } } // The component names for different outputs of the java_sdk_library. @@ -1103,44 +1043,6 @@ func (c *commonToSdkLibraryAndImport) findClosestScopePath(scope *apiScope) *sco return nil } -func (c *commonToSdkLibraryAndImport) selectHeaderJarsForSdkVersion(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths { - - // If a specific numeric version has been requested then use prebuilt versions of the sdk. - if !sdkVersion.ApiLevel.IsPreview() { - return PrebuiltJars(ctx, c.module.RootLibraryName(), sdkVersion) - } - - paths := c.selectScopePaths(ctx, sdkVersion.Kind) - if paths == nil { - return nil - } - - return paths.stubsHeaderPath -} - -// selectScopePaths returns the *scopePaths appropriate for the specific kind. -// -// If the module does not support the specific kind then it will return the *scopePaths for the -// closest kind which is a subset of the requested kind. e.g. if requesting android.SdkModule then -// it will return *scopePaths for android.SdkSystem if available or android.SdkPublic of not. -func (c *commonToSdkLibraryAndImport) selectScopePaths(ctx android.BaseModuleContext, kind android.SdkKind) *scopePaths { - apiScope := sdkKindToApiScope(kind) - - paths := c.findClosestScopePath(apiScope) - if paths == nil { - var scopes []string - for _, s := range AllApiScopes { - if c.findScopePaths(s) != nil { - scopes = append(scopes, s.name) - } - } - ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.module.RootLibraryName(), scopes) - return nil - } - - return paths -} - // sdkKindToApiScope maps from android.SdkKind to apiScope. func sdkKindToApiScope(kind android.SdkKind) *apiScope { var apiScope *apiScope @@ -1159,37 +1061,6 @@ func sdkKindToApiScope(kind android.SdkKind) *apiScope { return apiScope } -// to satisfy SdkLibraryDependency interface -func (c *commonToSdkLibraryAndImport) SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath { - paths := c.selectScopePaths(ctx, kind) - if paths == nil { - return makeUnsetDexJarPath() - } - - return paths.stubsDexJarPath -} - -// to satisfy SdkLibraryDependency interface -func (c *commonToSdkLibraryAndImport) SdkApiExportableStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath { - paths := c.selectScopePaths(ctx, kind) - if paths == nil { - return makeUnsetDexJarPath() - } - - return paths.exportableStubsDexJarPath -} - -// to satisfy SdkLibraryDependency interface -func (c *commonToSdkLibraryAndImport) SdkRemovedTxtFile(ctx android.BaseModuleContext, kind android.SdkKind) android.OptionalPath { - apiScope := sdkKindToApiScope(kind) - paths := c.findScopePaths(apiScope) - if paths == nil { - return android.OptionalPath{} - } - - return paths.removedApiFilePath -} - func (c *commonToSdkLibraryAndImport) sdkComponentPropertiesForChildLibrary() interface{} { componentProps := &struct { SdkLibraryName *string @@ -1280,33 +1151,43 @@ var _ SdkLibraryComponentDependency = (*Import)(nil) var _ SdkLibraryComponentDependency = (*SdkLibrary)(nil) var _ SdkLibraryComponentDependency = (*SdkLibraryImport)(nil) -// Provides access to sdk_version related files, e.g. header and implementation jars. -type SdkLibraryDependency interface { - SdkLibraryComponentDependency +type SdkLibraryInfo struct { + // GeneratingLibs is the names of the library modules that this sdk library + // generates. Note that this only includes the name of the modules that other modules can + // depend on, and is not a holistic list of generated modules. + GeneratingLibs []string - // Get the header jars appropriate for the supplied sdk_version. - // - // These are turbine generated jars so they only change if the externals of the - // class changes but it does not contain and implementation or JavaDoc. - SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths + // Map of sdk kind to the dex jar for the "everything" stubs. + // It is needed by the hiddenapi processing tool which processes dex files. + EverythingStubDexJarPaths map[android.SdkKind]OptionalDexJarPath - // SdkApiStubDexJar returns the dex jar for the stubs for the prebuilt - // java_sdk_library_import module. It is needed by the hiddenapi processing tool which - // processes dex files. - SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath + // Map of sdk kind to the dex jar for the "exportable" stubs. + // It is needed by the hiddenapi processing tool which processes dex files. + ExportableStubDexJarPaths map[android.SdkKind]OptionalDexJarPath - // SdkApiExportableStubDexJar returns the exportable dex jar for the stubs for - // java_sdk_library module. It is needed by the hiddenapi processing tool which processes - // dex files. - SdkApiExportableStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath + // Map of sdk kind to the optional path to the removed.txt file. + RemovedTxtFiles map[android.SdkKind]android.OptionalPath - // SdkRemovedTxtFile returns the optional path to the removed.txt file for the specified sdk kind. - SdkRemovedTxtFile(ctx android.BaseModuleContext, kind android.SdkKind) android.OptionalPath + // Whether if this can be used as a shared library. + SharedLibrary bool +} - // sharedLibrary returns true if this can be used as a shared library. - sharedLibrary() bool +var SdkLibraryInfoProvider = blueprint.NewProvider[SdkLibraryInfo]() - getImplLibraryModule() *Library +func getGeneratingLibs(ctx android.ModuleContext, sdkVersion android.SdkSpec, sdkLibraryModuleName string, sdkInfo SdkLibraryInfo) []string { + apiLevel := sdkVersion.ApiLevel + if apiLevel.IsPreview() { + return sdkInfo.GeneratingLibs + } + + generatingPrebuilts := []string{} + for _, apiScope := range AllApiScopes { + scopePrebuiltModuleName := prebuiltApiModuleName("sdk", sdkLibraryModuleName, apiScope.name, apiLevel.String()) + if ctx.OtherModuleExists(scopePrebuiltModuleName) { + generatingPrebuilts = append(generatingPrebuilts, scopePrebuiltModuleName) + } + } + return generatingPrebuilts } type SdkLibrary struct { @@ -1322,12 +1203,13 @@ type SdkLibrary struct { builtInstalledForApex []dexpreopterInstall } -var _ SdkLibraryDependency = (*SdkLibrary)(nil) - func (module *SdkLibrary) generateTestAndSystemScopesByDefault() bool { return module.sdkLibraryProperties.Generate_system_and_test_apis } +var _ UsesLibraryDependency = (*SdkLibrary)(nil) + +// To satisfy the UsesLibraryDependency interface func (module *SdkLibrary) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath { if module.implLibraryModule != nil { return module.implLibraryModule.DexJarBuildPath(ctx) @@ -1335,6 +1217,7 @@ func (module *SdkLibrary) DexJarBuildPath(ctx android.ModuleErrorfContext) Optio return makeUnsetDexJarPath() } +// To satisfy the UsesLibraryDependency interface func (module *SdkLibrary) DexJarInstallPath() android.Path { if module.implLibraryModule != nil { return module.implLibraryModule.DexJarInstallPath() @@ -1397,7 +1280,7 @@ func (module *SdkLibrary) CheckMinSdkVersion(ctx android.ModuleContext) { } func CheckMinSdkVersion(ctx android.ModuleContext, module *Library) { - android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx), func(c android.ModuleContext, do android.PayloadDepsCallback) { + android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx), func(c android.BaseModuleContext, do android.PayloadDepsCallback) { ctx.WalkDeps(func(child android.Module, parent android.Module) bool { isExternal := !module.depIsInSameApex(ctx, child) if am, ok := child.(android.ApexModule); ok { @@ -1452,7 +1335,7 @@ func (module *SdkLibrary) ComponentDepsMutator(ctx android.BottomUpMutatorContex ctx.AddVariationDependencies(nil, apiScope.exportableStubsTag, exportableStubModuleName) // Add a dependency on the stubs source in order to access both stubs source and api information. - ctx.AddVariationDependencies(nil, apiScope.stubsSourceAndApiTag, module.stubsSourceModuleName(apiScope)) + ctx.AddVariationDependencies(nil, apiScope.stubsSourceAndApiTag, module.droidstubsModuleName(apiScope)) if module.compareAgainstLatestApi(apiScope) { // Add dependencies on the latest finalized version of the API .txt file. @@ -1522,8 +1405,6 @@ func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) module.HideFromMake() } - module.generateCommonBuildActions(ctx) - module.stem = proptools.StringDefault(module.overridableProperties.Stem, ctx.ModuleName()) module.provideHiddenAPIPropertyInfo(ctx) @@ -1556,11 +1437,11 @@ func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) if dep, ok := android.OtherModuleProvider(ctx, to, JavaInfoProvider); ok { module.implLibraryHeaderJars = append(module.implLibraryHeaderJars, dep.HeaderJars...) module.implLibraryModule = to.(*Library) - android.SetProvider(ctx, JavaInfoProvider, dep) } } }) + sdkLibInfo := module.generateCommonBuildActions(ctx) apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) if !apexInfo.IsForPlatform() { module.hideApexVariantFromMake = true @@ -1589,7 +1470,10 @@ func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) module.dexer.proguardDictionary = module.implLibraryModule.dexer.proguardDictionary module.dexer.proguardUsageZip = module.implLibraryModule.dexer.proguardUsageZip module.linter.reports = module.implLibraryModule.linter.reports - module.linter.outputs.depSets = module.implLibraryModule.LintDepSets() + + if lintInfo, ok := android.OtherModuleProvider(ctx, module.implLibraryModule, LintProvider); ok { + android.SetProvider(ctx, LintProvider, lintInfo) + } if !module.Host() { module.hostdexInstallFile = module.implLibraryModule.hostdexInstallFile @@ -1631,9 +1515,21 @@ func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) } android.SetProvider(ctx, android.AdditionalSdkInfoProvider, android.AdditionalSdkInfo{additionalSdkInfo}) module.setOutputFiles(ctx) + + var generatingLibs []string + for _, apiScope := range AllApiScopes { + if _, ok := module.scopePaths[apiScope]; ok { + generatingLibs = append(generatingLibs, module.stubsLibraryModuleName(apiScope)) + } + } + if module.requiresRuntimeImplementationLibrary() && module.implLibraryModule != nil { + generatingLibs = append(generatingLibs, module.implLibraryModuleName()) setOutputFiles(ctx, module.implLibraryModule.Module) } + + sdkLibInfo.GeneratingLibs = generatingLibs + android.SetProvider(ctx, SdkLibraryInfoProvider, sdkLibInfo) } func (module *SdkLibrary) BuiltInstalledForApex() []dexpreopterInstall { @@ -1740,411 +1636,6 @@ func childModuleVisibility(childVisibility []string) []string { return visibility } -// Creates the implementation java library -func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) { - visibility := childModuleVisibility(module.sdkLibraryProperties.Impl_library_visibility) - - staticLibs := module.properties.Static_libs.Clone() - staticLibs.AppendSimpleValue(module.sdkLibraryProperties.Impl_only_static_libs) - props := struct { - Name *string - Visibility []string - Libs []string - Static_libs proptools.Configurable[[]string] - Apex_available []string - Stem *string - }{ - Name: proptools.StringPtr(module.implLibraryModuleName()), - Visibility: visibility, - - Libs: append(module.properties.Libs, module.sdkLibraryProperties.Impl_only_libs...), - - Static_libs: staticLibs, - // Pass the apex_available settings down so that the impl library can be statically - // embedded within a library that is added to an APEX. Needed for updatable-media. - Apex_available: module.ApexAvailable(), - - Stem: proptools.StringPtr(module.Name()), - } - - properties := []interface{}{ - &module.properties, - &module.protoProperties, - &module.deviceProperties, - &module.dexProperties, - &module.dexpreoptProperties, - &module.linter.properties, - &module.overridableProperties, - &props, - module.sdkComponentPropertiesForChildLibrary(), - } - mctx.CreateModule(LibraryFactory, properties...) -} - -type libraryProperties struct { - Name *string - Visibility []string - Srcs []string - Installable *bool - Sdk_version *string - System_modules *string - Patch_module *string - Libs []string - Static_libs []string - Compile_dex *bool - Java_version *string - Openjdk9 struct { - Srcs []string - Javacflags []string - } - Dist struct { - Targets []string - Dest *string - Dir *string - Tag *string - } - Is_stubs_module *bool - Stub_contributing_api *string -} - -func (module *SdkLibrary) stubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope) libraryProperties { - props := libraryProperties{} - props.Visibility = []string{"//visibility:override", "//visibility:private"} - // sources are generated from the droiddoc - sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope) - props.Sdk_version = proptools.StringPtr(sdkVersion) - props.System_modules = module.deviceProperties.System_modules - props.Patch_module = module.properties.Patch_module - props.Installable = proptools.BoolPtr(false) - props.Libs = module.sdkLibraryProperties.Stub_only_libs - props.Libs = append(props.Libs, module.scopeToProperties[apiScope].Libs...) - props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs - // The stub-annotations library contains special versions of the annotations - // with CLASS retention policy, so that they're kept. - if proptools.Bool(module.sdkLibraryProperties.Annotations_enabled) { - props.Libs = append(props.Libs, "stub-annotations") - } - props.Openjdk9.Srcs = module.properties.Openjdk9.Srcs - props.Openjdk9.Javacflags = module.properties.Openjdk9.Javacflags - // We compile the stubs for 1.8 in line with the main android.jar stubs, and potential - // interop with older developer tools that don't support 1.9. - props.Java_version = proptools.StringPtr("1.8") - props.Is_stubs_module = proptools.BoolPtr(true) - props.Stub_contributing_api = proptools.StringPtr(apiScope.kind.String()) - - return props -} - -// Creates a static java library that has API stubs -func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) { - - props := module.stubsLibraryProps(mctx, apiScope) - props.Name = proptools.StringPtr(module.sourceStubsLibraryModuleName(apiScope)) - props.Srcs = []string{":" + module.stubsSourceModuleName(apiScope)} - - mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) -} - -// Create a static java library that compiles the "exportable" stubs -func (module *SdkLibrary) createExportableStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) { - props := module.stubsLibraryProps(mctx, apiScope) - props.Name = proptools.StringPtr(module.exportableSourceStubsLibraryModuleName(apiScope)) - props.Srcs = []string{":" + module.stubsSourceModuleName(apiScope) + "{.exportable}"} - - mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) -} - -// Creates a droidstubs module that creates stubs source files from the given full source -// files and also updates and checks the API specification files. -func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookContext, apiScope *apiScope, name string, scopeSpecificDroidstubsArgs []string) { - props := struct { - Name *string - Visibility []string - Srcs []string - Installable *bool - Sdk_version *string - Api_surface *string - System_modules *string - Libs proptools.Configurable[[]string] - Output_javadoc_comments *bool - Arg_files []string - Args *string - Java_version *string - Annotations_enabled *bool - Merge_annotations_dirs []string - Merge_inclusion_annotations_dirs []string - Generate_stubs *bool - Previous_api *string - Aconfig_declarations []string - Check_api struct { - Current ApiToCheck - Last_released ApiToCheck - - Api_lint struct { - Enabled *bool - New_since *string - Baseline_file *string - } - } - Aidl struct { - Include_dirs []string - Local_include_dirs []string - } - Dists []android.Dist - }{} - - // The stubs source processing uses the same compile time classpath when extracting the - // API from the implementation library as it does when compiling it. i.e. the same - // * sdk version - // * system_modules - // * libs (static_libs/libs) - - props.Name = proptools.StringPtr(name) - props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_source_visibility) - props.Srcs = append(props.Srcs, module.properties.Srcs...) - props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...) - props.Sdk_version = module.deviceProperties.Sdk_version - props.Api_surface = &apiScope.name - props.System_modules = module.deviceProperties.System_modules - props.Installable = proptools.BoolPtr(false) - // A droiddoc module has only one Libs property and doesn't distinguish between - // shared libs and static libs. So we need to add both of these libs to Libs property. - props.Libs = proptools.NewConfigurable[[]string](nil, nil) - props.Libs.AppendSimpleValue(module.properties.Libs) - props.Libs.Append(module.properties.Static_libs) - props.Libs.AppendSimpleValue(module.sdkLibraryProperties.Stub_only_libs) - props.Libs.AppendSimpleValue(module.scopeToProperties[apiScope].Libs) - props.Aidl.Include_dirs = module.deviceProperties.Aidl.Include_dirs - props.Aidl.Local_include_dirs = module.deviceProperties.Aidl.Local_include_dirs - props.Java_version = module.properties.Java_version - - props.Annotations_enabled = module.sdkLibraryProperties.Annotations_enabled - props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs - props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs - props.Aconfig_declarations = module.sdkLibraryProperties.Aconfig_declarations - - droidstubsArgs := []string{} - if len(module.sdkLibraryProperties.Api_packages) != 0 { - droidstubsArgs = append(droidstubsArgs, "--stub-packages "+strings.Join(module.sdkLibraryProperties.Api_packages, ":")) - } - droidstubsArgs = append(droidstubsArgs, module.sdkLibraryProperties.Droiddoc_options...) - disabledWarnings := []string{"HiddenSuperclass"} - if proptools.BoolDefault(module.sdkLibraryProperties.Api_lint.Legacy_errors_allowed, true) { - disabledWarnings = append(disabledWarnings, - "BroadcastBehavior", - "DeprecationMismatch", - "MissingPermission", - "SdkConstant", - "Todo", - ) - } - droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide ")) - - // Output Javadoc comments for public scope. - if apiScope == apiScopePublic { - props.Output_javadoc_comments = proptools.BoolPtr(true) - } - - // Add in scope specific arguments. - droidstubsArgs = append(droidstubsArgs, scopeSpecificDroidstubsArgs...) - props.Arg_files = module.sdkLibraryProperties.Droiddoc_option_files - props.Args = proptools.StringPtr(strings.Join(droidstubsArgs, " ")) - - // List of APIs identified from the provided source files are created. They are later - // compared against to the not-yet-released (a.k.a current) list of APIs and to the - // last-released (a.k.a numbered) list of API. - currentApiFileName := apiScope.apiFilePrefix + "current.txt" - removedApiFileName := apiScope.apiFilePrefix + "removed.txt" - apiDir := module.getApiDir() - currentApiFileName = path.Join(apiDir, currentApiFileName) - removedApiFileName = path.Join(apiDir, removedApiFileName) - - // check against the not-yet-release API - props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName) - props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName) - - if module.compareAgainstLatestApi(apiScope) { - // check against the latest released API - latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope)) - props.Previous_api = latestApiFilegroupName - props.Check_api.Last_released.Api_file = latestApiFilegroupName - props.Check_api.Last_released.Removed_api_file = proptools.StringPtr( - module.latestRemovedApiFilegroupName(apiScope)) - props.Check_api.Last_released.Baseline_file = proptools.StringPtr( - module.latestIncompatibilitiesFilegroupName(apiScope)) - - if proptools.Bool(module.sdkLibraryProperties.Api_lint.Enabled) { - // Enable api lint. - props.Check_api.Api_lint.Enabled = proptools.BoolPtr(true) - props.Check_api.Api_lint.New_since = latestApiFilegroupName - - // If it exists then pass a lint-baseline.txt through to droidstubs. - baselinePath := path.Join(apiDir, apiScope.apiFilePrefix+"lint-baseline.txt") - baselinePathRelativeToRoot := path.Join(mctx.ModuleDir(), baselinePath) - paths, err := mctx.GlobWithDeps(baselinePathRelativeToRoot, nil) - if err != nil { - mctx.ModuleErrorf("error checking for presence of %s: %s", baselinePathRelativeToRoot, err) - } - if len(paths) == 1 { - props.Check_api.Api_lint.Baseline_file = proptools.StringPtr(baselinePath) - } else if len(paths) != 0 { - mctx.ModuleErrorf("error checking for presence of %s: expected one path, found: %v", baselinePathRelativeToRoot, paths) - } - } - } - - if !Bool(module.sdkLibraryProperties.No_dist) { - // Dist the api txt and removed api txt artifacts for sdk builds. - distDir := proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api")) - stubsTypeTagPrefix := "" - if mctx.Config().ReleaseHiddenApiExportableStubs() { - stubsTypeTagPrefix = ".exportable" - } - for _, p := range []struct { - tag string - pattern string - }{ - // "exportable" api files are copied to the dist directory instead of the - // "everything" api files when "RELEASE_HIDDEN_API_EXPORTABLE_STUBS" build flag - // is set. Otherwise, the "everything" api files are copied to the dist directory. - {tag: "%s.api.txt", pattern: "%s.txt"}, - {tag: "%s.removed-api.txt", pattern: "%s-removed.txt"}, - } { - props.Dists = append(props.Dists, android.Dist{ - Targets: []string{"sdk", "win_sdk"}, - Dir: distDir, - Dest: proptools.StringPtr(fmt.Sprintf(p.pattern, module.distStem())), - Tag: proptools.StringPtr(fmt.Sprintf(p.tag, stubsTypeTagPrefix)), - }) - } - } - - mctx.CreateModule(DroidstubsFactory, &props, module.sdkComponentPropertiesForChildLibrary()).(*Droidstubs).CallHookIfAvailable(mctx) -} - -func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) { - props := struct { - Name *string - Visibility []string - Api_contributions []string - Libs proptools.Configurable[[]string] - Static_libs []string - System_modules *string - Enable_validation *bool - Stubs_type *string - Sdk_version *string - Previous_api *string - }{} - - props.Name = proptools.StringPtr(module.apiLibraryModuleName(apiScope)) - props.Visibility = []string{"//visibility:override", "//visibility:private"} - - apiContributions := []string{} - - // Api surfaces are not independent of each other, but have subset relationships, - // and so does the api files. To generate from-text stubs for api surfaces other than public, - // all subset api domains' api_contriubtions must be added as well. - scope := apiScope - for scope != nil { - apiContributions = append(apiContributions, module.stubsSourceModuleName(scope)+".api.contribution") - scope = scope.extends - } - if apiScope == apiScopePublic { - additionalApiContribution := module.apiLibraryAdditionalApiContribution() - if additionalApiContribution != "" { - apiContributions = append(apiContributions, additionalApiContribution) - } - } - - props.Api_contributions = apiContributions - - // Ensure that stub-annotations is added to the classpath before any other libs - props.Libs = proptools.NewConfigurable[[]string](nil, nil) - props.Libs.AppendSimpleValue([]string{"stub-annotations"}) - props.Libs.AppendSimpleValue(module.properties.Libs) - props.Libs.Append(module.properties.Static_libs) - props.Libs.AppendSimpleValue(module.sdkLibraryProperties.Stub_only_libs) - props.Libs.AppendSimpleValue(module.scopeToProperties[apiScope].Libs) - props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs - - props.System_modules = module.deviceProperties.System_modules - props.Enable_validation = proptools.BoolPtr(true) - props.Stubs_type = proptools.StringPtr("everything") - - if module.deviceProperties.Sdk_version != nil { - props.Sdk_version = module.deviceProperties.Sdk_version - } - - if module.compareAgainstLatestApi(apiScope) { - // check against the latest released API - latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope)) - props.Previous_api = latestApiFilegroupName - } - - mctx.CreateModule(ApiLibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) -} - -func (module *SdkLibrary) topLevelStubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope, doDist bool) libraryProperties { - props := libraryProperties{} - - props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_library_visibility) - sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope) - props.Sdk_version = proptools.StringPtr(sdkVersion) - - props.System_modules = module.deviceProperties.System_modules - - // The imports need to be compiled to dex if the java_sdk_library requests it. - compileDex := module.dexProperties.Compile_dex - if module.stubLibrariesCompiledForDex() { - compileDex = proptools.BoolPtr(true) - } - props.Compile_dex = compileDex - - props.Stub_contributing_api = proptools.StringPtr(apiScope.kind.String()) - - if !Bool(module.sdkLibraryProperties.No_dist) && doDist { - props.Dist.Targets = []string{"sdk", "win_sdk"} - props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.distStem())) - props.Dist.Dir = proptools.StringPtr(module.apiDistPath(apiScope)) - props.Dist.Tag = proptools.StringPtr(".jar") - } - props.Is_stubs_module = proptools.BoolPtr(true) - - return props -} - -func (module *SdkLibrary) createTopLevelStubsLibrary( - mctx android.DefaultableHookContext, apiScope *apiScope) { - - // Dist the "everything" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is false - doDist := !mctx.Config().ReleaseHiddenApiExportableStubs() - props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist) - props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope)) - - // Add the stub compiling java_library/java_api_library as static lib based on build config - staticLib := module.sourceStubsLibraryModuleName(apiScope) - if mctx.Config().BuildFromTextStub() && module.ModuleBuildFromTextStubs() { - staticLib = module.apiLibraryModuleName(apiScope) - } - props.Static_libs = append(props.Static_libs, staticLib) - - mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) -} - -func (module *SdkLibrary) createTopLevelExportableStubsLibrary( - mctx android.DefaultableHookContext, apiScope *apiScope) { - - // Dist the "exportable" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is true - doDist := mctx.Config().ReleaseHiddenApiExportableStubs() - props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist) - props.Name = proptools.StringPtr(module.exportableStubsLibraryModuleName(apiScope)) - - staticLib := module.exportableSourceStubsLibraryModuleName(apiScope) - props.Static_libs = append(props.Static_libs, staticLib) - - mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) -} - func (module *SdkLibrary) compareAgainstLatestApi(apiScope *apiScope) bool { return !(apiScope.unstable || module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api) } @@ -2170,104 +1661,6 @@ func (module *SdkLibrary) ModuleBuildFromTextStubs() bool { return proptools.BoolDefault(module.sdkLibraryProperties.Build_from_text_stub, true) } -// Creates the xml file that publicizes the runtime library -func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) { - moduleMinApiLevel := module.Library.MinSdkVersion(mctx) - var moduleMinApiLevelStr = moduleMinApiLevel.String() - if moduleMinApiLevel == android.NoneApiLevel { - moduleMinApiLevelStr = "current" - } - props := struct { - Name *string - Lib_name *string - Apex_available []string - On_bootclasspath_since *string - On_bootclasspath_before *string - Min_device_sdk *string - Max_device_sdk *string - Sdk_library_min_api_level *string - Uses_libs_dependencies []string - }{ - Name: proptools.StringPtr(module.xmlPermissionsModuleName()), - Lib_name: proptools.StringPtr(module.BaseModuleName()), - Apex_available: module.ApexProperties.Apex_available, - On_bootclasspath_since: module.commonSdkLibraryProperties.On_bootclasspath_since, - On_bootclasspath_before: module.commonSdkLibraryProperties.On_bootclasspath_before, - Min_device_sdk: module.commonSdkLibraryProperties.Min_device_sdk, - Max_device_sdk: module.commonSdkLibraryProperties.Max_device_sdk, - Sdk_library_min_api_level: &moduleMinApiLevelStr, - Uses_libs_dependencies: module.usesLibraryProperties.Uses_libs, - } - - mctx.CreateModule(sdkLibraryXmlFactory, &props) -} - -func PrebuiltJars(ctx android.BaseModuleContext, baseName string, s android.SdkSpec) android.Paths { - var ver android.ApiLevel - var kind android.SdkKind - if s.UsePrebuilt(ctx) { - ver = s.ApiLevel - kind = s.Kind - } else { - // We don't have prebuilt SDK for the specific sdkVersion. - // Instead of breaking the build, fallback to use "system_current" - ver = android.FutureApiLevel - kind = android.SdkSystem - } - - dir := filepath.Join("prebuilts", "sdk", ver.String(), kind.String()) - jar := filepath.Join(dir, baseName+".jar") - jarPath := android.ExistentPathForSource(ctx, jar) - if !jarPath.Valid() { - if ctx.Config().AllowMissingDependencies() { - return android.Paths{android.PathForSource(ctx, jar)} - } else { - ctx.PropertyErrorf("sdk_library", "invalid sdk version %q, %q does not exist", s.Raw, jar) - } - return nil - } - return android.Paths{jarPath.Path()} -} - -// Check to see if the other module is within the same set of named APEXes as this module. -// -// If either this or the other module are on the platform then this will return -// false. -func withinSameApexesAs(ctx android.BaseModuleContext, other android.Module) bool { - apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) - otherApexInfo, _ := android.OtherModuleProvider(ctx, other, android.ApexInfoProvider) - return len(otherApexInfo.InApexVariants) > 0 && reflect.DeepEqual(apexInfo.InApexVariants, otherApexInfo.InApexVariants) -} - -func (module *SdkLibrary) sdkJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths { - // If the client doesn't set sdk_version, but if this library prefers stubs over - // the impl library, let's provide the widest API surface possible. To do so, - // force override sdk_version to module_current so that the closest possible API - // surface could be found in selectHeaderJarsForSdkVersion - if module.defaultsToStubs() && !sdkVersion.Specified() { - sdkVersion = android.SdkSpecFrom(ctx, "module_current") - } - - // Only provide access to the implementation library if it is actually built. - if module.requiresRuntimeImplementationLibrary() { - // Check any special cases for java_sdk_library. - // - // Only allow access to the implementation library in the following condition: - // * No sdk_version specified on the referencing module. - // * The referencing module is in the same apex as this. - if sdkVersion.Kind == android.SdkPrivate || withinSameApexesAs(ctx, module) { - return module.implLibraryHeaderJars - } - } - - return module.selectHeaderJarsForSdkVersion(ctx, sdkVersion) -} - -// to satisfy SdkLibraryDependency interface -func (module *SdkLibrary) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths { - return module.sdkJars(ctx, sdkVersion) -} - var javaSdkLibrariesKey = android.NewOnceKey("javaSdkLibraries") func javaSdkLibraries(config android.Config) *[]string { @@ -2284,11 +1677,6 @@ func (module *SdkLibrary) getApiDir() string { // runtime libs and xml file. If requested, the stubs and docs are created twice // once for public API level and once for system API level func (module *SdkLibrary) CreateInternalModules(mctx android.DefaultableHookContext) { - // If the module has been disabled then don't create any child modules. - if !module.Enabled(mctx) { - return - } - if len(module.properties.Srcs) == 0 { mctx.PropertyErrorf("srcs", "java_sdk_library must specify srcs") return @@ -2338,10 +1726,10 @@ func (module *SdkLibrary) CreateInternalModules(mctx android.DefaultableHookCont for _, scope := range generatedScopes { // Use the stubs source name for legacy reasons. - module.createStubsSourcesAndApi(mctx, scope, module.stubsSourceModuleName(scope), scope.droidstubsArgs) + module.createDroidstubs(mctx, scope, module.droidstubsModuleName(scope), scope.droidstubsArgs) - module.createStubsLibrary(mctx, scope) - module.createExportableStubsLibrary(mctx, scope) + module.createFromSourceStubsLibrary(mctx, scope) + module.createExportableFromSourceStubsLibrary(mctx, scope) if mctx.Config().BuildFromTextStub() && module.ModuleBuildFromTextStubs() { module.createApiLibrary(mctx, scope) @@ -2391,54 +1779,6 @@ func (module *SdkLibrary) requiresRuntimeImplementationLibrary() bool { return !proptools.Bool(module.sdkLibraryProperties.Api_only) } -func (module *SdkLibrary) defaultsToStubs() bool { - return proptools.Bool(module.sdkLibraryProperties.Default_to_stubs) -} - -// Defines how to name the individual component modules the sdk library creates. -type sdkLibraryComponentNamingScheme interface { - stubsLibraryModuleName(scope *apiScope, baseName string) string - - stubsSourceModuleName(scope *apiScope, baseName string) string - - apiLibraryModuleName(scope *apiScope, baseName string) string - - sourceStubsLibraryModuleName(scope *apiScope, baseName string) string - - exportableStubsLibraryModuleName(scope *apiScope, baseName string) string - - exportableSourceStubsLibraryModuleName(scope *apiScope, baseName string) string -} - -type defaultNamingScheme struct { -} - -func (s *defaultNamingScheme) stubsLibraryModuleName(scope *apiScope, baseName string) string { - return scope.stubsLibraryModuleName(baseName) -} - -func (s *defaultNamingScheme) stubsSourceModuleName(scope *apiScope, baseName string) string { - return scope.stubsSourceModuleName(baseName) -} - -func (s *defaultNamingScheme) apiLibraryModuleName(scope *apiScope, baseName string) string { - return scope.apiLibraryModuleName(baseName) -} - -func (s *defaultNamingScheme) sourceStubsLibraryModuleName(scope *apiScope, baseName string) string { - return scope.sourceStubLibraryModuleName(baseName) -} - -func (s *defaultNamingScheme) exportableStubsLibraryModuleName(scope *apiScope, baseName string) string { - return scope.exportableStubsLibraryModuleName(baseName) -} - -func (s *defaultNamingScheme) exportableSourceStubsLibraryModuleName(scope *apiScope, baseName string) string { - return scope.exportableSourceStubsLibraryModuleName(baseName) -} - -var _ sdkLibraryComponentNamingScheme = (*defaultNamingScheme)(nil) - func moduleStubLinkType(j *Module) (stub bool, ret sdkLinkType) { kind := android.ToSdkKind(proptools.String(j.properties.Stub_contributing_api)) switch kind { @@ -2500,7 +1840,7 @@ func SdkLibraryFactory() android.Module { module.commonSdkLibraryProperties.Shared_library = proptools.BoolPtr(false) } - if module.initCommonAfterDefaultsApplied(ctx) { + if module.initCommonAfterDefaultsApplied() { module.CreateInternalModules(ctx) } }) @@ -2579,8 +1919,6 @@ type SdkLibraryImport struct { installFile android.Path } -var _ SdkLibraryDependency = (*SdkLibraryImport)(nil) - // The type of a structure that contains a field of type sdkLibraryScopeProperties // for each apiscope in allApiScopes, e.g. something like: // @@ -2636,7 +1974,7 @@ func sdkLibraryImportFactory() android.Module { InitJavaModule(module, android.HostAndDeviceSupported) module.SetDefaultableHook(func(mctx android.DefaultableHookContext) { - if module.initCommonAfterDefaultsApplied(mctx) { + if module.initCommonAfterDefaultsApplied() { module.createInternalModules(mctx) } }) @@ -2690,86 +2028,6 @@ func (module *SdkLibraryImport) createInternalModules(mctx android.DefaultableHo *javaSdkLibraries = append(*javaSdkLibraries, module.BaseModuleName()) } -func (module *SdkLibraryImport) createJavaImportForStubs(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) { - // Creates a java import for the jar with ".stubs" suffix - props := struct { - Name *string - Source_module_name *string - Created_by_java_sdk_library_name *string - Sdk_version *string - Libs []string - Jars []string - Compile_dex *bool - Is_stubs_module *bool - - android.UserSuppliedPrebuiltProperties - }{} - props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope)) - props.Source_module_name = proptools.StringPtr(apiScope.stubsLibraryModuleName(module.BaseModuleName())) - props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName()) - props.Sdk_version = scopeProperties.Sdk_version - // Prepend any of the libs from the legacy public properties to the libs for each of the - // scopes to avoid having to duplicate them in each scope. - props.Libs = append(module.properties.Libs, scopeProperties.Libs...) - props.Jars = scopeProperties.Jars - - // The imports are preferred if the java_sdk_library_import is preferred. - props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt) - - // The imports need to be compiled to dex if the java_sdk_library_import requests it. - compileDex := module.properties.Compile_dex - if module.stubLibrariesCompiledForDex() { - compileDex = proptools.BoolPtr(true) - } - props.Compile_dex = compileDex - props.Is_stubs_module = proptools.BoolPtr(true) - - mctx.CreateModule(ImportFactory, &props, module.sdkComponentPropertiesForChildLibrary()) -} - -func (module *SdkLibraryImport) createPrebuiltStubsSources(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) { - props := struct { - Name *string - Source_module_name *string - Created_by_java_sdk_library_name *string - Srcs []string - - android.UserSuppliedPrebuiltProperties - }{} - props.Name = proptools.StringPtr(module.stubsSourceModuleName(apiScope)) - props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName())) - props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName()) - props.Srcs = scopeProperties.Stub_srcs - - // The stubs source is preferred if the java_sdk_library_import is preferred. - props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt) - - mctx.CreateModule(PrebuiltStubsSourcesFactory, &props, module.sdkComponentPropertiesForChildLibrary()) -} - -func (module *SdkLibraryImport) createPrebuiltApiContribution(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) { - api_file := scopeProperties.Current_api - api_surface := &apiScope.name - - props := struct { - Name *string - Source_module_name *string - Created_by_java_sdk_library_name *string - Api_surface *string - Api_file *string - Visibility []string - }{} - - props.Name = proptools.StringPtr(module.stubsSourceModuleName(apiScope) + ".api.contribution") - props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()) + ".api.contribution") - props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName()) - props.Api_surface = api_surface - props.Api_file = api_file - props.Visibility = []string{"//visibility:override", "//visibility:public"} - - mctx.CreateModule(ApiContributionImportFactory, &props, module.sdkComponentPropertiesForChildLibrary()) -} - // Add the dependencies on the child module in the component deps mutator so that it // creates references to the prebuilt and not the source modules. func (module *SdkLibraryImport) ComponentDepsMutator(ctx android.BottomUpMutatorContext) { @@ -2783,7 +2041,7 @@ func (module *SdkLibraryImport) ComponentDepsMutator(ctx android.BottomUpMutator if len(scopeProperties.Stub_srcs) > 0 { // Add dependencies to the prebuilt stubs source library - ctx.AddVariationDependencies(nil, apiScope.stubsSourceTag, android.PrebuiltNameFromSource(module.stubsSourceModuleName(apiScope))) + ctx.AddVariationDependencies(nil, apiScope.stubsSourceTag, android.PrebuiltNameFromSource(module.droidstubsModuleName(apiScope))) } } } @@ -2837,8 +2095,6 @@ func (module *SdkLibraryImport) MinSdkVersion(ctx android.EarlyModuleContext) an var _ hiddenAPIModule = (*SdkLibraryImport)(nil) func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { - module.generateCommonBuildActions(ctx) - // Assume that source module(sdk_library) is installed in /<sdk_library partition>/framework module.installFile = android.PathForModuleInstall(ctx, "framework", module.Stem()+".jar") @@ -2868,6 +2124,7 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo } } }) + sdkLibInfo := module.generateCommonBuildActions(ctx) // Populate the scope paths with information from the properties. for apiScope, scopeProperties := range module.scopeProperties { @@ -2882,70 +2139,38 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo } if ctx.Device() { - // If this is a variant created for a prebuilt_apex then use the dex implementation jar - // obtained from the associated deapexer module. + // Shared libraries deapexed from prebuilt apexes are no longer supported. + // Set the dexJarBuildPath to a fake path. + // This allows soong analysis pass, but will be an error during ninja execution if there are + // any rdeps. ai, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) if ai.ForPrebuiltApex { - // Get the path of the dex implementation jar from the `deapexer` module. - di, err := android.FindDeapexerProviderForModule(ctx) - if err != nil { - // An error was found, possibly due to multiple apexes in the tree that export this library - // Defer the error till a client tries to call DexJarBuildPath - module.dexJarFileErr = err - module.initHiddenAPIError(err) - return - } - dexJarFileApexRootRelative := ApexRootRelativePathToJavaLib(module.BaseModuleName()) - if dexOutputPath := di.PrebuiltExportPath(dexJarFileApexRootRelative); dexOutputPath != nil { - dexJarFile := makeDexJarPathFromPath(dexOutputPath) - module.dexJarFile = dexJarFile - installPath := android.PathForModuleInPartitionInstall( - ctx, "apex", ai.ApexVariationName, dexJarFileApexRootRelative) - module.installFile = installPath - module.initHiddenAPI(ctx, dexJarFile, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil) - - module.dexpreopter.installPath = module.dexpreopter.getInstallPath(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), installPath) - module.dexpreopter.isSDKLibrary = true - module.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), &module.dexpreopter) - - if profilePath := di.PrebuiltExportPath(dexJarFileApexRootRelative + ".prof"); profilePath != nil { - module.dexpreopter.inputProfilePathOnHost = profilePath - } - } else { - // This should never happen as a variant for a prebuilt_apex is only created if the - // prebuilt_apex has been configured to export the java library dex file. - ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt APEX %s", di.ApexModuleName()) + module.dexJarFile = makeDexJarPathFromPath(android.PathForModuleInstall(ctx, "intentionally_no_longer_supported")) + module.initHiddenAPI(ctx, module.dexJarFile, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil) + } + } + + var generatingLibs []string + for _, apiScope := range AllApiScopes { + if scopeProperties, ok := module.scopeProperties[apiScope]; ok { + if len(scopeProperties.Jars) == 0 { + continue } + generatingLibs = append(generatingLibs, module.stubsLibraryModuleName(apiScope)) } } module.setOutputFiles(ctx) if module.implLibraryModule != nil { + generatingLibs = append(generatingLibs, module.implLibraryModuleName()) setOutputFiles(ctx, module.implLibraryModule.Module) } -} - -func (module *SdkLibraryImport) sdkJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec, headerJars bool) android.Paths { - // For consistency with SdkLibrary make the implementation jar available to libraries that - // are within the same APEX. - implLibraryModule := module.implLibraryModule - if implLibraryModule != nil && withinSameApexesAs(ctx, module) { - if headerJars { - return implLibraryModule.HeaderJars() - } else { - return implLibraryModule.ImplementationJars() - } - } - - return module.selectHeaderJarsForSdkVersion(ctx, sdkVersion) + sdkLibInfo.GeneratingLibs = generatingLibs + android.SetProvider(ctx, SdkLibraryInfoProvider, sdkLibInfo) } -// to satisfy SdkLibraryDependency interface -func (module *SdkLibraryImport) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths { - // This module is just a wrapper for the prebuilt stubs. - return module.sdkJars(ctx, sdkVersion, true) -} +var _ UsesLibraryDependency = (*SdkLibraryImport)(nil) // to satisfy UsesLibraryDependency interface func (module *SdkLibraryImport) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath { @@ -2984,29 +2209,6 @@ func (module *SdkLibraryImport) JacocoReportClassesFile() android.Path { } // to satisfy apex.javaDependency interface -func (module *SdkLibraryImport) LintDepSets() LintDepSets { - if module.implLibraryModule == nil { - return LintDepSets{} - } else { - return module.implLibraryModule.LintDepSets() - } -} - -func (module *SdkLibraryImport) GetStrictUpdatabilityLinting() bool { - if module.implLibraryModule == nil { - return false - } else { - return module.implLibraryModule.GetStrictUpdatabilityLinting() - } -} - -func (module *SdkLibraryImport) SetStrictUpdatabilityLinting(strictLinting bool) { - if module.implLibraryModule != nil { - module.implLibraryModule.SetStrictUpdatabilityLinting(strictLinting) - } -} - -// to satisfy apex.javaDependency interface func (module *SdkLibraryImport) Stem() string { return module.BaseModuleName() } @@ -3047,333 +2249,6 @@ func (j *SdkLibraryImport) UseProfileGuidedDexpreopt() bool { return proptools.Bool(j.importDexpreoptProperties.Dex_preopt.Profile_guided) } -// java_sdk_library_xml -type sdkLibraryXml struct { - android.ModuleBase - android.DefaultableModuleBase - android.ApexModuleBase - - properties sdkLibraryXmlProperties - - outputFilePath android.OutputPath - installDirPath android.InstallPath - - hideApexVariantFromMake bool -} - -type sdkLibraryXmlProperties struct { - // canonical name of the lib - Lib_name *string - - // Signals that this shared library is part of the bootclasspath starting - // on the version indicated in this attribute. - // - // This will make platforms at this level and above to ignore - // <uses-library> tags with this library name because the library is already - // available - On_bootclasspath_since *string - - // Signals that this shared library was part of the bootclasspath before - // (but not including) the version indicated in this attribute. - // - // The system will automatically add a <uses-library> tag with this library to - // apps that target any SDK less than the version indicated in this attribute. - On_bootclasspath_before *string - - // Indicates that PackageManager should ignore this shared library if the - // platform is below the version indicated in this attribute. - // - // This means that the device won't recognise this library as installed. - Min_device_sdk *string - - // Indicates that PackageManager should ignore this shared library if the - // platform is above the version indicated in this attribute. - // - // This means that the device won't recognise this library as installed. - Max_device_sdk *string - - // The SdkLibrary's min api level as a string - // - // This value comes from the ApiLevel of the MinSdkVersion property. - Sdk_library_min_api_level *string - - // Uses-libs dependencies that the shared library requires to work correctly. - // - // This will add dependency="foo:bar" to the <library> section. - Uses_libs_dependencies []string -} - -// java_sdk_library_xml builds the permission xml file for a java_sdk_library. -// Not to be used directly by users. java_sdk_library internally uses this. -func sdkLibraryXmlFactory() android.Module { - module := &sdkLibraryXml{} - - module.AddProperties(&module.properties) - - android.InitApexModule(module) - android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) - - return module -} - -func (module *sdkLibraryXml) UniqueApexVariations() bool { - // sdkLibraryXml needs a unique variation per APEX because the generated XML file contains the path to the - // mounted APEX, which contains the name of the APEX. - return true -} - -// from android.PrebuiltEtcModule -func (module *sdkLibraryXml) BaseDir() string { - return "etc" -} - -// from android.PrebuiltEtcModule -func (module *sdkLibraryXml) SubDir() string { - return "permissions" -} - -var _ etc.PrebuiltEtcModule = (*sdkLibraryXml)(nil) - -// from android.ApexModule -func (module *sdkLibraryXml) AvailableFor(what string) bool { - return true -} - -func (module *sdkLibraryXml) DepsMutator(ctx android.BottomUpMutatorContext) { - // do nothing -} - -var _ android.ApexModule = (*sdkLibraryXml)(nil) - -// Implements android.ApexModule -func (module *sdkLibraryXml) ShouldSupportSdkVersion(ctx android.BaseModuleContext, - sdkVersion android.ApiLevel) error { - // sdkLibraryXml doesn't need to be checked separately because java_sdk_library is checked - return nil -} - -// File path to the runtime implementation library -func (module *sdkLibraryXml) implPath(ctx android.ModuleContext) string { - implName := proptools.String(module.properties.Lib_name) - if apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider); !apexInfo.IsForPlatform() { - // TODO(b/146468504): ApexVariationName() is only a soong module name, not apex name. - // In most cases, this works fine. But when apex_name is set or override_apex is used - // this can be wrong. - return fmt.Sprintf("/apex/%s/javalib/%s.jar", apexInfo.BaseApexName, implName) - } - partition := "system" - if module.SocSpecific() { - partition = "vendor" - } else if module.DeviceSpecific() { - partition = "odm" - } else if module.ProductSpecific() { - partition = "product" - } else if module.SystemExtSpecific() { - partition = "system_ext" - } - return "/" + partition + "/framework/" + implName + ".jar" -} - -func formattedOptionalSdkLevelAttribute(ctx android.ModuleContext, attrName string, value *string) string { - if value == nil { - return "" - } - apiLevel, err := android.ApiLevelFromUser(ctx, *value) - if err != nil { - // attributes in bp files have underscores but in the xml have dashes. - ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), err.Error()) - return "" - } - if apiLevel.IsCurrent() { - // passing "current" would always mean a future release, never the current (or the current in - // progress) which means some conditions would never be triggered. - ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), - `"current" is not an allowed value for this attribute`) - return "" - } - // "safeValue" is safe because it translates finalized codenames to a string - // with their SDK int. - safeValue := apiLevel.String() - return formattedOptionalAttribute(attrName, &safeValue) -} - -// formats an attribute for the xml permissions file if the value is not null -// returns empty string otherwise -func formattedOptionalAttribute(attrName string, value *string) string { - if value == nil { - return "" - } - return fmt.Sprintf(" %s=\"%s\"\n", attrName, *value) -} - -func formattedDependenciesAttribute(dependencies []string) string { - if dependencies == nil { - return "" - } - return fmt.Sprintf(" dependency=\"%s\"\n", strings.Join(dependencies, ":")) -} - -func (module *sdkLibraryXml) permissionsContents(ctx android.ModuleContext) string { - libName := proptools.String(module.properties.Lib_name) - libNameAttr := formattedOptionalAttribute("name", &libName) - filePath := module.implPath(ctx) - filePathAttr := formattedOptionalAttribute("file", &filePath) - implicitFromAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-since", module.properties.On_bootclasspath_since) - implicitUntilAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-before", module.properties.On_bootclasspath_before) - minSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "min-device-sdk", module.properties.Min_device_sdk) - maxSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "max-device-sdk", module.properties.Max_device_sdk) - dependenciesAttr := formattedDependenciesAttribute(module.properties.Uses_libs_dependencies) - // <library> is understood in all android versions whereas <apex-library> is only understood from API T (and ignored before that). - // similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the apex-library to make sure this library is not loaded before T - var libraryTag string - if module.properties.Min_device_sdk != nil { - libraryTag = " <apex-library\n" - } else { - libraryTag = " <library\n" - } - - return strings.Join([]string{ - "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n", - "<!-- Copyright (C) 2018 The Android Open Source Project\n", - "\n", - " Licensed under the Apache License, Version 2.0 (the \"License\");\n", - " you may not use this file except in compliance with the License.\n", - " You may obtain a copy of the License at\n", - "\n", - " http://www.apache.org/licenses/LICENSE-2.0\n", - "\n", - " Unless required by applicable law or agreed to in writing, software\n", - " distributed under the License is distributed on an \"AS IS\" BASIS,\n", - " WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", - " See the License for the specific language governing permissions and\n", - " limitations under the License.\n", - "-->\n", - "<permissions>\n", - libraryTag, - libNameAttr, - filePathAttr, - implicitFromAttr, - implicitUntilAttr, - minSdkAttr, - maxSdkAttr, - dependenciesAttr, - " />\n", - "</permissions>\n", - }, "") -} - -func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleContext) { - apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) - module.hideApexVariantFromMake = !apexInfo.IsForPlatform() - - libName := proptools.String(module.properties.Lib_name) - module.selfValidate(ctx) - xmlContent := module.permissionsContents(ctx) - - module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath - android.WriteFileRuleVerbatim(ctx, module.outputFilePath, xmlContent) - - module.installDirPath = android.PathForModuleInstall(ctx, "etc", module.SubDir()) - ctx.PackageFile(module.installDirPath, libName+".xml", module.outputFilePath) - - ctx.SetOutputFiles(android.OutputPaths{module.outputFilePath}.Paths(), "") -} - -func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries { - if module.hideApexVariantFromMake { - return []android.AndroidMkEntries{{ - Disabled: true, - }} - } - - return []android.AndroidMkEntries{{ - Class: "ETC", - OutputFile: android.OptionalPathForPath(module.outputFilePath), - ExtraEntries: []android.AndroidMkExtraEntriesFunc{ - func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - entries.SetString("LOCAL_MODULE_TAGS", "optional") - entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.String()) - entries.SetString("LOCAL_INSTALLED_MODULE_STEM", module.outputFilePath.Base()) - }, - }, - }} -} - -func (module *sdkLibraryXml) selfValidate(ctx android.ModuleContext) { - module.validateAtLeastTAttributes(ctx) - module.validateMinAndMaxDeviceSdk(ctx) - module.validateMinMaxDeviceSdkAndModuleMinSdk(ctx) - module.validateOnBootclasspathBeforeRequirements(ctx) -} - -func (module *sdkLibraryXml) validateAtLeastTAttributes(ctx android.ModuleContext) { - t := android.ApiLevelOrPanic(ctx, "Tiramisu") - module.attrAtLeastT(ctx, t, module.properties.Min_device_sdk, "min_device_sdk") - module.attrAtLeastT(ctx, t, module.properties.Max_device_sdk, "max_device_sdk") - module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_before, "on_bootclasspath_before") - module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_since, "on_bootclasspath_since") -} - -func (module *sdkLibraryXml) attrAtLeastT(ctx android.ModuleContext, t android.ApiLevel, attr *string, attrName string) { - if attr != nil { - if level, err := android.ApiLevelFromUser(ctx, *attr); err == nil { - // we will inform the user of invalid inputs when we try to write the - // permissions xml file so we don't need to do it here - if t.GreaterThan(level) { - ctx.PropertyErrorf(attrName, "Attribute value needs to be at least T") - } - } - } -} - -func (module *sdkLibraryXml) validateMinAndMaxDeviceSdk(ctx android.ModuleContext) { - if module.properties.Min_device_sdk != nil && module.properties.Max_device_sdk != nil { - min, minErr := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk) - max, maxErr := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk) - if minErr == nil && maxErr == nil { - // we will inform the user of invalid inputs when we try to write the - // permissions xml file so we don't need to do it here - if min.GreaterThan(max) { - ctx.ModuleErrorf("min_device_sdk can't be greater than max_device_sdk") - } - } - } -} - -func (module *sdkLibraryXml) validateMinMaxDeviceSdkAndModuleMinSdk(ctx android.ModuleContext) { - moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level) - if module.properties.Min_device_sdk != nil { - api, err := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk) - if err == nil { - if moduleMinApi.GreaterThan(api) { - ctx.PropertyErrorf("min_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi) - } - } - } - if module.properties.Max_device_sdk != nil { - api, err := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk) - if err == nil { - if moduleMinApi.GreaterThan(api) { - ctx.PropertyErrorf("max_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi) - } - } - } -} - -func (module *sdkLibraryXml) validateOnBootclasspathBeforeRequirements(ctx android.ModuleContext) { - moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level) - if module.properties.On_bootclasspath_before != nil { - t := android.ApiLevelOrPanic(ctx, "Tiramisu") - // if we use the attribute, then we need to do this validation - if moduleMinApi.LessThan(t) { - // if minAPi is < T, then we need to have min_device_sdk (which only accepts T+) - if module.properties.Min_device_sdk == nil { - ctx.PropertyErrorf("on_bootclasspath_before", "Using this property requires that the module's min_sdk_version or the shared library's min_device_sdk is at least T") - } - } - } -} - type sdkLibrarySdkMemberType struct { android.SdkMemberTypeBase } @@ -3510,7 +2385,6 @@ func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMembe } } - s.Naming_scheme = sdk.commonSdkLibraryProperties.Naming_scheme s.Shared_library = proptools.BoolPtr(sdk.sharedLibrary()) s.Compile_dex = sdk.dexProperties.Compile_dex s.Doctag_paths = sdk.doctagPaths @@ -3520,7 +2394,7 @@ func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMembe s.Min_device_sdk = sdk.commonSdkLibraryProperties.Min_device_sdk s.Max_device_sdk = sdk.commonSdkLibraryProperties.Max_device_sdk - implLibrary := sdk.getImplLibraryModule() + implLibrary := sdk.implLibraryModule if implLibrary != nil && implLibrary.dexpreopter.dexpreoptProperties.Dex_preopt_result.Profile_guided { s.DexPreoptProfileGuided = proptools.BoolPtr(true) } diff --git a/java/sdk_library_external.go b/java/sdk_library_external.go deleted file mode 100644 index 4f8398194..000000000 --- a/java/sdk_library_external.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2020 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package java - -import ( - "android/soong/android" -) - -type partitionGroup int - -// Representation of partition group for checking inter-partition library dependencies. -// Between system and system_ext, there are no restrictions of dependencies, -// so we can treat these partitions as the same in terms of inter-partition dependency. -// Same policy is applied between vendor and odm partiton. -const ( - partitionGroupNone partitionGroup = iota - // group for system, and system_ext partition - partitionGroupSystem - // group for vendor and odm partition - partitionGroupVendor - // product partition - partitionGroupProduct -) - -func (g partitionGroup) String() string { - switch g { - case partitionGroupSystem: - return "system" - case partitionGroupVendor: - return "vendor" - case partitionGroupProduct: - return "product" - } - - return "" -} - -// Get partition group of java module that can be used at inter-partition dependency check. -// We currently have three groups -// -// (system, system_ext) => system partition group -// (vendor, odm) => vendor partition group -// (product) => product partition group -func (j *Module) partitionGroup(ctx android.EarlyModuleContext) partitionGroup { - // system and system_ext partition can be treated as the same in terms of inter-partition dependency. - if j.Platform() || j.SystemExtSpecific() { - return partitionGroupSystem - } - - // vendor and odm partition can be treated as the same in terms of inter-partition dependency. - if j.SocSpecific() || j.DeviceSpecific() { - return partitionGroupVendor - } - - // product partition is independent. - if j.ProductSpecific() { - return partitionGroupProduct - } - - panic("Cannot determine partition type") -} - -func (j *Module) allowListedInterPartitionJavaLibrary(ctx android.EarlyModuleContext) bool { - return inList(j.Name(), ctx.Config().InterPartitionJavaLibraryAllowList()) -} - -func (j *Module) syspropWithPublicStubs() bool { - return j.deviceProperties.SyspropPublicStub != "" -} - -type javaSdkLibraryEnforceContext interface { - Name() string - allowListedInterPartitionJavaLibrary(ctx android.EarlyModuleContext) bool - partitionGroup(ctx android.EarlyModuleContext) partitionGroup - syspropWithPublicStubs() bool -} - -var _ javaSdkLibraryEnforceContext = (*Module)(nil) - -func (j *Module) checkPartitionsForJavaDependency(ctx android.EarlyModuleContext, propName string, dep javaSdkLibraryEnforceContext) { - if dep.allowListedInterPartitionJavaLibrary(ctx) { - return - } - - if dep.syspropWithPublicStubs() { - return - } - - // If product interface is not enforced, skip check between system and product partition. - // But still need to check between product and vendor partition because product interface flag - // just represents enforcement between product and system, and vendor interface enforcement - // that is enforced here by precondition is representing enforcement between vendor and other partitions. - if !ctx.Config().EnforceProductPartitionInterface() { - productToSystem := j.partitionGroup(ctx) == partitionGroupProduct && dep.partitionGroup(ctx) == partitionGroupSystem - systemToProduct := j.partitionGroup(ctx) == partitionGroupSystem && dep.partitionGroup(ctx) == partitionGroupProduct - - if productToSystem || systemToProduct { - return - } - } - - // If module and dependency library is inter-partition - if j.partitionGroup(ctx) != dep.partitionGroup(ctx) { - errorFormat := "dependency on java_library (%q) is not allowed across the partitions (%s -> %s), use java_sdk_library instead" - ctx.PropertyErrorf(propName, errorFormat, dep.Name(), j.partitionGroup(ctx), dep.partitionGroup(ctx)) - } -} diff --git a/java/sdk_library_internal.go b/java/sdk_library_internal.go new file mode 100644 index 000000000..ca088cf68 --- /dev/null +++ b/java/sdk_library_internal.go @@ -0,0 +1,1017 @@ +// Copyright 2024 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package java + +import ( + "android/soong/android" + "android/soong/etc" + "fmt" + "path" + "strings" + + "github.com/google/blueprint/proptools" +) + +// --------------------------------------------------------------------------------------------- +// Naming scheme of the submodules generated by java_sdk_library and java_sdk_library_import +// --------------------------------------------------------------------------------------------- + +const ( + sdkXmlFileSuffix = ".xml" + implLibSuffix = ".impl" +) + +func implLibraryModuleName(sdkLibName string) string { + return sdkLibName + implLibSuffix +} + +// Module name of the runtime implementation library +func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string { + return implLibraryModuleName(c.module.RootLibraryName()) +} + +// Module name of the XML file for the lib +func (c *commonToSdkLibraryAndImport) xmlPermissionsModuleName() string { + return c.module.RootLibraryName() + sdkXmlFileSuffix +} + +// Name of the java_library module that compiles the stubs source. +func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string { + baseName := c.module.RootLibraryName() + return apiScope.stubsLibraryModuleName(baseName) +} + +// Name of the java_library module that compiles the exportable stubs source. +func (c *commonToSdkLibraryAndImport) exportableStubsLibraryModuleName(apiScope *apiScope) string { + baseName := c.module.RootLibraryName() + return apiScope.exportableStubsLibraryModuleName(baseName) +} + +// Name of the droidstubs module that generates the stubs source and may also +// generate/check the API. +func (c *commonToSdkLibraryAndImport) droidstubsModuleName(apiScope *apiScope) string { + baseName := c.module.RootLibraryName() + return apiScope.stubsSourceModuleName(baseName) +} + +// Name of the java_api_library module that generates the from-text stubs source +// and compiles to a jar file. +func (c *commonToSdkLibraryAndImport) fromTextStubsLibraryModuleName(apiScope *apiScope) string { + baseName := c.module.RootLibraryName() + return apiScope.apiLibraryModuleName(baseName) +} + +// Name of the java_library module that compiles the stubs +// generated from source Java files. +func (c *commonToSdkLibraryAndImport) fromSourceStubsLibraryModuleName(apiScope *apiScope) string { + baseName := c.module.RootLibraryName() + return apiScope.sourceStubsLibraryModuleName(baseName) +} + +// Name of the java_library module that compiles the exportable stubs +// generated from source Java files. +func (c *commonToSdkLibraryAndImport) exportableFromSourceStubsLibraryModuleName(apiScope *apiScope) string { + baseName := c.module.RootLibraryName() + return apiScope.exportableSourceStubsLibraryModuleName(baseName) +} + +// --------------------------------------------------------------------------------------------- +// Build rules of the submodules generated by java_sdk_library. +// java_sdk_library "framework-foo" generates the following submodules: +// +// - "framework-foo.impl" (type: [Library]): the implementation library, which generates the +// compilation outputs that include the implementation details and the private apis +// (i.e. class/methods that are annotated @hide). +// +// - "framework-foo.stubs.source.<[apiScope.name]>" (type: [Droidstubs]): droidstubs module that +// generates the stubs and the api files for the given api scope. +// +// - "framework-foo.stubs.<[apiScope.name]>" (type: [Library]): stub library module that +// provides the compilation output of the stubs to the reverse dependencies. The module +// itself does not perform any compilation actions; the module statically depends on one of +// the from-source stub module or the from-text stub configuration based on the build +// configuration. +// +// - "framework-foo.stubs.<[apiScope.name]>.from-source" (type: [Library]): stub library module +// that compiles the stubs generated by the droidstubs submodule. This module is a static +// dependency of the stub library module when +// [android/soong/android/config.BuildFromTextStub()] is false. +// +// - "framework-foo.stubs.<[apiScope.name]>.from-text" (type: [ApiLibrary]): api library module +// that generates and compiles the stubs from the api files checked in the tree instead of +// the source Java files (e.g. *-current.txt files). This module is a static dependency of +// the stub library module when [android/soong/android/config.BuildFromTextStub()] is true. +// +// - "framework-foo.stubs.exportable.<[apiScope.name]>" (type: [Library]): stub library module +// that provides the "exportable" stubs. "exportable" stubs are the stubs that do not +// include in-development flagged apis. This module is only used for SDK builds to generate +// the SDK artifacts, and not purposed for consumption for other modules. +// +// - "framework-foo.stubs.exportable.<[apiScope.name]>.from-source" (type: [Library]): stub +// library module that compiles the "exportable" stubs generated by the droidstubs +// submodule. This module is always a static dependency of the "exportable" stub library +// module given that from-text stubs cannot be used for SDK builds as it does not contain +// documentations. +// +// - "framework-foo.xml" (type: [sdkLibraryXml]): xml library that generates the permission xml +// file, which allows [SdkLibrary] to be used with <uses-permission> tag in the +// AndroidManifest.xml files. +// --------------------------------------------------------------------------------------------- + +// Creates the implementation [Library] with ".impl" suffix. +func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) { + visibility := childModuleVisibility(module.sdkLibraryProperties.Impl_library_visibility) + + staticLibs := module.properties.Static_libs.Clone() + staticLibs.AppendSimpleValue(module.sdkLibraryProperties.Impl_only_static_libs) + props := struct { + Name *string + Enabled proptools.Configurable[bool] + Visibility []string + Libs []string + Static_libs proptools.Configurable[[]string] + Apex_available []string + Stem *string + }{ + Name: proptools.StringPtr(module.implLibraryModuleName()), + Enabled: module.EnabledProperty(), + Visibility: visibility, + + Libs: append(module.properties.Libs, module.sdkLibraryProperties.Impl_only_libs...), + + Static_libs: staticLibs, + // Pass the apex_available settings down so that the impl library can be statically + // embedded within a library that is added to an APEX. Needed for updatable-media. + Apex_available: module.ApexAvailable(), + + Stem: proptools.StringPtr(module.Name()), + } + + properties := []interface{}{ + &module.properties, + &module.protoProperties, + &module.deviceProperties, + &module.dexProperties, + &module.dexpreoptProperties, + &module.linter.properties, + &module.overridableProperties, + &props, + module.sdkComponentPropertiesForChildLibrary(), + } + mctx.CreateModule(LibraryFactory, properties...) +} + +// Creates the [Droidstubs] module with ".stubs.source.<[apiScope.name]>" that creates stubs +// source files from the given full source files and also updates and checks the API +// specification files (i.e. "*-current.txt", "*-removed.txt" files). +func (module *SdkLibrary) createDroidstubs(mctx android.DefaultableHookContext, apiScope *apiScope, name string, scopeSpecificDroidstubsArgs []string) { + props := struct { + Name *string + Enabled proptools.Configurable[bool] + Visibility []string + Srcs []string + Installable *bool + Sdk_version *string + Api_surface *string + System_modules *string + Libs proptools.Configurable[[]string] + Output_javadoc_comments *bool + Arg_files []string + Args *string + Java_version *string + Annotations_enabled *bool + Merge_annotations_dirs []string + Merge_inclusion_annotations_dirs []string + Generate_stubs *bool + Previous_api *string + Aconfig_declarations []string + Check_api struct { + Current ApiToCheck + Last_released ApiToCheck + + Api_lint struct { + Enabled *bool + New_since *string + Baseline_file *string + } + } + Aidl struct { + Include_dirs []string + Local_include_dirs []string + } + Dists []android.Dist + }{} + + // The stubs source processing uses the same compile time classpath when extracting the + // API from the implementation library as it does when compiling it. i.e. the same + // * sdk version + // * system_modules + // * libs (static_libs/libs) + + props.Name = proptools.StringPtr(name) + props.Enabled = module.EnabledProperty() + props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_source_visibility) + props.Srcs = append(props.Srcs, module.properties.Srcs...) + props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...) + props.Sdk_version = module.deviceProperties.Sdk_version + props.Api_surface = &apiScope.name + props.System_modules = module.deviceProperties.System_modules + props.Installable = proptools.BoolPtr(false) + // A droiddoc module has only one Libs property and doesn't distinguish between + // shared libs and static libs. So we need to add both of these libs to Libs property. + props.Libs = proptools.NewConfigurable[[]string](nil, nil) + props.Libs.AppendSimpleValue(module.properties.Libs) + props.Libs.Append(module.properties.Static_libs) + props.Libs.AppendSimpleValue(module.sdkLibraryProperties.Stub_only_libs) + props.Libs.AppendSimpleValue(module.scopeToProperties[apiScope].Libs) + props.Aidl.Include_dirs = module.deviceProperties.Aidl.Include_dirs + props.Aidl.Local_include_dirs = module.deviceProperties.Aidl.Local_include_dirs + props.Java_version = module.properties.Java_version + + props.Annotations_enabled = module.sdkLibraryProperties.Annotations_enabled + props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs + props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs + props.Aconfig_declarations = module.sdkLibraryProperties.Aconfig_declarations + + droidstubsArgs := []string{} + if len(module.sdkLibraryProperties.Api_packages) != 0 { + droidstubsArgs = append(droidstubsArgs, "--stub-packages "+strings.Join(module.sdkLibraryProperties.Api_packages, ":")) + } + droidstubsArgs = append(droidstubsArgs, module.sdkLibraryProperties.Droiddoc_options...) + disabledWarnings := []string{"HiddenSuperclass"} + if proptools.BoolDefault(module.sdkLibraryProperties.Api_lint.Legacy_errors_allowed, true) { + disabledWarnings = append(disabledWarnings, + "BroadcastBehavior", + "DeprecationMismatch", + "MissingPermission", + "SdkConstant", + "Todo", + ) + } + droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide ")) + + // Output Javadoc comments for public scope. + if apiScope == apiScopePublic { + props.Output_javadoc_comments = proptools.BoolPtr(true) + } + + // Add in scope specific arguments. + droidstubsArgs = append(droidstubsArgs, scopeSpecificDroidstubsArgs...) + props.Arg_files = module.sdkLibraryProperties.Droiddoc_option_files + props.Args = proptools.StringPtr(strings.Join(droidstubsArgs, " ")) + + // List of APIs identified from the provided source files are created. They are later + // compared against to the not-yet-released (a.k.a current) list of APIs and to the + // last-released (a.k.a numbered) list of API. + currentApiFileName := apiScope.apiFilePrefix + "current.txt" + removedApiFileName := apiScope.apiFilePrefix + "removed.txt" + apiDir := module.getApiDir() + currentApiFileName = path.Join(apiDir, currentApiFileName) + removedApiFileName = path.Join(apiDir, removedApiFileName) + + // check against the not-yet-release API + props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName) + props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName) + + if module.compareAgainstLatestApi(apiScope) { + // check against the latest released API + latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope)) + props.Previous_api = latestApiFilegroupName + props.Check_api.Last_released.Api_file = latestApiFilegroupName + props.Check_api.Last_released.Removed_api_file = proptools.StringPtr( + module.latestRemovedApiFilegroupName(apiScope)) + props.Check_api.Last_released.Baseline_file = proptools.StringPtr( + module.latestIncompatibilitiesFilegroupName(apiScope)) + + if proptools.Bool(module.sdkLibraryProperties.Api_lint.Enabled) { + // Enable api lint. + props.Check_api.Api_lint.Enabled = proptools.BoolPtr(true) + props.Check_api.Api_lint.New_since = latestApiFilegroupName + + // If it exists then pass a lint-baseline.txt through to droidstubs. + baselinePath := path.Join(apiDir, apiScope.apiFilePrefix+"lint-baseline.txt") + baselinePathRelativeToRoot := path.Join(mctx.ModuleDir(), baselinePath) + paths, err := mctx.GlobWithDeps(baselinePathRelativeToRoot, nil) + if err != nil { + mctx.ModuleErrorf("error checking for presence of %s: %s", baselinePathRelativeToRoot, err) + } + if len(paths) == 1 { + props.Check_api.Api_lint.Baseline_file = proptools.StringPtr(baselinePath) + } else if len(paths) != 0 { + mctx.ModuleErrorf("error checking for presence of %s: expected one path, found: %v", baselinePathRelativeToRoot, paths) + } + } + } + + if !Bool(module.sdkLibraryProperties.No_dist) { + // Dist the api txt and removed api txt artifacts for sdk builds. + distDir := proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api")) + stubsTypeTagPrefix := "" + if mctx.Config().ReleaseHiddenApiExportableStubs() { + stubsTypeTagPrefix = ".exportable" + } + for _, p := range []struct { + tag string + pattern string + }{ + // "exportable" api files are copied to the dist directory instead of the + // "everything" api files when "RELEASE_HIDDEN_API_EXPORTABLE_STUBS" build flag + // is set. Otherwise, the "everything" api files are copied to the dist directory. + {tag: "%s.api.txt", pattern: "%s.txt"}, + {tag: "%s.removed-api.txt", pattern: "%s-removed.txt"}, + } { + props.Dists = append(props.Dists, android.Dist{ + Targets: []string{"sdk", "win_sdk"}, + Dir: distDir, + Dest: proptools.StringPtr(fmt.Sprintf(p.pattern, module.distStem())), + Tag: proptools.StringPtr(fmt.Sprintf(p.tag, stubsTypeTagPrefix)), + }) + } + } + + mctx.CreateModule(DroidstubsFactory, &props, module.sdkComponentPropertiesForChildLibrary()).(*Droidstubs).CallHookIfAvailable(mctx) +} + +type libraryProperties struct { + Name *string + Enabled proptools.Configurable[bool] + Visibility []string + Srcs []string + Installable *bool + Sdk_version *string + System_modules *string + Patch_module *string + Libs []string + Static_libs []string + Compile_dex *bool + Java_version *string + Openjdk9 struct { + Srcs []string + Javacflags []string + } + Dist struct { + Targets []string + Dest *string + Dir *string + Tag *string + } + Is_stubs_module *bool + Stub_contributing_api *string +} + +func (module *SdkLibrary) stubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope) libraryProperties { + props := libraryProperties{} + props.Enabled = module.EnabledProperty() + props.Visibility = []string{"//visibility:override", "//visibility:private"} + // sources are generated from the droiddoc + sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope) + props.Sdk_version = proptools.StringPtr(sdkVersion) + props.System_modules = module.deviceProperties.System_modules + props.Patch_module = module.properties.Patch_module + props.Installable = proptools.BoolPtr(false) + props.Libs = module.sdkLibraryProperties.Stub_only_libs + props.Libs = append(props.Libs, module.scopeToProperties[apiScope].Libs...) + props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs + // The stub-annotations library contains special versions of the annotations + // with CLASS retention policy, so that they're kept. + if proptools.Bool(module.sdkLibraryProperties.Annotations_enabled) { + props.Libs = append(props.Libs, "stub-annotations") + } + props.Openjdk9.Srcs = module.properties.Openjdk9.Srcs + props.Openjdk9.Javacflags = module.properties.Openjdk9.Javacflags + // We compile the stubs for 1.8 in line with the main android.jar stubs, and potential + // interop with older developer tools that don't support 1.9. + props.Java_version = proptools.StringPtr("1.8") + props.Is_stubs_module = proptools.BoolPtr(true) + props.Stub_contributing_api = proptools.StringPtr(apiScope.kind.String()) + + return props +} + +// Creates the from-source stub [Library] with ".stubs.<[apiScope.name]>.from-source" suffix. +func (module *SdkLibrary) createFromSourceStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) { + + props := module.stubsLibraryProps(mctx, apiScope) + props.Name = proptools.StringPtr(module.fromSourceStubsLibraryModuleName(apiScope)) + props.Srcs = []string{":" + module.droidstubsModuleName(apiScope)} + + mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) +} + +// Creates the "exportable" from-source stub [Library] with +// ".stubs.exportable.<[apiScope.name]>" suffix. +func (module *SdkLibrary) createExportableFromSourceStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) { + props := module.stubsLibraryProps(mctx, apiScope) + props.Name = proptools.StringPtr(module.exportableFromSourceStubsLibraryModuleName(apiScope)) + props.Srcs = []string{":" + module.droidstubsModuleName(apiScope) + "{.exportable}"} + + mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) +} + +// Creates the from-text stub [ApiLibrary] with ".stubs.<[apiScope.name]>.from-text" suffix. +func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) { + props := struct { + Name *string + Enabled proptools.Configurable[bool] + Visibility []string + Api_contributions []string + Libs proptools.Configurable[[]string] + Static_libs []string + System_modules *string + Enable_validation *bool + Stubs_type *string + Sdk_version *string + Previous_api *string + }{} + + props.Name = proptools.StringPtr(module.fromTextStubsLibraryModuleName(apiScope)) + props.Enabled = module.EnabledProperty() + props.Visibility = []string{"//visibility:override", "//visibility:private"} + + apiContributions := []string{} + + // Api surfaces are not independent of each other, but have subset relationships, + // and so does the api files. To generate from-text stubs for api surfaces other than public, + // all subset api domains' api_contriubtions must be added as well. + scope := apiScope + for scope != nil { + apiContributions = append(apiContributions, module.droidstubsModuleName(scope)+".api.contribution") + scope = scope.extends + } + if apiScope == apiScopePublic { + additionalApiContribution := module.apiLibraryAdditionalApiContribution() + if additionalApiContribution != "" { + apiContributions = append(apiContributions, additionalApiContribution) + } + } + + props.Api_contributions = apiContributions + + // Ensure that stub-annotations is added to the classpath before any other libs + props.Libs = proptools.NewConfigurable[[]string](nil, nil) + props.Libs.AppendSimpleValue([]string{"stub-annotations"}) + props.Libs.AppendSimpleValue(module.properties.Libs) + props.Libs.Append(module.properties.Static_libs) + props.Libs.AppendSimpleValue(module.sdkLibraryProperties.Stub_only_libs) + props.Libs.AppendSimpleValue(module.scopeToProperties[apiScope].Libs) + props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs + + props.System_modules = module.deviceProperties.System_modules + props.Enable_validation = proptools.BoolPtr(true) + props.Stubs_type = proptools.StringPtr("everything") + + if module.deviceProperties.Sdk_version != nil { + props.Sdk_version = module.deviceProperties.Sdk_version + } + + if module.compareAgainstLatestApi(apiScope) { + // check against the latest released API + latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope)) + props.Previous_api = latestApiFilegroupName + } + + mctx.CreateModule(ApiLibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) +} + +func (module *SdkLibrary) topLevelStubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope, doDist bool) libraryProperties { + props := libraryProperties{} + + props.Enabled = module.EnabledProperty() + props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_library_visibility) + sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope) + props.Sdk_version = proptools.StringPtr(sdkVersion) + + props.System_modules = module.deviceProperties.System_modules + + // The imports need to be compiled to dex if the java_sdk_library requests it. + compileDex := module.dexProperties.Compile_dex + if module.stubLibrariesCompiledForDex() { + compileDex = proptools.BoolPtr(true) + } + props.Compile_dex = compileDex + + props.Stub_contributing_api = proptools.StringPtr(apiScope.kind.String()) + + if !Bool(module.sdkLibraryProperties.No_dist) && doDist { + props.Dist.Targets = []string{"sdk", "win_sdk"} + props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.distStem())) + props.Dist.Dir = proptools.StringPtr(module.apiDistPath(apiScope)) + props.Dist.Tag = proptools.StringPtr(".jar") + } + props.Is_stubs_module = proptools.BoolPtr(true) + + return props +} + +// Creates the stub [Library] with ".stubs.<[apiScope.name]>" suffix. +func (module *SdkLibrary) createTopLevelStubsLibrary( + mctx android.DefaultableHookContext, apiScope *apiScope) { + + // Dist the "everything" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is false + doDist := !mctx.Config().ReleaseHiddenApiExportableStubs() + props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist) + props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope)) + + // Add the stub compiling java_library/java_api_library as static lib based on build config + staticLib := module.fromSourceStubsLibraryModuleName(apiScope) + if mctx.Config().BuildFromTextStub() && module.ModuleBuildFromTextStubs() { + staticLib = module.fromTextStubsLibraryModuleName(apiScope) + } + props.Static_libs = append(props.Static_libs, staticLib) + + mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) +} + +// Creates the "exportable" stub [Library] with ".stubs.exportable.<[apiScope.name]>" suffix. +func (module *SdkLibrary) createTopLevelExportableStubsLibrary( + mctx android.DefaultableHookContext, apiScope *apiScope) { + + // Dist the "exportable" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is true + doDist := mctx.Config().ReleaseHiddenApiExportableStubs() + props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist) + props.Name = proptools.StringPtr(module.exportableStubsLibraryModuleName(apiScope)) + + staticLib := module.exportableFromSourceStubsLibraryModuleName(apiScope) + props.Static_libs = append(props.Static_libs, staticLib) + + mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) +} + +// Creates the [sdkLibraryXml] with ".xml" suffix. +func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) { + moduleMinApiLevel := module.Library.MinSdkVersion(mctx) + var moduleMinApiLevelStr = moduleMinApiLevel.String() + if moduleMinApiLevel == android.NoneApiLevel { + moduleMinApiLevelStr = "current" + } + props := struct { + Name *string + Enabled proptools.Configurable[bool] + Lib_name *string + Apex_available []string + On_bootclasspath_since *string + On_bootclasspath_before *string + Min_device_sdk *string + Max_device_sdk *string + Sdk_library_min_api_level *string + Uses_libs_dependencies []string + }{ + Name: proptools.StringPtr(module.xmlPermissionsModuleName()), + Enabled: module.EnabledProperty(), + Lib_name: proptools.StringPtr(module.BaseModuleName()), + Apex_available: module.ApexProperties.Apex_available, + On_bootclasspath_since: module.commonSdkLibraryProperties.On_bootclasspath_since, + On_bootclasspath_before: module.commonSdkLibraryProperties.On_bootclasspath_before, + Min_device_sdk: module.commonSdkLibraryProperties.Min_device_sdk, + Max_device_sdk: module.commonSdkLibraryProperties.Max_device_sdk, + Sdk_library_min_api_level: &moduleMinApiLevelStr, + Uses_libs_dependencies: module.usesLibraryProperties.Uses_libs, + } + + mctx.CreateModule(sdkLibraryXmlFactory, &props) +} + +// --------------------------------------------------------------------------------------------- +// Build rules of the submodules generated by java_sdk_library_import. +// Note that the java_sdk_library_import module does not generate the implementation library. +// Instead, it will create a dependency to the source implemenetation library if one exists. +// java_sdk_library_import "framework-foo" generates the following submodules: +// +// - "framework-foo.stubs.<[apiScope.name]>" (type: [Import]): prebuilt stub library module that +// provides the stub jar file checked in the tree. +// +// - "framework-foo.stubs.source.<[apiScope.name]>" (type: [PrebuiltStubsSources]): prebuilt +// droidstubs module that provides the stub source jar file checked in the tree. +// +// - "framework-foo.stubs.source.<[apiScope.name]>.api.contribution" +// (type [JavaApiContributionImport]): prebuilt java_api_contribution module that provides +// the prebuilt api file for previously released from-text stub generation. +// --------------------------------------------------------------------------------------------- + +// Creates the prebuilt stub [Import] with ".stubs.<[apiScope.name]>" suffix. +func (module *SdkLibraryImport) createJavaImportForStubs(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) { + // Creates a java import for the jar with ".stubs" suffix + props := struct { + Name *string + Source_module_name *string + Created_by_java_sdk_library_name *string + Sdk_version *string + Libs []string + Jars []string + Compile_dex *bool + Is_stubs_module *bool + + android.UserSuppliedPrebuiltProperties + }{} + props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope)) + props.Source_module_name = proptools.StringPtr(apiScope.stubsLibraryModuleName(module.BaseModuleName())) + props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName()) + props.Sdk_version = scopeProperties.Sdk_version + // Prepend any of the libs from the legacy public properties to the libs for each of the + // scopes to avoid having to duplicate them in each scope. + props.Libs = append(module.properties.Libs, scopeProperties.Libs...) + props.Jars = scopeProperties.Jars + + // The imports are preferred if the java_sdk_library_import is preferred. + props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt) + + // The imports need to be compiled to dex if the java_sdk_library_import requests it. + compileDex := module.properties.Compile_dex + if module.stubLibrariesCompiledForDex() { + compileDex = proptools.BoolPtr(true) + } + props.Compile_dex = compileDex + props.Is_stubs_module = proptools.BoolPtr(true) + + mctx.CreateModule(ImportFactory, &props, module.sdkComponentPropertiesForChildLibrary()) +} + +func (module *SdkLibraryImport) createPrebuiltStubsSources(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) { + props := struct { + Name *string + Source_module_name *string + Created_by_java_sdk_library_name *string + Srcs []string + + android.UserSuppliedPrebuiltProperties + }{} + props.Name = proptools.StringPtr(module.droidstubsModuleName(apiScope)) + props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName())) + props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName()) + props.Srcs = scopeProperties.Stub_srcs + + // The stubs source is preferred if the java_sdk_library_import is preferred. + props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt) + + mctx.CreateModule(PrebuiltStubsSourcesFactory, &props, module.sdkComponentPropertiesForChildLibrary()) +} + +// Creates the prebuilt api contribution [JavaApiContributionImport] with +// ".stubs.source.<[apiScope.name]>.api.contribution" suffix. +func (module *SdkLibraryImport) createPrebuiltApiContribution(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) { + api_file := scopeProperties.Current_api + api_surface := &apiScope.name + + props := struct { + Name *string + Source_module_name *string + Created_by_java_sdk_library_name *string + Api_surface *string + Api_file *string + Visibility []string + }{} + + props.Name = proptools.StringPtr(module.droidstubsModuleName(apiScope) + ".api.contribution") + props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()) + ".api.contribution") + props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName()) + props.Api_surface = api_surface + props.Api_file = api_file + props.Visibility = []string{"//visibility:override", "//visibility:public"} + + mctx.CreateModule(ApiContributionImportFactory, &props, module.sdkComponentPropertiesForChildLibrary()) +} + +// --------------------------------------------------------------------------------------------- +// End of the build rules of the submodules generated by java_sdk_library_import. +// --------------------------------------------------------------------------------------------- + +// Definition of the [sdkLibraryXml] module. The module generates the permissions xml file, +// so that the apps can specify the java_sdk_library using <uses-permission> tag in the +// AndroidManifest.xml file. +type sdkLibraryXml struct { + android.ModuleBase + android.DefaultableModuleBase + android.ApexModuleBase + + properties sdkLibraryXmlProperties + + outputFilePath android.OutputPath + installDirPath android.InstallPath + + hideApexVariantFromMake bool +} + +type sdkLibraryXmlProperties struct { + // canonical name of the lib + Lib_name *string + + // Signals that this shared library is part of the bootclasspath starting + // on the version indicated in this attribute. + // + // This will make platforms at this level and above to ignore + // <uses-library> tags with this library name because the library is already + // available + On_bootclasspath_since *string + + // Signals that this shared library was part of the bootclasspath before + // (but not including) the version indicated in this attribute. + // + // The system will automatically add a <uses-library> tag with this library to + // apps that target any SDK less than the version indicated in this attribute. + On_bootclasspath_before *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is below the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Min_device_sdk *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is above the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Max_device_sdk *string + + // The SdkLibrary's min api level as a string + // + // This value comes from the ApiLevel of the MinSdkVersion property. + Sdk_library_min_api_level *string + + // Uses-libs dependencies that the shared library requires to work correctly. + // + // This will add dependency="foo:bar" to the <library> section. + Uses_libs_dependencies []string +} + +// java_sdk_library_xml builds the permission xml file for a java_sdk_library. +// Not to be used directly by users. java_sdk_library internally uses this. +func sdkLibraryXmlFactory() android.Module { + module := &sdkLibraryXml{} + + module.AddProperties(&module.properties) + + android.InitApexModule(module) + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) + + return module +} + +func (module *sdkLibraryXml) UniqueApexVariations() bool { + // sdkLibraryXml needs a unique variation per APEX because the generated XML file contains the path to the + // mounted APEX, which contains the name of the APEX. + return true +} + +// from android.PrebuiltEtcModule +func (module *sdkLibraryXml) BaseDir() string { + return "etc" +} + +// from android.PrebuiltEtcModule +func (module *sdkLibraryXml) SubDir() string { + return "permissions" +} + +var _ etc.PrebuiltEtcModule = (*sdkLibraryXml)(nil) + +// from android.ApexModule +func (module *sdkLibraryXml) AvailableFor(what string) bool { + return true +} + +func (module *sdkLibraryXml) DepsMutator(ctx android.BottomUpMutatorContext) { + // do nothing +} + +var _ android.ApexModule = (*sdkLibraryXml)(nil) + +// Implements android.ApexModule +func (module *sdkLibraryXml) ShouldSupportSdkVersion(ctx android.BaseModuleContext, + sdkVersion android.ApiLevel) error { + // sdkLibraryXml doesn't need to be checked separately because java_sdk_library is checked + return nil +} + +// File path to the runtime implementation library +func (module *sdkLibraryXml) implPath(ctx android.ModuleContext) string { + implName := proptools.String(module.properties.Lib_name) + if apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider); !apexInfo.IsForPlatform() { + // TODO(b/146468504): ApexVariationName() is only a soong module name, not apex name. + // In most cases, this works fine. But when apex_name is set or override_apex is used + // this can be wrong. + return fmt.Sprintf("/apex/%s/javalib/%s.jar", apexInfo.BaseApexName, implName) + } + partition := "system" + if module.SocSpecific() { + partition = "vendor" + } else if module.DeviceSpecific() { + partition = "odm" + } else if module.ProductSpecific() { + partition = "product" + } else if module.SystemExtSpecific() { + partition = "system_ext" + } + return "/" + partition + "/framework/" + implName + ".jar" +} + +func formattedOptionalSdkLevelAttribute(ctx android.ModuleContext, attrName string, value *string) string { + if value == nil { + return "" + } + apiLevel, err := android.ApiLevelFromUser(ctx, *value) + if err != nil { + // attributes in bp files have underscores but in the xml have dashes. + ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), err.Error()) + return "" + } + if apiLevel.IsCurrent() { + // passing "current" would always mean a future release, never the current (or the current in + // progress) which means some conditions would never be triggered. + ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), + `"current" is not an allowed value for this attribute`) + return "" + } + // "safeValue" is safe because it translates finalized codenames to a string + // with their SDK int. + safeValue := apiLevel.String() + return formattedOptionalAttribute(attrName, &safeValue) +} + +// formats an attribute for the xml permissions file if the value is not null +// returns empty string otherwise +func formattedOptionalAttribute(attrName string, value *string) string { + if value == nil { + return "" + } + return fmt.Sprintf(" %s=\"%s\"\n", attrName, *value) +} + +func formattedDependenciesAttribute(dependencies []string) string { + if dependencies == nil { + return "" + } + return fmt.Sprintf(" dependency=\"%s\"\n", strings.Join(dependencies, ":")) +} + +func (module *sdkLibraryXml) permissionsContents(ctx android.ModuleContext) string { + libName := proptools.String(module.properties.Lib_name) + libNameAttr := formattedOptionalAttribute("name", &libName) + filePath := module.implPath(ctx) + filePathAttr := formattedOptionalAttribute("file", &filePath) + implicitFromAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-since", module.properties.On_bootclasspath_since) + implicitUntilAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-before", module.properties.On_bootclasspath_before) + minSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "min-device-sdk", module.properties.Min_device_sdk) + maxSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "max-device-sdk", module.properties.Max_device_sdk) + dependenciesAttr := formattedDependenciesAttribute(module.properties.Uses_libs_dependencies) + // <library> is understood in all android versions whereas <apex-library> is only understood from API T (and ignored before that). + // similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the apex-library to make sure this library is not loaded before T + var libraryTag string + if module.properties.Min_device_sdk != nil { + libraryTag = " <apex-library\n" + } else { + libraryTag = " <library\n" + } + + return strings.Join([]string{ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n", + "<!-- Copyright (C) 2018 The Android Open Source Project\n", + "\n", + " Licensed under the Apache License, Version 2.0 (the \"License\");\n", + " you may not use this file except in compliance with the License.\n", + " You may obtain a copy of the License at\n", + "\n", + " http://www.apache.org/licenses/LICENSE-2.0\n", + "\n", + " Unless required by applicable law or agreed to in writing, software\n", + " distributed under the License is distributed on an \"AS IS\" BASIS,\n", + " WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + " See the License for the specific language governing permissions and\n", + " limitations under the License.\n", + "-->\n", + "<permissions>\n", + libraryTag, + libNameAttr, + filePathAttr, + implicitFromAttr, + implicitUntilAttr, + minSdkAttr, + maxSdkAttr, + dependenciesAttr, + " />\n", + "</permissions>\n", + }, "") +} + +func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleContext) { + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) + module.hideApexVariantFromMake = !apexInfo.IsForPlatform() + + libName := proptools.String(module.properties.Lib_name) + module.selfValidate(ctx) + xmlContent := module.permissionsContents(ctx) + + module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath + android.WriteFileRuleVerbatim(ctx, module.outputFilePath, xmlContent) + + module.installDirPath = android.PathForModuleInstall(ctx, "etc", module.SubDir()) + ctx.PackageFile(module.installDirPath, libName+".xml", module.outputFilePath) + + ctx.SetOutputFiles(android.OutputPaths{module.outputFilePath}.Paths(), "") +} + +func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries { + if module.hideApexVariantFromMake { + return []android.AndroidMkEntries{{ + Disabled: true, + }} + } + + return []android.AndroidMkEntries{{ + Class: "ETC", + OutputFile: android.OptionalPathForPath(module.outputFilePath), + ExtraEntries: []android.AndroidMkExtraEntriesFunc{ + func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { + entries.SetString("LOCAL_MODULE_TAGS", "optional") + entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.String()) + entries.SetString("LOCAL_INSTALLED_MODULE_STEM", module.outputFilePath.Base()) + }, + }, + }} +} + +func (module *sdkLibraryXml) selfValidate(ctx android.ModuleContext) { + module.validateAtLeastTAttributes(ctx) + module.validateMinAndMaxDeviceSdk(ctx) + module.validateMinMaxDeviceSdkAndModuleMinSdk(ctx) + module.validateOnBootclasspathBeforeRequirements(ctx) +} + +func (module *sdkLibraryXml) validateAtLeastTAttributes(ctx android.ModuleContext) { + t := android.ApiLevelOrPanic(ctx, "Tiramisu") + module.attrAtLeastT(ctx, t, module.properties.Min_device_sdk, "min_device_sdk") + module.attrAtLeastT(ctx, t, module.properties.Max_device_sdk, "max_device_sdk") + module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_before, "on_bootclasspath_before") + module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_since, "on_bootclasspath_since") +} + +func (module *sdkLibraryXml) attrAtLeastT(ctx android.ModuleContext, t android.ApiLevel, attr *string, attrName string) { + if attr != nil { + if level, err := android.ApiLevelFromUser(ctx, *attr); err == nil { + // we will inform the user of invalid inputs when we try to write the + // permissions xml file so we don't need to do it here + if t.GreaterThan(level) { + ctx.PropertyErrorf(attrName, "Attribute value needs to be at least T") + } + } + } +} + +func (module *sdkLibraryXml) validateMinAndMaxDeviceSdk(ctx android.ModuleContext) { + if module.properties.Min_device_sdk != nil && module.properties.Max_device_sdk != nil { + min, minErr := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk) + max, maxErr := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk) + if minErr == nil && maxErr == nil { + // we will inform the user of invalid inputs when we try to write the + // permissions xml file so we don't need to do it here + if min.GreaterThan(max) { + ctx.ModuleErrorf("min_device_sdk can't be greater than max_device_sdk") + } + } + } +} + +func (module *sdkLibraryXml) validateMinMaxDeviceSdkAndModuleMinSdk(ctx android.ModuleContext) { + moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level) + if module.properties.Min_device_sdk != nil { + api, err := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk) + if err == nil { + if moduleMinApi.GreaterThan(api) { + ctx.PropertyErrorf("min_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi) + } + } + } + if module.properties.Max_device_sdk != nil { + api, err := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk) + if err == nil { + if moduleMinApi.GreaterThan(api) { + ctx.PropertyErrorf("max_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi) + } + } + } +} + +func (module *sdkLibraryXml) validateOnBootclasspathBeforeRequirements(ctx android.ModuleContext) { + moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level) + if module.properties.On_bootclasspath_before != nil { + t := android.ApiLevelOrPanic(ctx, "Tiramisu") + // if we use the attribute, then we need to do this validation + if moduleMinApi.LessThan(t) { + // if minAPi is < T, then we need to have min_device_sdk (which only accepts T+) + if module.properties.Min_device_sdk == nil { + ctx.PropertyErrorf("on_bootclasspath_before", "Using this property requires that the module's min_sdk_version or the shared library's min_device_sdk is at least T") + } + } + } +} diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go index bb6331506..6031d7230 100644 --- a/java/sdk_library_test.go +++ b/java/sdk_library_test.go @@ -22,8 +22,6 @@ import ( "testing" "android/soong/android" - - "github.com/google/blueprint/proptools" ) func TestJavaSdkLibrary(t *testing.T) { @@ -55,7 +53,7 @@ func TestJavaSdkLibrary(t *testing.T) { java_library { name: "baz", srcs: ["c.java"], - libs: ["foo", "bar.stubs"], + libs: ["foo.stubs.system", "bar.stubs"], sdk_version: "system_current", } java_sdk_library { @@ -92,25 +90,25 @@ func TestJavaSdkLibrary(t *testing.T) { java_library { name: "qux", srcs: ["c.java"], - libs: ["baz", "fred", "quuz.stubs", "wilma", "barney", "betty"], + libs: ["baz", "fred.stubs", "quuz.stubs", "wilma.stubs", "barney.stubs.system", "betty.stubs.system"], sdk_version: "system_current", } java_library { name: "baz-test", srcs: ["c.java"], - libs: ["foo"], + libs: ["foo.stubs.test"], sdk_version: "test_current", } java_library { name: "baz-29", srcs: ["c.java"], - libs: ["foo"], + libs: ["sdk_system_29_foo"], sdk_version: "system_29", } java_library { name: "baz-module-30", srcs: ["c.java"], - libs: ["foo"], + libs: ["sdk_module-lib_30_foo"], sdk_version: "module_30", } `) @@ -162,11 +160,11 @@ func TestJavaSdkLibrary(t *testing.T) { baz29Javac := result.ModuleForTests("baz-29", "android_common").Rule("javac") // tests if baz-29 is actually linked to the system 29 stubs lib - android.AssertStringDoesContain(t, "baz-29 javac classpath", baz29Javac.Args["classpath"], "prebuilts/sdk/29/system/foo.jar") + android.AssertStringDoesContain(t, "baz-29 javac classpath", baz29Javac.Args["classpath"], "prebuilts/sdk/sdk_system_29_foo/android_common/combined/sdk_system_29_foo.jar") bazModule30Javac := result.ModuleForTests("baz-module-30", "android_common").Rule("javac") // tests if "baz-module-30" is actually linked to the module 30 stubs lib - android.AssertStringDoesContain(t, "baz-module-30 javac classpath", bazModule30Javac.Args["classpath"], "prebuilts/sdk/30/module-lib/foo.jar") + android.AssertStringDoesContain(t, "baz-module-30 javac classpath", bazModule30Javac.Args["classpath"], "prebuilts/sdk/sdk_module-lib_30_foo/android_common/combined/sdk_module-lib_30_foo.jar") // test if baz has exported SDK lib names foo and bar to qux qux := result.ModuleForTests("qux", "android_common") @@ -422,7 +420,7 @@ func TestJavaSdkLibrary_StubOrImplOnlyLibs(t *testing.T) { for _, expectation := range expectations { verify("sdklib.impl", expectation.lib, expectation.on_impl_classpath, expectation.in_impl_combined) - stubName := apiScopePublic.sourceStubLibraryModuleName("sdklib") + stubName := apiScopePublic.sourceStubsLibraryModuleName("sdklib") verify(stubName, expectation.lib, expectation.on_stub_classpath, expectation.in_stub_combined) } } @@ -445,7 +443,7 @@ func TestJavaSdkLibrary_DoNotAccessImplWhenItIsNotBuilt(t *testing.T) { java_library { name: "bar", srcs: ["b.java"], - libs: ["foo"], + libs: ["foo.stubs"], } `) @@ -763,7 +761,7 @@ func TestJavaSdkLibrary_SystemServer_AccessToStubScopeLibs(t *testing.T) { java_library { name: "bar", srcs: ["a.java"], - libs: ["foo-public", "foo-system", "foo-module-lib", "foo-system-server"], + libs: ["foo-public.stubs", "foo-system.stubs.system", "foo-module-lib.stubs.module_lib", "foo-system-server.stubs.system_server"], sdk_version: "system_server_current", } `) @@ -789,102 +787,26 @@ func TestJavaSdkLibrary_SystemServer_AccessToStubScopeLibs(t *testing.T) { } } -func TestJavaSdkLibrary_MissingScope(t *testing.T) { - prepareForJavaTest. - ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`requires api scope module-lib from foo but it only has \[\] available`)). - RunTestWithBp(t, ` - java_sdk_library { - name: "foo", - srcs: ["a.java"], - public: { - enabled: false, - }, - } - - java_library { - name: "baz", - srcs: ["a.java"], - libs: ["foo"], - sdk_version: "module_current", - } - `) -} - -func TestJavaSdkLibrary_FallbackScope(t *testing.T) { - android.GroupFixturePreparers( - prepareForJavaTest, - PrepareForTestWithJavaSdkLibraryFiles, - FixtureWithLastReleaseApis("foo"), - ).RunTestWithBp(t, ` - java_sdk_library { - name: "foo", - srcs: ["a.java"], - system: { - enabled: true, - }, - } - - java_library { - name: "baz", - srcs: ["a.java"], - libs: ["foo"], - // foo does not have module-lib scope so it should fallback to system - sdk_version: "module_current", - } - `) -} - -func TestJavaSdkLibrary_DefaultToStubs(t *testing.T) { - result := android.GroupFixturePreparers( - prepareForJavaTest, - PrepareForTestWithJavaSdkLibraryFiles, - FixtureWithLastReleaseApis("foo"), - ).RunTestWithBp(t, ` - java_sdk_library { - name: "foo", - srcs: ["a.java"], - system: { - enabled: true, - }, - default_to_stubs: true, - } - - java_library { - name: "baz", - srcs: ["a.java"], - libs: ["foo"], - // does not have sdk_version set, should fallback to module, - // which will then fallback to system because the module scope - // is not enabled. - } - `) - // The baz library should depend on the system stubs jar. - bazLibrary := result.ModuleForTests("baz", "android_common").Rule("javac") - if expected, actual := `^-classpath .*:out/soong/[^:]*/turbine-combined/foo\.stubs.system\.jar$`, bazLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) { - t.Errorf("expected %q, found %#q", expected, actual) - } -} - func TestJavaSdkLibraryImport(t *testing.T) { result := prepareForJavaTest.RunTestWithBp(t, ` java_library { name: "foo", srcs: ["a.java"], - libs: ["sdklib"], + libs: ["sdklib.stubs"], sdk_version: "current", } java_library { name: "foo.system", srcs: ["a.java"], - libs: ["sdklib"], + libs: ["sdklib.stubs.system"], sdk_version: "system_current", } java_library { name: "foo.test", srcs: ["a.java"], - libs: ["sdklib"], + libs: ["sdklib.stubs.test"], sdk_version: "test_current", } @@ -1017,7 +939,7 @@ func testJavaSdkLibraryImport_Preferred(t *testing.T, prefer string, preparer an java_library { name: "public", srcs: ["a.java"], - libs: ["sdklib"], + libs: ["sdklib.stubs"], sdk_version: "current", } `) @@ -1190,155 +1112,6 @@ func TestSdkLibraryImport_MetadataModuleSupersedesPreferred(t *testing.T) { } } -func TestJavaSdkLibraryEnforce(t *testing.T) { - partitionToBpOption := func(partition string) string { - switch partition { - case "system": - return "" - case "vendor": - return "soc_specific: true," - case "product": - return "product_specific: true," - default: - panic("Invalid partition group name: " + partition) - } - } - - type testConfigInfo struct { - libraryType string - fromPartition string - toPartition string - enforceProductInterface bool - enforceJavaSdkLibraryCheck bool - allowList []string - } - - createPreparer := func(info testConfigInfo) android.FixturePreparer { - bpFileTemplate := ` - java_library { - name: "foo", - srcs: ["foo.java"], - libs: ["bar"], - sdk_version: "current", - %s - } - - %s { - name: "bar", - srcs: ["bar.java"], - sdk_version: "current", - %s - } - ` - - bpFile := fmt.Sprintf(bpFileTemplate, - partitionToBpOption(info.fromPartition), - info.libraryType, - partitionToBpOption(info.toPartition)) - - return android.GroupFixturePreparers( - PrepareForTestWithJavaSdkLibraryFiles, - FixtureWithLastReleaseApis("bar"), - android.FixtureWithRootAndroidBp(bpFile), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.EnforceProductPartitionInterface = proptools.BoolPtr(info.enforceProductInterface) - variables.EnforceInterPartitionJavaSdkLibrary = proptools.BoolPtr(info.enforceJavaSdkLibraryCheck) - variables.InterPartitionJavaLibraryAllowList = info.allowList - }), - ) - } - - runTest := func(t *testing.T, info testConfigInfo, expectedErrorPattern string) { - t.Run(fmt.Sprintf("%v", info), func(t *testing.T) { - errorHandler := android.FixtureExpectsNoErrors - if expectedErrorPattern != "" { - errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorPattern) - } - android.GroupFixturePreparers( - prepareForJavaTest, - createPreparer(info), - ). - ExtendWithErrorHandler(errorHandler). - RunTest(t) - }) - } - - errorMessage := "is not allowed across the partitions" - - runTest(t, testConfigInfo{ - libraryType: "java_library", - fromPartition: "product", - toPartition: "system", - enforceProductInterface: true, - enforceJavaSdkLibraryCheck: false, - }, "") - - runTest(t, testConfigInfo{ - libraryType: "java_library", - fromPartition: "product", - toPartition: "system", - enforceProductInterface: false, - enforceJavaSdkLibraryCheck: true, - }, "") - - runTest(t, testConfigInfo{ - libraryType: "java_library", - fromPartition: "product", - toPartition: "system", - enforceProductInterface: true, - enforceJavaSdkLibraryCheck: true, - }, errorMessage) - - runTest(t, testConfigInfo{ - libraryType: "java_library", - fromPartition: "vendor", - toPartition: "system", - enforceProductInterface: true, - enforceJavaSdkLibraryCheck: true, - }, errorMessage) - - runTest(t, testConfigInfo{ - libraryType: "java_library", - fromPartition: "vendor", - toPartition: "system", - enforceProductInterface: true, - enforceJavaSdkLibraryCheck: true, - allowList: []string{"bar"}, - }, "") - - runTest(t, testConfigInfo{ - libraryType: "java_library", - fromPartition: "vendor", - toPartition: "product", - enforceProductInterface: true, - enforceJavaSdkLibraryCheck: true, - }, errorMessage) - - runTest(t, testConfigInfo{ - libraryType: "java_sdk_library", - fromPartition: "product", - toPartition: "system", - enforceProductInterface: true, - enforceJavaSdkLibraryCheck: true, - }, "") - - runTest(t, testConfigInfo{ - libraryType: "java_sdk_library", - fromPartition: "vendor", - toPartition: "system", - enforceProductInterface: true, - enforceJavaSdkLibraryCheck: true, - }, "") - - runTest(t, testConfigInfo{ - libraryType: "java_sdk_library", - fromPartition: "vendor", - toPartition: "product", - enforceProductInterface: true, - enforceJavaSdkLibraryCheck: true, - }, "") -} - func TestJavaSdkLibraryDist(t *testing.T) { result := android.GroupFixturePreparers( PrepareForTestWithJavaBuildComponents, @@ -1657,7 +1430,7 @@ func TestSdkLibraryDependency(t *testing.T) { name: "bar", srcs: ["c.java", "b.java"], libs: [ - "foo", + "foo.stubs", ], uses_libs: [ "foo", @@ -1752,7 +1525,7 @@ func TestStubResolutionOfJavaSdkLibraryInLibs(t *testing.T) { name: "mymodule", srcs: ["a.java"], sdk_version: "current", - libs: ["sdklib",], // this should be dynamically resolved to sdklib.stubs (source) or prebuilt_sdklib.stubs (prebuilt) + libs: ["sdklib.stubs",], // this should be dynamically resolved to sdklib.stubs (source) or prebuilt_sdklib.stubs (prebuilt) } ` @@ -1893,3 +1666,111 @@ func TestStubLinkType(t *testing.T) { } `) } + +func TestSdkLibDirectDependency(t *testing.T) { + android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis("foo", "bar"), + ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern([]string{ + `module "baz" variant "android_common": cannot depend directly on java_sdk_library ` + + `"foo"; try depending on "foo.stubs", or "foo.impl" instead`, + `module "baz" variant "android_common": cannot depend directly on java_sdk_library ` + + `"prebuilt_bar"; try depending on "bar.stubs", or "bar.impl" instead`, + }), + ).RunTestWithBp(t, ` + java_sdk_library { + name: "foo", + srcs: ["a.java"], + sdk_version: "current", + public: { + enabled: true, + }, + } + + java_sdk_library_import { + name: "foo", + public: { + jars: ["a.jar"], + stub_srcs: ["a.java"], + current_api: "current.txt", + removed_api: "removed.txt", + annotations: "annotations.zip", + }, + } + + java_sdk_library { + name: "bar", + srcs: ["a.java"], + sdk_version: "current", + public: { + enabled: true, + }, + } + + java_sdk_library_import { + name: "bar", + prefer: true, + public: { + jars: ["a.jar"], + stub_srcs: ["a.java"], + current_api: "current.txt", + removed_api: "removed.txt", + annotations: "annotations.zip", + }, + } + + java_library { + name: "baz", + srcs: ["b.java"], + libs: [ + "foo", + "bar", + ], + } + `) +} + +func TestSdkLibDirectDependencyWithPrebuiltSdk(t *testing.T) { + android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_sdk_version = intPtr(34) + variables.Platform_sdk_codename = stringPtr("VanillaIceCream") + variables.Platform_version_active_codenames = []string{"VanillaIceCream"} + variables.Platform_systemsdk_versions = []string{"33", "34", "VanillaIceCream"} + variables.DeviceSystemSdkVersions = []string{"VanillaIceCream"} + }), + FixtureWithPrebuiltApis(map[string][]string{ + "33": {"foo"}, + "34": {"foo"}, + "35": {"foo"}, + }), + ).ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern( + `module "baz" variant "android_common": cannot depend directly on java_sdk_library "foo"; `+ + `try depending on "sdk_public_33_foo", "sdk_system_33_foo", "sdk_test_33_foo", `+ + `"sdk_module-lib_33_foo", or "sdk_system-server_33_foo" instead`), + ).RunTestWithBp(t, ` + java_sdk_library { + name: "foo", + srcs: ["a.java"], + sdk_version: "current", + public: { + enabled: true, + }, + system: { + enabled: true, + }, + } + + java_library { + name: "baz", + srcs: ["b.java"], + libs: [ + "foo", + ], + sdk_version: "system_33", + } + `) +} diff --git a/java/testing.go b/java/testing.go index 6ed605475..4615487fb 100644 --- a/java/testing.go +++ b/java/testing.go @@ -30,6 +30,7 @@ import ( ) const defaultJavaDir = "default/java" +const testDefaultUpdatableModuleVersion = "340090000" // Test fixture preparer that will register most java build components. // @@ -61,6 +62,7 @@ var PrepareForTestWithJavaBuildComponents = android.GroupFixturePreparers( // Needed for the global lint checks provided from frameworks/base "prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar": nil, }.AddToFixture(), + android.PrepareForTestWithBuildFlag("RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION", testDefaultUpdatableModuleVersion), ) var prepareForTestWithFrameworkDeps = android.GroupFixturePreparers( @@ -388,7 +390,6 @@ func registerRequiredBuildComponentsForTest(ctx android.RegistrationContext) { RegisterStubsBuildComponents(ctx) RegisterSystemModulesBuildComponents(ctx) registerSystemserverClasspathBuildComponents(ctx) - registerLintBuildComponents(ctx) android.RegisterApexContributionsBuildComponents(ctx) } diff --git a/linkerconfig/proto/Android.bp b/linkerconfig/proto/Android.bp index 754e7bfbb..a93050251 100644 --- a/linkerconfig/proto/Android.bp +++ b/linkerconfig/proto/Android.bp @@ -15,6 +15,7 @@ cc_library_static { "//apex_available:platform", "//apex_available:anyapex", ], + visibility: ["//system/linkerconfig"], } python_library_host { diff --git a/multitree/api_imports.go b/multitree/api_imports.go deleted file mode 100644 index 51b9e07a5..000000000 --- a/multitree/api_imports.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2022 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package multitree - -import ( - "android/soong/android" - "strings" - - "github.com/google/blueprint" -) - -var ( - apiImportNameSuffix = ".apiimport" -) - -func init() { - RegisterApiImportsModule(android.InitRegistrationContext) - android.RegisterMakeVarsProvider(pctx, makeVarsProvider) -} - -func RegisterApiImportsModule(ctx android.RegistrationContext) { - ctx.RegisterModuleType("api_imports", apiImportsFactory) -} - -type ApiImports struct { - android.ModuleBase - properties apiImportsProperties -} - -type apiImportsProperties struct { - Shared_libs []string // List of C shared libraries from API surfaces - Header_libs []string // List of C header libraries from API surfaces - Apex_shared_libs []string // List of C shared libraries with APEX stubs -} - -// 'api_imports' is a module which describes modules available from API surfaces. -// This module is required to get the list of all imported API modules, because -// it is discouraged to loop and fetch all modules from its type information. The -// only module with name 'api_imports' will be used from the build. -func apiImportsFactory() android.Module { - module := &ApiImports{} - module.AddProperties(&module.properties) - android.InitAndroidModule(module) - return module -} - -func (imports *ApiImports) GenerateAndroidBuildActions(ctx android.ModuleContext) { - // ApiImport module does not generate any build actions -} - -type ApiImportInfo struct { - SharedLibs, HeaderLibs, ApexSharedLibs map[string]string -} - -var ApiImportsProvider = blueprint.NewMutatorProvider[ApiImportInfo]("deps") - -// Store module lists into ApiImportInfo and share it over mutator provider. -func (imports *ApiImports) DepsMutator(ctx android.BottomUpMutatorContext) { - generateNameMapWithSuffix := func(names []string) map[string]string { - moduleNameMap := make(map[string]string) - for _, name := range names { - moduleNameMap[name] = name + apiImportNameSuffix - } - - return moduleNameMap - } - - sharedLibs := generateNameMapWithSuffix(imports.properties.Shared_libs) - headerLibs := generateNameMapWithSuffix(imports.properties.Header_libs) - apexSharedLibs := generateNameMapWithSuffix(imports.properties.Apex_shared_libs) - - android.SetProvider(ctx, ApiImportsProvider, ApiImportInfo{ - SharedLibs: sharedLibs, - HeaderLibs: headerLibs, - ApexSharedLibs: apexSharedLibs, - }) -} - -func GetApiImportSuffix() string { - return apiImportNameSuffix -} - -func makeVarsProvider(ctx android.MakeVarsContext) { - ctx.VisitAllModules(func(m android.Module) { - if i, ok := m.(*ApiImports); ok { - ctx.Strict("API_IMPORTED_SHARED_LIBRARIES", strings.Join(i.properties.Shared_libs, " ")) - ctx.Strict("API_IMPORTED_HEADER_LIBRARIES", strings.Join(i.properties.Header_libs, " ")) - } - }) -} diff --git a/multitree/api_surface.go b/multitree/api_surface.go deleted file mode 100644 index 0f605d84a..000000000 --- a/multitree/api_surface.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2021 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package multitree - -import ( - "android/soong/android" - "github.com/google/blueprint" -) - -var ( - pctx = android.NewPackageContext("android/soong/multitree") -) - -func init() { - RegisterApiSurfaceBuildComponents(android.InitRegistrationContext) -} - -var PrepareForTestWithApiSurface = android.FixtureRegisterWithContext(RegisterApiSurfaceBuildComponents) - -func RegisterApiSurfaceBuildComponents(ctx android.RegistrationContext) { - ctx.RegisterModuleType("api_surface", ApiSurfaceFactory) -} - -type ApiSurface struct { - android.ModuleBase - ExportableModuleBase - properties apiSurfaceProperties - - taggedOutputs map[string]android.Paths -} - -type apiSurfaceProperties struct { - Contributions []string -} - -func ApiSurfaceFactory() android.Module { - module := &ApiSurface{} - module.AddProperties(&module.properties) - android.InitAndroidModule(module) - InitExportableModule(module) - return module -} - -func (surface *ApiSurface) DepsMutator(ctx android.BottomUpMutatorContext) { - if surface.properties.Contributions != nil { - ctx.AddVariationDependencies(nil, nil, surface.properties.Contributions...) - } - -} -func (surface *ApiSurface) GenerateAndroidBuildActions(ctx android.ModuleContext) { - contributionFiles := make(map[string]android.Paths) - var allOutputs android.Paths - ctx.WalkDeps(func(child, parent android.Module) bool { - if contribution, ok := child.(ApiContribution); ok { - copied := contribution.CopyFilesWithTag(ctx) - for tag, files := range copied { - contributionFiles[child.Name()+"#"+tag] = files - } - for _, paths := range copied { - allOutputs = append(allOutputs, paths...) - } - return false // no transitive dependencies - } - return false - }) - - // phony target - ctx.Build(pctx, android.BuildParams{ - Rule: blueprint.Phony, - Output: android.PathForPhony(ctx, ctx.ModuleName()), - Inputs: allOutputs, - }) - - surface.taggedOutputs = contributionFiles - - ctx.SetOutputFiles(allOutputs, "") -} - -func (surface *ApiSurface) TaggedOutputs() map[string]android.Paths { - return surface.taggedOutputs -} - -func (surface *ApiSurface) Exportable() bool { - return true -} - -var _ Exportable = (*ApiSurface)(nil) - -type ApiContribution interface { - // copy files necessaryt to construct an API surface - // For C, it will be map.txt and .h files - // For Java, it will be api.txt - CopyFilesWithTag(ctx android.ModuleContext) map[string]android.Paths // output paths - - // Generate Android.bp in out/ to use the exported .txt files - // GenerateBuildFiles(ctx ModuleContext) Paths //output paths -} diff --git a/multitree/export.go b/multitree/export.go deleted file mode 100644 index 8be8f7058..000000000 --- a/multitree/export.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2022 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package multitree - -import ( - "android/soong/android" - - "github.com/google/blueprint/proptools" -) - -type moduleExportProperty struct { - // True if the module is exported to the other components in a multi-tree. - // Any components in the multi-tree can import this module to use. - Export *bool -} - -type ExportableModuleBase struct { - properties moduleExportProperty -} - -type Exportable interface { - // Properties for the exporable module. - exportableModuleProps() *moduleExportProperty - - // Check if this module can be exported. - // If this returns false, the module will not be exported regardless of the 'export' value. - Exportable() bool - - // Returns 'true' if this module has 'export: true' - // This module will not be exported if it returns 'false' to 'Exportable()' interface even if - // it has 'export: true'. - IsExported() bool - - // Map from tags to outputs. - // Each module can tag their outputs for convenience. - TaggedOutputs() map[string]android.Paths -} - -type ExportableModule interface { - android.Module - Exportable -} - -func InitExportableModule(module ExportableModule) { - module.AddProperties(module.exportableModuleProps()) -} - -func (m *ExportableModuleBase) exportableModuleProps() *moduleExportProperty { - return &m.properties -} - -func (m *ExportableModuleBase) IsExported() bool { - return proptools.Bool(m.properties.Export) -} diff --git a/multitree/import.go b/multitree/import.go deleted file mode 100644 index 1e5c421bc..000000000 --- a/multitree/import.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2022 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package multitree - -import ( - "android/soong/android" -) - -var ( - nameSuffix = ".imported" -) - -type MultitreeImportedModuleInterface interface { - GetMultitreeImportedModuleName() string -} - -func init() { - android.RegisterModuleType("imported_filegroup", importedFileGroupFactory) - - android.PreArchMutators(RegisterMultitreePreArchMutators) -} - -type importedFileGroupProperties struct { - // Imported modules from the other components in a multi-tree - Imported []string -} - -type importedFileGroup struct { - android.ModuleBase - - properties importedFileGroupProperties - srcs android.Paths -} - -func (ifg *importedFileGroup) Name() string { - return ifg.BaseModuleName() + nameSuffix -} - -func importedFileGroupFactory() android.Module { - module := &importedFileGroup{} - module.AddProperties(&module.properties) - - android.InitAndroidModule(module) - return module -} - -var _ MultitreeImportedModuleInterface = (*importedFileGroup)(nil) - -func (ifg *importedFileGroup) GetMultitreeImportedModuleName() string { - // The base module name of the imported filegroup is used as the imported module name - return ifg.BaseModuleName() -} - -var _ android.SourceFileProducer = (*importedFileGroup)(nil) - -func (ifg *importedFileGroup) Srcs() android.Paths { - return ifg.srcs -} - -func (ifg *importedFileGroup) GenerateAndroidBuildActions(ctx android.ModuleContext) { - // srcs from this module must not be used. Adding a dot path to avoid the empty - // source failure. Still soong returns error when a module wants to build against - // this source, which is intended. - ifg.srcs = android.PathsForModuleSrc(ctx, []string{"."}) -} - -func RegisterMultitreePreArchMutators(ctx android.RegisterMutatorsContext) { - ctx.BottomUp("multitree_imported_rename", MultitreeImportedRenameMutator).Parallel() -} - -func MultitreeImportedRenameMutator(ctx android.BottomUpMutatorContext) { - if m, ok := ctx.Module().(MultitreeImportedModuleInterface); ok { - name := m.GetMultitreeImportedModuleName() - if !ctx.OtherModuleExists(name) { - // Provide an empty filegroup not to break the build while updating the metadata. - // In other cases, soong will report an error to guide users to run 'm update-meta' - // first. - if !ctx.Config().TargetMultitreeUpdateMeta() { - ctx.ModuleErrorf("\"%s\" filegroup must be imported.\nRun 'm update-meta' first to import the filegroup.", name) - } - ctx.Rename(name) - } - } -} diff --git a/multitree/metadata.go b/multitree/metadata.go deleted file mode 100644 index 0eb0efc95..000000000 --- a/multitree/metadata.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2022 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package multitree - -import ( - "android/soong/android" - "encoding/json" -) - -func init() { - android.RegisterParallelSingletonType("update-meta", UpdateMetaSingleton) -} - -func UpdateMetaSingleton() android.Singleton { - return &updateMetaSingleton{} -} - -type jsonImported struct { - FileGroups map[string][]string `json:",omitempty"` -} - -type metadataJsonFlags struct { - Imported jsonImported `json:",omitempty"` - Exported map[string][]string `json:",omitempty"` -} - -type updateMetaSingleton struct { - importedModules []string - generatedMetadataFile android.OutputPath -} - -func (s *updateMetaSingleton) GenerateBuildActions(ctx android.SingletonContext) { - metadata := metadataJsonFlags{ - Imported: jsonImported{ - FileGroups: make(map[string][]string), - }, - Exported: make(map[string][]string), - } - ctx.VisitAllModules(func(module android.Module) { - if ifg, ok := module.(*importedFileGroup); ok { - metadata.Imported.FileGroups[ifg.BaseModuleName()] = ifg.properties.Imported - } - if e, ok := module.(ExportableModule); ok { - if e.IsExported() && e.Exportable() { - for tag, files := range e.TaggedOutputs() { - // TODO(b/219846705): refactor this to a dictionary - metadata.Exported[e.Name()+":"+tag] = append(metadata.Exported[e.Name()+":"+tag], files.Strings()...) - } - } - } - }) - jsonStr, err := json.Marshal(metadata) - if err != nil { - ctx.Errorf(err.Error()) - } - s.generatedMetadataFile = android.PathForOutput(ctx, "multitree", "metadata.json") - android.WriteFileRule(ctx, s.generatedMetadataFile, string(jsonStr)) -} - -func (s *updateMetaSingleton) MakeVars(ctx android.MakeVarsContext) { - ctx.Strict("MULTITREE_METADATA", s.generatedMetadataFile.String()) -} diff --git a/phony/Android.bp b/phony/Android.bp index db5efc98e..2e250c699 100644 --- a/phony/Android.bp +++ b/phony/Android.bp @@ -13,4 +13,5 @@ bootstrap_go_package { "phony.go", ], pluginFor: ["soong_build"], + visibility: ["//visibility:public"], } diff --git a/response/Android.bp b/response/Android.bp index e19981f8f..2f319fec9 100644 --- a/response/Android.bp +++ b/response/Android.bp @@ -13,4 +13,8 @@ bootstrap_go_package { testSrcs: [ "response_test.go", ], + visibility: [ + "//build/make/tools/compliance", + "//build/soong:__subpackages__", + ], } diff --git a/rust/Android.bp b/rust/Android.bp index 53c94621e..781f325d1 100644 --- a/rust/Android.bp +++ b/rust/Android.bp @@ -61,4 +61,5 @@ bootstrap_go_package { "test_test.go", ], pluginFor: ["soong_build"], + visibility: ["//visibility:public"], } diff --git a/rust/bindgen.go b/rust/bindgen.go index 31aa13725..abb51814e 100644 --- a/rust/bindgen.go +++ b/rust/bindgen.go @@ -198,18 +198,20 @@ func (b *bindgenDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) andr cflags = append(cflags, "-D__ANDROID_VNDK__") if ctx.RustModule().InVendor() { cflags = append(cflags, "-D__ANDROID_VENDOR__") - - vendorApiLevel := ctx.Config().VendorApiLevel() - if vendorApiLevel == "" { - // TODO(b/314036847): This is a fallback for UDC targets. - // This must be a build failure when UDC is no longer built - // from this source tree. - vendorApiLevel = ctx.Config().PlatformSdkVersion().String() - } - cflags = append(cflags, "-D__ANDROID_VENDOR_API__="+vendorApiLevel) } else if ctx.RustModule().InProduct() { cflags = append(cflags, "-D__ANDROID_PRODUCT__") } + + // Define __ANDROID_VENDOR_API__ for both product and vendor variants + // because they both use the same LLNDK libraries. + vendorApiLevel := ctx.Config().VendorApiLevel() + if vendorApiLevel == "" { + // TODO(b/314036847): This is a fallback for UDC targets. + // This must be a build failure when UDC is no longer built + // from this source tree. + vendorApiLevel = ctx.Config().PlatformSdkVersion().String() + } + cflags = append(cflags, "-D__ANDROID_VENDOR_API__="+vendorApiLevel) } if ctx.RustModule().InRecovery() { diff --git a/rust/clippy.go b/rust/clippy.go index 6f0ed7ff1..426fd7393 100644 --- a/rust/clippy.go +++ b/rust/clippy.go @@ -38,11 +38,14 @@ func (c *clippy) props() []interface{} { } func (c *clippy) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) { - enabled, lints, err := config.ClippyLintsForDir(ctx.ModuleDir(), c.Properties.Clippy_lints) + dirEnabled, lints, err := config.ClippyLintsForDir(ctx.ModuleDir(), c.Properties.Clippy_lints) if err != nil { ctx.PropertyErrorf("clippy_lints", err.Error()) } - flags.Clippy = enabled + + envDisable := ctx.Config().IsEnvTrue("SOONG_DISABLE_CLIPPY") + + flags.Clippy = dirEnabled && !envDisable flags.ClippyFlags = append(flags.ClippyFlags, lints) return flags, deps } diff --git a/rust/compiler.go b/rust/compiler.go index a2546a194..5bce16b8d 100644 --- a/rust/compiler.go +++ b/rust/compiler.go @@ -39,13 +39,13 @@ type compiler interface { initialize(ctx ModuleContext) compilerFlags(ctx ModuleContext, flags Flags) Flags cfgFlags(ctx ModuleContext, flags Flags) Flags - featureFlags(ctx ModuleContext, flags Flags) Flags + featureFlags(ctx ModuleContext, module *Module, flags Flags) Flags compilerProps() []interface{} compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput compilerDeps(ctx DepsContext, deps Deps) Deps crateName() string edition() string - features() []string + features(ctx android.ConfigurableEvaluatorContext, module *Module) []string rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath Thinlto() bool @@ -194,7 +194,7 @@ type BaseCompilerProperties struct { Crate_name string `android:"arch_variant"` // list of features to enable for this crate - Features []string `android:"arch_variant"` + Features proptools.Configurable[[]string] `android:"arch_variant"` // list of configuration options to enable for this crate. To enable features, use the "features" property. Cfgs proptools.Configurable[[]string] `android:"arch_variant"` @@ -346,22 +346,23 @@ func cfgsToFlags(cfgs []string) []string { return flags } -func (compiler *baseCompiler) features() []string { - return compiler.Properties.Features +func (compiler *baseCompiler) features(ctx android.ConfigurableEvaluatorContext, module *Module) []string { + eval := module.ConfigurableEvaluator(ctx) + return compiler.Properties.Features.GetOrDefault(eval, nil) } -func (compiler *baseCompiler) featuresToFlags() []string { +func (compiler *baseCompiler) featuresToFlags(ctx android.ConfigurableEvaluatorContext, module *Module) []string { flags := []string{} - for _, feature := range compiler.features() { + for _, feature := range compiler.features(ctx, module) { flags = append(flags, "--cfg 'feature=\""+feature+"\"'") } return flags } -func (compiler *baseCompiler) featureFlags(ctx ModuleContext, flags Flags) Flags { - flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags()...) - flags.RustdocFlags = append(flags.RustdocFlags, compiler.featuresToFlags()...) +func (compiler *baseCompiler) featureFlags(ctx ModuleContext, module *Module, flags Flags) Flags { + flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags(ctx, module)...) + flags.RustdocFlags = append(flags.RustdocFlags, compiler.featuresToFlags(ctx, module)...) return flags } diff --git a/rust/config/Android.bp b/rust/config/Android.bp index 79ea7a174..25f7580d0 100644 --- a/rust/config/Android.bp +++ b/rust/config/Android.bp @@ -24,4 +24,8 @@ bootstrap_go_package { "x86_64_device.go", "arm64_linux_host.go", ], + visibility: [ + "//build/soong:__subpackages__", + "//prebuilts/rust/soong", + ], } diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go index 9850570c2..94a445700 100644 --- a/rust/config/arm64_device.go +++ b/rust/config/arm64_device.go @@ -35,8 +35,13 @@ var ( }, "armv8-2a": []string{}, "armv8-2a-dotprod": []string{}, + + // branch-protection=bti,pac-ret is equivalent to Clang's mbranch-protection=standard "armv9-a": []string{ - // branch-protection=bti,pac-ret is equivalent to Clang's mbranch-protection=standard + "-Z branch-protection=bti,pac-ret", + "-Z stack-protector=none", + }, + "armv9-2a": []string{ "-Z branch-protection=bti,pac-ret", "-Z stack-protector=none", }, diff --git a/rust/config/global.go b/rust/config/global.go index 990a64331..68a74c204 100644 --- a/rust/config/global.go +++ b/rust/config/global.go @@ -24,7 +24,7 @@ import ( var ( pctx = android.NewPackageContext("android/soong/rust/config") - RustDefaultVersion = "1.80.1" + RustDefaultVersion = "1.81.0" RustDefaultBase = "prebuilts/rust/" DefaultEdition = "2021" Stdlibs = []string{ diff --git a/rust/config/x86_64_device.go b/rust/config/x86_64_device.go index fee1923b0..3c484d894 100644 --- a/rust/config/x86_64_device.go +++ b/rust/config/x86_64_device.go @@ -29,6 +29,7 @@ var ( x86_64ArchVariantRustFlags = map[string][]string{ "": []string{}, + "alderlake": []string{"-C target-cpu=alderlake"}, "broadwell": []string{"-C target-cpu=broadwell"}, "goldmont": []string{"-C target-cpu=goldmont"}, "goldmont-plus": []string{"-C target-cpu=goldmont-plus"}, diff --git a/rust/config/x86_device.go b/rust/config/x86_device.go index 5d9d88aef..3c597cc23 100644 --- a/rust/config/x86_device.go +++ b/rust/config/x86_device.go @@ -27,6 +27,7 @@ var ( x86ArchVariantRustFlags = map[string][]string{ "": []string{}, + "alderlake": []string{"-C target-cpu=alderlake"}, "atom": []string{"-C target-cpu=atom"}, "broadwell": []string{"-C target-cpu=broadwell"}, "goldmont": []string{"-C target-cpu=goldmont"}, diff --git a/rust/coverage.go b/rust/coverage.go index 91a78060d..381fcf1b4 100644 --- a/rust/coverage.go +++ b/rust/coverage.go @@ -87,10 +87,6 @@ func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags } func (cov *coverage) begin(ctx BaseModuleContext) { - if ctx.Host() { - // Host coverage not yet supported. - } else { - // Update useSdk and sdkVersion args if Rust modules become SDK aware. - cov.Properties = cc.SetCoverageProperties(ctx, cov.Properties, ctx.RustModule().nativeCoverage(), false, "") - } + // Update useSdk and sdkVersion args if Rust modules become SDK aware. + cov.Properties = cc.SetCoverageProperties(ctx, cov.Properties, ctx.RustModule().nativeCoverage(), false, "") } diff --git a/rust/project_json.go b/rust/project_json.go index 24dcc89f1..6c1e32047 100644 --- a/rust/project_json.go +++ b/rust/project_json.go @@ -151,7 +151,7 @@ func (singleton *projectGeneratorSingleton) addCrate(ctx android.SingletonContex crate.Env["OUT_DIR"] = rModule.compiler.cargoOutDir().String() } - for _, feature := range rModule.compiler.features() { + for _, feature := range rModule.compiler.features(ctx, rModule) { crate.Cfg = append(crate.Cfg, "feature=\""+feature+"\"") } diff --git a/rust/rust.go b/rust/rust.go index 240c22178..5602edc79 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -29,7 +29,6 @@ import ( "android/soong/cc" cc_config "android/soong/cc/config" "android/soong/fuzz" - "android/soong/multitree" "android/soong/rust/config" ) @@ -921,7 +920,7 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { if mod.compiler != nil { flags = mod.compiler.compilerFlags(ctx, flags) flags = mod.compiler.cfgFlags(ctx, flags) - flags = mod.compiler.featureFlags(ctx, flags) + flags = mod.compiler.featureFlags(ctx, mod, flags) } if mod.coverage != nil { flags, deps = mod.coverage.flags(ctx, flags, deps) @@ -1218,47 +1217,6 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { skipModuleList := map[string]bool{} - var apiImportInfo multitree.ApiImportInfo - hasApiImportInfo := false - - ctx.VisitDirectDeps(func(dep android.Module) { - if dep.Name() == "api_imports" { - apiImportInfo, _ = android.OtherModuleProvider(ctx, dep, multitree.ApiImportsProvider) - hasApiImportInfo = true - } - }) - - if hasApiImportInfo { - targetStubModuleList := map[string]string{} - targetOrigModuleList := map[string]string{} - - // Search for dependency which both original module and API imported library with APEX stub exists - ctx.VisitDirectDeps(func(dep android.Module) { - depName := ctx.OtherModuleName(dep) - if apiLibrary, ok := apiImportInfo.ApexSharedLibs[depName]; ok { - targetStubModuleList[apiLibrary] = depName - } - }) - ctx.VisitDirectDeps(func(dep android.Module) { - depName := ctx.OtherModuleName(dep) - if origLibrary, ok := targetStubModuleList[depName]; ok { - targetOrigModuleList[origLibrary] = depName - } - }) - - // Decide which library should be used between original and API imported library - ctx.VisitDirectDeps(func(dep android.Module) { - depName := ctx.OtherModuleName(dep) - if apiLibrary, ok := targetOrigModuleList[depName]; ok { - if cc.ShouldUseStubForApex(ctx, dep) { - skipModuleList[depName] = true - } else { - skipModuleList[apiLibrary] = true - } - } - }) - } - var transitiveAndroidMkSharedLibs []*android.DepSet[string] var directAndroidMkSharedLibs []string @@ -1609,13 +1567,6 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { deps := mod.deps(ctx) var commonDepVariations []blueprint.Variation - apiImportInfo := cc.GetApiImports(mod, actx) - if mod.usePublicApi() || mod.useVendorApi() { - for idx, lib := range deps.SharedLibs { - deps.SharedLibs[idx] = cc.GetReplaceModuleName(lib, apiImportInfo.SharedLibs) - } - } - if ctx.Os() == android.Android { deps.SharedLibs, _ = cc.FilterNdkLibs(mod, ctx.Config(), deps.SharedLibs) } @@ -1708,15 +1659,7 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { variations := []blueprint.Variation{ {Mutator: "link", Variation: "shared"}, } - // For core variant, add a dep on the implementation (if it exists) and its .apiimport (if it exists) - // GenerateAndroidBuildActions will pick the correct impl/stub based on the api_domain boundary - if _, ok := apiImportInfo.ApexSharedLibs[name]; !ok || ctx.OtherModuleExists(name) { - cc.AddSharedLibDependenciesWithVersions(ctx, mod, variations, depTag, name, version, false) - } - - if apiLibraryName, ok := apiImportInfo.ApexSharedLibs[name]; ok { - cc.AddSharedLibDependenciesWithVersions(ctx, mod, variations, depTag, apiLibraryName, version, false) - } + cc.AddSharedLibDependenciesWithVersions(ctx, mod, variations, depTag, name, version, false) } for _, lib := range deps.WholeStaticLibs { diff --git a/scripts/Android.bp b/scripts/Android.bp index 3d81b83c2..00b3ca591 100644 --- a/scripts/Android.bp +++ b/scripts/Android.bp @@ -184,12 +184,21 @@ python_binary_host { libs: ["ninja_rsp"], } +python_binary_host { + name: "lint_strict_updatability_checks", + main: "lint_strict_updatability_checks.py", + srcs: [ + "lint_strict_updatability_checks.py", + ], + libs: ["ninja_rsp"], +} + python_test_host { - name: "lint_project_xml_test", - main: "lint_project_xml_test.py", + name: "lint_strict_updatability_checks_test", + main: "lint_strict_updatability_checks_test.py", srcs: [ - "lint_project_xml_test.py", - "lint_project_xml.py", + "lint_strict_updatability_checks_test.py", + "lint_strict_updatability_checks.py", ], libs: ["ninja_rsp"], test_suites: ["general-tests"], diff --git a/scripts/check_prebuilt_presigned_apk.py b/scripts/check_prebuilt_presigned_apk.py index abab2e146..db64f90c6 100755 --- a/scripts/check_prebuilt_presigned_apk.py +++ b/scripts/check_prebuilt_presigned_apk.py @@ -36,7 +36,7 @@ def has_preprocessed_issues(args, *, fail=False): if fail: sys.exit(args.apk + ': Contains compressed JNI libraries') return True - # It's ok for non-privileged apps to have compressed dex files, see go/gms-uncompressed-jni-slides + # It's ok for non-privileged apps to have compressed dex files if args.privileged and args.uncompress_priv_app_dex: if info.filename.endswith('.dex') and info.compress_type != zipfile.ZIP_STORED: if fail: @@ -46,6 +46,10 @@ def has_preprocessed_issues(args, *, fail=False): def main(): + # This script enforces requirements for presigned apps as documented in: + # go/gms-uncompressed-jni-slides + # https://docs.partner.android.com/gms/building/integrating/jni-libs + # https://docs.partner.android.com/gms/policies/domains/mba#jni-lib parser = argparse.ArgumentParser() parser.add_argument('--aapt2', help = "the path to the aapt2 executable") parser.add_argument('--zipalign', help = "the path to the zipalign executable") diff --git a/scripts/lint_project_xml.py b/scripts/lint_project_xml.py index c40b07d38..ce6aa21a2 100755 --- a/scripts/lint_project_xml.py +++ b/scripts/lint_project_xml.py @@ -75,8 +75,6 @@ def parse_args(): help='file containing the module\'s manifest.') parser.add_argument('--merged_manifest', dest='merged_manifest', help='file containing merged manifest for the module and its dependencies.') - parser.add_argument('--baseline', dest='baseline_path', - help='file containing baseline lint issues.') parser.add_argument('--library', dest='library', action='store_true', help='mark the module as a library.') parser.add_argument('--test', dest='test', action='store_true', @@ -94,8 +92,6 @@ def parse_args(): help='treat a lint issue as a warning.') group.add_argument('--disable_check', dest='checks', action=check_action('ignore'), default=[], help='disable a lint issue.') - group.add_argument('--disallowed_issues', dest='disallowed_issues', default=[], - help='lint issues disallowed in the baseline file') return parser.parse_args() @@ -140,30 +136,10 @@ def write_config_xml(f, args): f.write("</lint>\n") -def check_baseline_for_disallowed_issues(baseline, forced_checks): - issues_element = baseline.documentElement - if issues_element.tagName != 'issues': - raise RuntimeError('expected issues tag at root') - issues = issues_element.getElementsByTagName('issue') - disallowed = set() - for issue in issues: - id = issue.getAttribute('id') - if id in forced_checks: - disallowed.add(id) - return disallowed - - def main(): """Program entry point.""" args = parse_args() - if args.baseline_path: - baseline = minidom.parse(args.baseline_path) - disallowed_issues = check_baseline_for_disallowed_issues(baseline, args.disallowed_issues) - if disallowed_issues: - sys.exit('disallowed issues %s found in lint baseline file %s for module %s' - % (disallowed_issues, args.baseline_path, args.name)) - if args.project_out: with open(args.project_out, 'w') as f: write_project_xml(f, args) diff --git a/scripts/lint_strict_updatability_checks.py b/scripts/lint_strict_updatability_checks.py new file mode 100755 index 000000000..5b5dfd81a --- /dev/null +++ b/scripts/lint_strict_updatability_checks.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2018 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. +# + +"""This file checks baselines passed to Android Lint for checks that must not be baselined.""" + +import argparse +import sys +from xml.dom import minidom + +from ninja_rsp import NinjaRspFileReader + + +def parse_args(): + """Parse commandline arguments.""" + + def convert_arg_line_to_args(arg_line): + for arg in arg_line.split(): + if arg.startswith('#'): + return + if not arg.strip(): + continue + yield arg + + parser = argparse.ArgumentParser(fromfile_prefix_chars='@') + parser.convert_arg_line_to_args = convert_arg_line_to_args + parser.add_argument('--name', dest='name', + help='name of the module.') + parser.add_argument('--baselines', dest='baselines', action='append', default=[], + help='file containing whitespace separated list of baseline files.') + parser.add_argument('--disallowed_issues', dest='disallowed_issues', default=[], + help='lint issues disallowed in the baseline file') + return parser.parse_args() + + +def check_baseline_for_disallowed_issues(baseline, forced_checks): + issues_element = baseline.documentElement + if issues_element.tagName != 'issues': + raise RuntimeError('expected issues tag at root') + issues = issues_element.getElementsByTagName('issue') + disallowed = set() + for issue in issues: + id = issue.getAttribute('id') + if id in forced_checks: + disallowed.add(id) + return disallowed + + +def main(): + """Program entry point.""" + args = parse_args() + + error = False + for baseline_rsp_file in args.baselines: + for baseline_path in NinjaRspFileReader(baseline_rsp_file): + baseline = minidom.parse(baseline_path) + disallowed_issues = check_baseline_for_disallowed_issues(baseline, args.disallowed_issues) + if disallowed_issues: + print('disallowed issues %s found in lint baseline file %s for module %s' + % (disallowed_issues, baseline_path, args.name)) + error = True + + if error: + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/scripts/lint_project_xml_test.py b/scripts/lint_strict_updatability_checks_test.py index 344691d00..fd8610f3a 100644..100755 --- a/scripts/lint_project_xml_test.py +++ b/scripts/lint_strict_updatability_checks_test.py @@ -15,12 +15,12 @@ # limitations under the License. # -"""Unit tests for lint_project_xml.py.""" +"""Unit tests for lint_strict_updatability_checks.py.""" import unittest from xml.dom import minidom -import lint_project_xml +import lint_strict_updatability_checks class CheckBaselineForDisallowedIssuesTest(unittest.TestCase): @@ -44,7 +44,7 @@ class CheckBaselineForDisallowedIssuesTest(unittest.TestCase): '</issues>\n') def test_check_baseline_for_disallowed_issues(self): - disallowed_issues = lint_project_xml.check_baseline_for_disallowed_issues(self.baseline_xml, ["foo", "bar", "qux"]) + disallowed_issues = lint_strict_updatability_checks.check_baseline_for_disallowed_issues(self.baseline_xml, ["foo", "bar", "qux"]) self.assertEqual({"foo", "bar"}, disallowed_issues) diff --git a/scripts/manifest.py b/scripts/manifest.py index 81f9c61a8..32603e869 100755 --- a/scripts/manifest.py +++ b/scripts/manifest.py @@ -23,9 +23,40 @@ from xml.dom import minidom android_ns = 'http://schemas.android.com/apk/res/android' +def get_or_create_applications(doc, manifest): + """Get all <application> tags from the manifest, or create one if none exist. + Multiple <application> tags may exist when manifest feature flagging is used. + """ + applications = get_children_with_tag(manifest, 'application') + if len(applications) == 0: + application = doc.createElement('application') + indent = get_indent(manifest.firstChild, 1) + first = manifest.firstChild + manifest.insertBefore(doc.createTextNode(indent), first) + manifest.insertBefore(application, first) + applications.append(application) + return applications + + +def get_or_create_uses_sdks(doc, manifest): + """Get all <uses-sdk> tags from the manifest, or create one if none exist. + Multiple <uses-sdk> tags may exist when manifest feature flagging is used. + """ + uses_sdks = get_children_with_tag(manifest, 'uses-sdk') + if len(uses_sdks) == 0: + uses_sdk = doc.createElement('uses-sdk') + indent = get_indent(manifest.firstChild, 1) + manifest.insertBefore(uses_sdk, manifest.firstChild) + + # Insert an indent before uses-sdk to line it up with the indentation of the + # other children of the <manifest> tag. + manifest.insertBefore(doc.createTextNode(indent), manifest.firstChild) + uses_sdks.append(uses_sdk) + return uses_sdks + def get_children_with_tag(parent, tag_name): children = [] - for child in parent.childNodes: + for child in parent.childNodes: if child.nodeType == minidom.Node.ELEMENT_NODE and \ child.tagName == tag_name: children.append(child) diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py index b10125994..1e32d1d7d 100755 --- a/scripts/manifest_check.py +++ b/scripts/manifest_check.py @@ -25,10 +25,7 @@ import subprocess import sys from xml.dom import minidom -from manifest import android_ns -from manifest import get_children_with_tag -from manifest import parse_manifest -from manifest import write_xml +from manifest import * class ManifestMismatchError(Exception): @@ -122,7 +119,7 @@ def enforce_uses_libraries(manifest, required, optional, missing_optional, relax # handles module names specified in Android.bp properties. However not all # <uses-library> entries in the manifest correspond to real modules: some of # the optional libraries may be missing at build time. Therefor this script - # accepts raw module names as spelled in Android.bp/Amdroid.mk and trims the + # accepts raw module names as spelled in Android.bp/Android.mk and trims the # optional namespace part manually. required = trim_namespace_parts(required) optional = trim_namespace_parts(optional) @@ -205,15 +202,9 @@ def extract_uses_libs_xml(xml): """Extract <uses-library> tags from the manifest.""" manifest = parse_manifest(xml) - elems = get_children_with_tag(manifest, 'application') - if len(elems) > 1: - raise RuntimeError('found multiple <application> tags') - if not elems: - return [], [], [] - - application = elems[0] - - libs = get_children_with_tag(application, 'uses-library') + libs = [child + for application in get_or_create_applications(xml, manifest) + for child in get_children_with_tag(application, 'uses-library')] required = [uses_library_name(x) for x in libs if uses_library_required(x)] optional = [ @@ -266,7 +257,7 @@ def extract_target_sdk_version(manifest, is_apk=False): manifest: manifest (either parsed XML or aapt dump of APK) is_apk: if the manifest comes from an APK or an XML file """ - if is_apk: #pylint: disable=no-else-return + if is_apk: #pylint: disable=no-else-return return extract_target_sdk_version_apk(manifest) else: return extract_target_sdk_version_xml(manifest) @@ -376,7 +367,7 @@ def main(): # Create a status file that is empty on success, or contains an # error message on failure. When exceptions are suppressed, - # dexpreopt command command will check file size to determine if + # dexpreopt command will check file size to determine if # the check has failed. if args.enforce_uses_libraries_status: with open(args.enforce_uses_libraries_status, 'w') as f: @@ -386,7 +377,7 @@ def main(): if args.extract_target_sdk_version: try: print(extract_target_sdk_version(manifest, is_apk)) - except: #pylint: disable=bare-except + except: #pylint: disable=bare-except # Failed; don't crash, return "any" SDK version. This will # result in dexpreopt not adding any compatibility libraries. print(10000) diff --git a/scripts/manifest_check_test.py b/scripts/manifest_check_test.py index 8003b3e19..abe0d8b0e 100755 --- a/scripts/manifest_check_test.py +++ b/scripts/manifest_check_test.py @@ -44,8 +44,8 @@ def required_apk(value): class EnforceUsesLibrariesTest(unittest.TestCase): """Unit tests for add_extract_native_libs function.""" - def run_test(self, xml, apk, uses_libraries=[], optional_uses_libraries=[], - missing_optional_uses_libraries=[]): #pylint: disable=dangerous-default-value + def run_test(self, xml, apk, uses_libraries=(), optional_uses_libraries=(), + missing_optional_uses_libraries=()): #pylint: disable=dangerous-default-value doc = minidom.parseString(xml) try: relax = False @@ -114,14 +114,14 @@ class EnforceUsesLibrariesTest(unittest.TestCase): self.assertFalse(matches) def test_missing_uses_library(self): - xml = self.xml_tmpl % ('') - apk = self.apk_tmpl % ('') + xml = self.xml_tmpl % '' + apk = self.apk_tmpl % '' matches = self.run_test(xml, apk, uses_libraries=['foo']) self.assertFalse(matches) def test_missing_optional_uses_library(self): - xml = self.xml_tmpl % ('') - apk = self.apk_tmpl % ('') + xml = self.xml_tmpl % '' + apk = self.apk_tmpl % '' matches = self.run_test(xml, apk, optional_uses_libraries=['foo']) self.assertFalse(matches) @@ -234,6 +234,32 @@ class EnforceUsesLibrariesTest(unittest.TestCase): optional_uses_libraries=['//x/y/z:bar']) self.assertTrue(matches) + def test_multiple_applications(self): + xml = """<?xml version="1.0" encoding="utf-8"?> + <manifest xmlns:android="http://schemas.android.com/apk/res/android"> + <application android:featureFlag="foo"> + <uses-library android:name="foo" /> + <uses-library android:name="bar" android:required="false" /> + </application> + <application android:featureFlag="!foo"> + <uses-library android:name="foo" /> + <uses-library android:name="qux" android:required="false" /> + </application> + </manifest> + """ + apk = self.apk_tmpl % ('\n'.join([ + uses_library_apk('foo'), + uses_library_apk('bar', required_apk(False)), + uses_library_apk('foo'), + uses_library_apk('qux', required_apk(False)) + ])) + matches = self.run_test( + xml, + apk, + uses_libraries=['//x/y/z:foo'], + optional_uses_libraries=['//x/y/z:bar', '//x/y/z:qux']) + self.assertTrue(matches) + class ExtractTargetSdkVersionTest(unittest.TestCase): @@ -256,12 +282,12 @@ class ExtractTargetSdkVersionTest(unittest.TestCase): "targetSdkVersion:'%s'\n" "uses-permission: name='android.permission.ACCESS_NETWORK_STATE'\n") - def test_targert_sdk_version_28(self): + def test_target_sdk_version_28(self): xml = self.xml_tmpl % '28' apk = self.apk_tmpl % '28' self.run_test(xml, apk, '28') - def test_targert_sdk_version_29(self): + def test_target_sdk_version_29(self): xml = self.xml_tmpl % '29' apk = self.apk_tmpl % '29' self.run_test(xml, apk, '29') diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py index 58079aa5d..9847ad5bb 100755 --- a/scripts/manifest_fixer.py +++ b/scripts/manifest_fixer.py @@ -23,15 +23,7 @@ import sys from xml.dom import minidom -from manifest import android_ns -from manifest import compare_version_gt -from manifest import ensure_manifest_android_ns -from manifest import find_child_with_attribute -from manifest import get_children_with_tag -from manifest import get_indent -from manifest import parse_manifest -from manifest import write_xml - +from manifest import * def parse_args(): """Parse commandline arguments.""" @@ -48,9 +40,9 @@ def parse_args(): parser.add_argument('--library', dest='library', action='store_true', help='manifest is for a static library') parser.add_argument('--uses-library', dest='uses_libraries', action='append', - help='specify additional <uses-library> tag to add. android:requred is set to true') + help='specify additional <uses-library> tag to add. android:required is set to true') parser.add_argument('--optional-uses-library', dest='optional_uses_libraries', action='append', - help='specify additional <uses-library> tag to add. android:requred is set to false') + help='specify additional <uses-library> tag to add. android:required is set to false') parser.add_argument('--uses-non-sdk-api', dest='uses_non_sdk_api', action='store_true', help='manifest is for a package built against the platform') parser.add_argument('--logging-parent', dest='logging_parent', default='', @@ -91,47 +83,33 @@ def raise_min_sdk_version(doc, min_sdk_version, target_sdk_version, library): manifest = parse_manifest(doc) - # Get or insert the uses-sdk element - uses_sdk = get_children_with_tag(manifest, 'uses-sdk') - if len(uses_sdk) > 1: - raise RuntimeError('found multiple uses-sdk elements') - elif len(uses_sdk) == 1: - element = uses_sdk[0] - else: - element = doc.createElement('uses-sdk') - indent = get_indent(manifest.firstChild, 1) - manifest.insertBefore(element, manifest.firstChild) - - # Insert an indent before uses-sdk to line it up with the indentation of the - # other children of the <manifest> tag. - manifest.insertBefore(doc.createTextNode(indent), manifest.firstChild) - - # Get or insert the minSdkVersion attribute. If it is already present, make - # sure it as least the requested value. - min_attr = element.getAttributeNodeNS(android_ns, 'minSdkVersion') - if min_attr is None: - min_attr = doc.createAttributeNS(android_ns, 'android:minSdkVersion') - min_attr.value = min_sdk_version - element.setAttributeNode(min_attr) - else: - if compare_version_gt(min_sdk_version, min_attr.value): + for uses_sdk in get_or_create_uses_sdks(doc, manifest): + # Get or insert the minSdkVersion attribute. If it is already present, make + # sure it as least the requested value. + min_attr = uses_sdk.getAttributeNodeNS(android_ns, 'minSdkVersion') + if min_attr is None: + min_attr = doc.createAttributeNS(android_ns, 'android:minSdkVersion') min_attr.value = min_sdk_version - - # Insert the targetSdkVersion attribute if it is missing. If it is already - # present leave it as is. - target_attr = element.getAttributeNodeNS(android_ns, 'targetSdkVersion') - if target_attr is None: - target_attr = doc.createAttributeNS(android_ns, 'android:targetSdkVersion') - if library: - # TODO(b/117122200): libraries shouldn't set targetSdkVersion at all, but - # ManifestMerger treats minSdkVersion="Q" as targetSdkVersion="Q" if it - # is empty. Set it to something low so that it will be overriden by the - # main manifest, but high enough that it doesn't cause implicit - # permissions grants. - target_attr.value = '16' + uses_sdk.setAttributeNode(min_attr) else: - target_attr.value = target_sdk_version - element.setAttributeNode(target_attr) + if compare_version_gt(min_sdk_version, min_attr.value): + min_attr.value = min_sdk_version + + # Insert the targetSdkVersion attribute if it is missing. If it is already + # present leave it as is. + target_attr = uses_sdk.getAttributeNodeNS(android_ns, 'targetSdkVersion') + if target_attr is None: + target_attr = doc.createAttributeNS(android_ns, 'android:targetSdkVersion') + if library: + # TODO(b/117122200): libraries shouldn't set targetSdkVersion at all, but + # ManifestMerger treats minSdkVersion="Q" as targetSdkVersion="Q" if it + # is empty. Set it to something low so that it will be overridden by the + # main manifest, but high enough that it doesn't cause implicit + # permissions grants. + target_attr.value = '16' + else: + target_attr.value = target_sdk_version + uses_sdk.setAttributeNode(target_attr) def add_logging_parent(doc, logging_parent_value): @@ -147,37 +125,27 @@ def add_logging_parent(doc, logging_parent_value): manifest = parse_manifest(doc) logging_parent_key = 'android.content.pm.LOGGING_PARENT' - elems = get_children_with_tag(manifest, 'application') - application = elems[0] if len(elems) == 1 else None - if len(elems) > 1: - raise RuntimeError('found multiple <application> tags') - elif not elems: - application = doc.createElement('application') - indent = get_indent(manifest.firstChild, 1) - first = manifest.firstChild - manifest.insertBefore(doc.createTextNode(indent), first) - manifest.insertBefore(application, first) - - indent = get_indent(application.firstChild, 2) - - last = application.lastChild - if last is not None and last.nodeType != minidom.Node.TEXT_NODE: - last = None - - if not find_child_with_attribute(application, 'meta-data', android_ns, - 'name', logging_parent_key): - ul = doc.createElement('meta-data') - ul.setAttributeNS(android_ns, 'android:name', logging_parent_key) - ul.setAttributeNS(android_ns, 'android:value', logging_parent_value) - application.insertBefore(doc.createTextNode(indent), last) - application.insertBefore(ul, last) + for application in get_or_create_applications(doc, manifest): + indent = get_indent(application.firstChild, 2) + last = application.lastChild + if last is not None and last.nodeType != minidom.Node.TEXT_NODE: + last = None - # align the closing tag with the opening tag if it's not - # indented - if last and last.nodeType != minidom.Node.TEXT_NODE: - indent = get_indent(application.previousSibling, 1) - application.appendChild(doc.createTextNode(indent)) + if not find_child_with_attribute(application, 'meta-data', android_ns, + 'name', logging_parent_key): + ul = doc.createElement('meta-data') + ul.setAttributeNS(android_ns, 'android:name', logging_parent_key) + ul.setAttributeNS(android_ns, 'android:value', logging_parent_value) + application.insertBefore(doc.createTextNode(indent), last) + application.insertBefore(ul, last) + last = application.lastChild + + # align the closing tag with the opening tag if it's not + # indented + if last and last.nodeType != minidom.Node.TEXT_NODE: + indent = get_indent(application.previousSibling, 1) + application.appendChild(doc.createTextNode(indent)) def add_uses_libraries(doc, new_uses_libraries, required): @@ -192,42 +160,32 @@ def add_uses_libraries(doc, new_uses_libraries, required): """ manifest = parse_manifest(doc) - elems = get_children_with_tag(manifest, 'application') - application = elems[0] if len(elems) == 1 else None - if len(elems) > 1: - raise RuntimeError('found multiple <application> tags') - elif not elems: - application = doc.createElement('application') - indent = get_indent(manifest.firstChild, 1) - first = manifest.firstChild - manifest.insertBefore(doc.createTextNode(indent), first) - manifest.insertBefore(application, first) - - indent = get_indent(application.firstChild, 2) - - last = application.lastChild - if last is not None and last.nodeType != minidom.Node.TEXT_NODE: - last = None - - for name in new_uses_libraries: - if find_child_with_attribute(application, 'uses-library', android_ns, - 'name', name) is not None: - # If the uses-library tag of the same 'name' attribute value exists, - # respect it. - continue + for application in get_or_create_applications(doc, manifest): + indent = get_indent(application.firstChild, 2) + + last = application.lastChild + if last is not None and last.nodeType != minidom.Node.TEXT_NODE: + last = None + + for name in new_uses_libraries: + if find_child_with_attribute(application, 'uses-library', android_ns, + 'name', name) is not None: + # If the uses-library tag of the same 'name' attribute value exists, + # respect it. + continue - ul = doc.createElement('uses-library') - ul.setAttributeNS(android_ns, 'android:name', name) - ul.setAttributeNS(android_ns, 'android:required', str(required).lower()) + ul = doc.createElement('uses-library') + ul.setAttributeNS(android_ns, 'android:name', name) + ul.setAttributeNS(android_ns, 'android:required', str(required).lower()) - application.insertBefore(doc.createTextNode(indent), last) - application.insertBefore(ul, last) + application.insertBefore(doc.createTextNode(indent), last) + application.insertBefore(ul, last) - # align the closing tag with the opening tag if it's not - # indented - if application.lastChild.nodeType != minidom.Node.TEXT_NODE: - indent = get_indent(application.previousSibling, 1) - application.appendChild(doc.createTextNode(indent)) + # align the closing tag with the opening tag if it's not + # indented + if application.lastChild.nodeType != minidom.Node.TEXT_NODE: + indent = get_indent(application.previousSibling, 1) + application.appendChild(doc.createTextNode(indent)) def add_uses_non_sdk_api(doc): @@ -240,111 +198,63 @@ def add_uses_non_sdk_api(doc): """ manifest = parse_manifest(doc) - elems = get_children_with_tag(manifest, 'application') - application = elems[0] if len(elems) == 1 else None - if len(elems) > 1: - raise RuntimeError('found multiple <application> tags') - elif not elems: - application = doc.createElement('application') - indent = get_indent(manifest.firstChild, 1) - first = manifest.firstChild - manifest.insertBefore(doc.createTextNode(indent), first) - manifest.insertBefore(application, first) - - attr = application.getAttributeNodeNS(android_ns, 'usesNonSdkApi') - if attr is None: - attr = doc.createAttributeNS(android_ns, 'android:usesNonSdkApi') - attr.value = 'true' - application.setAttributeNode(attr) + for application in get_or_create_applications(doc, manifest): + attr = application.getAttributeNodeNS(android_ns, 'usesNonSdkApi') + if attr is None: + attr = doc.createAttributeNS(android_ns, 'android:usesNonSdkApi') + attr.value = 'true' + application.setAttributeNode(attr) def add_use_embedded_dex(doc): manifest = parse_manifest(doc) - elems = get_children_with_tag(manifest, 'application') - application = elems[0] if len(elems) == 1 else None - if len(elems) > 1: - raise RuntimeError('found multiple <application> tags') - elif not elems: - application = doc.createElement('application') - indent = get_indent(manifest.firstChild, 1) - first = manifest.firstChild - manifest.insertBefore(doc.createTextNode(indent), first) - manifest.insertBefore(application, first) - - attr = application.getAttributeNodeNS(android_ns, 'useEmbeddedDex') - if attr is None: - attr = doc.createAttributeNS(android_ns, 'android:useEmbeddedDex') - attr.value = 'true' - application.setAttributeNode(attr) - elif attr.value != 'true': - raise RuntimeError('existing attribute mismatches the option of --use-embedded-dex') + for application in get_or_create_applications(doc, manifest): + attr = application.getAttributeNodeNS(android_ns, 'useEmbeddedDex') + if attr is None: + attr = doc.createAttributeNS(android_ns, 'android:useEmbeddedDex') + attr.value = 'true' + application.setAttributeNode(attr) + elif attr.value != 'true': + raise RuntimeError('existing attribute mismatches the option of --use-embedded-dex') def add_extract_native_libs(doc, extract_native_libs): manifest = parse_manifest(doc) - elems = get_children_with_tag(manifest, 'application') - application = elems[0] if len(elems) == 1 else None - if len(elems) > 1: - raise RuntimeError('found multiple <application> tags') - elif not elems: - application = doc.createElement('application') - indent = get_indent(manifest.firstChild, 1) - first = manifest.firstChild - manifest.insertBefore(doc.createTextNode(indent), first) - manifest.insertBefore(application, first) - - value = str(extract_native_libs).lower() - attr = application.getAttributeNodeNS(android_ns, 'extractNativeLibs') - if attr is None: - attr = doc.createAttributeNS(android_ns, 'android:extractNativeLibs') - attr.value = value - application.setAttributeNode(attr) - elif attr.value != value: - raise RuntimeError('existing attribute extractNativeLibs="%s" conflicts with --extract-native-libs="%s"' % - (attr.value, value)) + for application in get_or_create_applications(doc, manifest): + value = str(extract_native_libs).lower() + attr = application.getAttributeNodeNS(android_ns, 'extractNativeLibs') + if attr is None: + attr = doc.createAttributeNS(android_ns, 'android:extractNativeLibs') + attr.value = value + application.setAttributeNode(attr) + elif attr.value != value: + raise RuntimeError('existing attribute extractNativeLibs="%s" conflicts with --extract-native-libs="%s"' % + (attr.value, value)) def set_has_code_to_false(doc): manifest = parse_manifest(doc) - elems = get_children_with_tag(manifest, 'application') - application = elems[0] if len(elems) == 1 else None - if len(elems) > 1: - raise RuntimeError('found multiple <application> tags') - elif not elems: - application = doc.createElement('application') - indent = get_indent(manifest.firstChild, 1) - first = manifest.firstChild - manifest.insertBefore(doc.createTextNode(indent), first) - manifest.insertBefore(application, first) - - attr = application.getAttributeNodeNS(android_ns, 'hasCode') - if attr is not None: - # Do nothing if the application already has a hasCode attribute. - return - attr = doc.createAttributeNS(android_ns, 'android:hasCode') - attr.value = 'false' - application.setAttributeNode(attr) + for application in get_or_create_applications(doc, manifest): + attr = application.getAttributeNodeNS(android_ns, 'hasCode') + if attr is not None: + # Do nothing if the application already has a hasCode attribute. + continue + attr = doc.createAttributeNS(android_ns, 'android:hasCode') + attr.value = 'false' + application.setAttributeNode(attr) + def set_test_only_flag_to_true(doc): manifest = parse_manifest(doc) - elems = get_children_with_tag(manifest, 'application') - application = elems[0] if len(elems) == 1 else None - if len(elems) > 1: - raise RuntimeError('found multiple <application> tags') - elif not elems: - application = doc.createElement('application') - indent = get_indent(manifest.firstChild, 1) - first = manifest.firstChild - manifest.insertBefore(doc.createTextNode(indent), first) - manifest.insertBefore(application, first) - - attr = application.getAttributeNodeNS(android_ns, 'testOnly') - if attr is not None: - # Do nothing If the application already has a testOnly attribute. - return - attr = doc.createAttributeNS(android_ns, 'android:testOnly') - attr.value = 'true' - application.setAttributeNode(attr) + for application in get_or_create_applications(doc, manifest): + attr = application.getAttributeNodeNS(android_ns, 'testOnly') + if attr is not None: + # Do nothing If the application already has a testOnly attribute. + continue + attr = doc.createAttributeNS(android_ns, 'android:testOnly') + attr.value = 'true' + application.setAttributeNode(attr) + def set_max_sdk_version(doc, max_sdk_version): """Replace the maxSdkVersion attribute value for permission and @@ -364,6 +274,7 @@ def set_max_sdk_version(doc, max_sdk_version): if max_attr and max_attr.value == 'current': max_attr.value = max_sdk_version + def override_placeholder_version(doc, new_version): """Replace the versionCode attribute value if it\'s currently set to the placeholder version of 0. @@ -374,9 +285,10 @@ def override_placeholder_version(doc, new_version): """ manifest = parse_manifest(doc) version = manifest.getAttribute("android:versionCode") - if (version == '0'): + if version == '0': manifest.setAttribute("android:versionCode", new_version) + def main(): """Program entry point.""" try: @@ -427,5 +339,6 @@ def main(): print('error: ' + str(err), file=sys.stderr) sys.exit(-1) + if __name__ == '__main__': main() diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py index 0a62b10a4..e4d8dc383 100755 --- a/scripts/manifest_fixer_test.py +++ b/scripts/manifest_fixer_test.py @@ -20,12 +20,13 @@ import io import sys import unittest from xml.dom import minidom -import xml.etree.ElementTree as ET +import xml.etree.ElementTree as ElementTree import manifest_fixer sys.dont_write_bytecode = True + class CompareVersionGtTest(unittest.TestCase): """Unit tests for compare_version_gt function.""" @@ -69,25 +70,24 @@ class RaiseMinSdkVersionTest(unittest.TestCase): '%s' '</manifest>\n') - # pylint: disable=redefined-builtin - def uses_sdk(self, min=None, target=None, extra=''): + def uses_sdk(self, min_sdk=None, target_sdk=None, extra=''): attrs = '' - if min: - attrs += ' android:minSdkVersion="%s"' % (min) - if target: - attrs += ' android:targetSdkVersion="%s"' % (target) + if min_sdk: + attrs += ' android:minSdkVersion="%s"' % min_sdk + if target_sdk: + attrs += ' android:targetSdkVersion="%s"' % target_sdk if extra: attrs += ' ' + extra - return ' <uses-sdk%s/>\n' % (attrs) + return ' <uses-sdk%s/>\n' % attrs def assert_xml_equal(self, output, expected): - self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected)) + self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected)) def test_no_uses_sdk(self): """Tests inserting a uses-sdk element into a manifest.""" manifest_input = self.manifest_tmpl % '' - expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28') + expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='28') output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False) self.assert_xml_equal(output, expected) @@ -95,7 +95,7 @@ class RaiseMinSdkVersionTest(unittest.TestCase): """Tests inserting a minSdkVersion attribute into a uses-sdk element.""" manifest_input = self.manifest_tmpl % ' <uses-sdk extra="foo"/>\n' - expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28', + expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='28', extra='extra="foo"') output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False) self.assert_xml_equal(output, expected) @@ -103,64 +103,64 @@ class RaiseMinSdkVersionTest(unittest.TestCase): def test_raise_min(self): """Tests inserting a minSdkVersion attribute into a uses-sdk element.""" - manifest_input = self.manifest_tmpl % self.uses_sdk(min='27') - expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28') + manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='27') + expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='28') output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False) self.assert_xml_equal(output, expected) def test_raise(self): """Tests raising a minSdkVersion attribute.""" - manifest_input = self.manifest_tmpl % self.uses_sdk(min='27') - expected = self.manifest_tmpl % self.uses_sdk(min='28', target='28') + manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='27') + expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='28') output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False) self.assert_xml_equal(output, expected) def test_no_raise_min(self): """Tests a minSdkVersion that doesn't need raising.""" - manifest_input = self.manifest_tmpl % self.uses_sdk(min='28') - expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27') + manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='28') + expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='27') output = self.raise_min_sdk_version_test(manifest_input, '27', '27', False) self.assert_xml_equal(output, expected) def test_raise_codename(self): """Tests raising a minSdkVersion attribute to a codename.""" - manifest_input = self.manifest_tmpl % self.uses_sdk(min='28') - expected = self.manifest_tmpl % self.uses_sdk(min='P', target='P') + manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='28') + expected = self.manifest_tmpl % self.uses_sdk(min_sdk='P', target_sdk='P') output = self.raise_min_sdk_version_test(manifest_input, 'P', 'P', False) self.assert_xml_equal(output, expected) def test_no_raise_codename(self): """Tests a minSdkVersion codename that doesn't need raising.""" - manifest_input = self.manifest_tmpl % self.uses_sdk(min='P') - expected = self.manifest_tmpl % self.uses_sdk(min='P', target='28') + manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='P') + expected = self.manifest_tmpl % self.uses_sdk(min_sdk='P', target_sdk='28') output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False) self.assert_xml_equal(output, expected) def test_target(self): """Tests an existing targetSdkVersion is preserved.""" - manifest_input = self.manifest_tmpl % self.uses_sdk(min='26', target='27') - expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27') + manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='26', target_sdk='27') + expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='27') output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False) self.assert_xml_equal(output, expected) def test_no_target(self): """Tests inserting targetSdkVersion when minSdkVersion exists.""" - manifest_input = self.manifest_tmpl % self.uses_sdk(min='27') - expected = self.manifest_tmpl % self.uses_sdk(min='28', target='29') + manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='27') + expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='29') output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False) self.assert_xml_equal(output, expected) def test_target_no_min(self): """"Tests inserting targetSdkVersion when minSdkVersion exists.""" - manifest_input = self.manifest_tmpl % self.uses_sdk(target='27') - expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27') + manifest_input = self.manifest_tmpl % self.uses_sdk(target_sdk='27') + expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='27') output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False) self.assert_xml_equal(output, expected) @@ -168,23 +168,23 @@ class RaiseMinSdkVersionTest(unittest.TestCase): """Tests inserting targetSdkVersion when minSdkVersion does not exist.""" manifest_input = self.manifest_tmpl % '' - expected = self.manifest_tmpl % self.uses_sdk(min='28', target='29') + expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='29') output = self.raise_min_sdk_version_test(manifest_input, '28', '29', False) self.assert_xml_equal(output, expected) def test_library_no_target(self): """Tests inserting targetSdkVersion when minSdkVersion exists.""" - manifest_input = self.manifest_tmpl % self.uses_sdk(min='27') - expected = self.manifest_tmpl % self.uses_sdk(min='28', target='16') + manifest_input = self.manifest_tmpl % self.uses_sdk(min_sdk='27') + expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='16') output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True) self.assert_xml_equal(output, expected) def test_library_target_no_min(self): """Tests inserting targetSdkVersion when minSdkVersion exists.""" - manifest_input = self.manifest_tmpl % self.uses_sdk(target='27') - expected = self.manifest_tmpl % self.uses_sdk(min='28', target='27') + manifest_input = self.manifest_tmpl % self.uses_sdk(target_sdk='27') + expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='27') output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True) self.assert_xml_equal(output, expected) @@ -192,7 +192,7 @@ class RaiseMinSdkVersionTest(unittest.TestCase): """Tests inserting targetSdkVersion when minSdkVersion does not exist.""" manifest_input = self.manifest_tmpl % '' - expected = self.manifest_tmpl % self.uses_sdk(min='28', target='16') + expected = self.manifest_tmpl % self.uses_sdk(min_sdk='28', target_sdk='16') output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True) self.assert_xml_equal(output, expected) @@ -228,12 +228,24 @@ class RaiseMinSdkVersionTest(unittest.TestCase): self.assert_xml_equal(output, expected) + def test_multiple_uses_sdks(self): + """Tests a manifest that contains multiple uses_sdks elements.""" + + manifest_input = self.manifest_tmpl % ( + ' <uses-sdk android:featureFlag="foo" android:minSdkVersion="21" />\n' + ' <uses-sdk android:featureFlag="!foo" android:minSdkVersion="22" />\n') + expected = self.manifest_tmpl % ( + ' <uses-sdk android:featureFlag="foo" android:minSdkVersion="28" android:targetSdkVersion="28" />\n' + ' <uses-sdk android:featureFlag="!foo" android:minSdkVersion="28" android:targetSdkVersion="28" />\n') + + output = self.raise_min_sdk_version_test(manifest_input, '28', '28', False) + self.assert_xml_equal(output, expected) class AddLoggingParentTest(unittest.TestCase): """Unit tests for add_logging_parent function.""" def assert_xml_equal(self, output, expected): - self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected)) + self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected)) def add_logging_parent_test(self, input_manifest, logging_parent=None): doc = minidom.parseString(input_manifest) @@ -253,8 +265,8 @@ class AddLoggingParentTest(unittest.TestCase): attrs = '' if logging_parent: meta_text = ('<meta-data android:name="android.content.pm.LOGGING_PARENT" ' - 'android:value="%s"/>\n') % (logging_parent) - attrs += ' <application>\n %s </application>\n' % (meta_text) + 'android:value="%s"/>\n') % logging_parent + attrs += ' <application>\n %s </application>\n' % meta_text return attrs @@ -277,7 +289,7 @@ class AddUsesLibrariesTest(unittest.TestCase): """Unit tests for add_uses_libraries function.""" def assert_xml_equal(self, output, expected): - self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected)) + self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected)) def run_test(self, input_manifest, new_uses_libraries): doc = minidom.parseString(input_manifest) @@ -289,18 +301,16 @@ class AddUsesLibrariesTest(unittest.TestCase): manifest_tmpl = ( '<?xml version="1.0" encoding="utf-8"?>\n' '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n' - ' <application>\n' '%s' - ' </application>\n' '</manifest>\n') def uses_libraries(self, name_required_pairs): - ret = '' + ret = ' <application>\n' for name, required in name_required_pairs: ret += ( ' <uses-library android:name="%s" android:required="%s"/>\n' ) % (name, required) - + ret += ' </application>\n' return ret def test_empty(self): @@ -361,12 +371,23 @@ class AddUsesLibrariesTest(unittest.TestCase): output = self.run_test(manifest_input, ['foo', 'bar']) self.assert_xml_equal(output, expected) + def test_multiple_application(self): + """When there are multiple applications, the libs are added to each.""" + manifest_input = self.manifest_tmpl % ( + self.uses_libraries([('foo', 'false')]) + + self.uses_libraries([('bar', 'false')])) + expected = self.manifest_tmpl % ( + self.uses_libraries([('foo', 'false'), ('bar', 'true')]) + + self.uses_libraries([('bar', 'false'), ('foo', 'true')])) + output = self.run_test(manifest_input, ['foo', 'bar']) + self.assert_xml_equal(output, expected) + class AddUsesNonSdkApiTest(unittest.TestCase): """Unit tests for add_uses_libraries function.""" def assert_xml_equal(self, output, expected): - self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected)) + self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected)) def run_test(self, input_manifest): doc = minidom.parseString(input_manifest) @@ -378,11 +399,11 @@ class AddUsesNonSdkApiTest(unittest.TestCase): manifest_tmpl = ( '<?xml version="1.0" encoding="utf-8"?>\n' '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n' - ' <application%s/>\n' + ' %s\n' '</manifest>\n') def uses_non_sdk_api(self, value): - return ' android:usesNonSdkApi="true"' if value else '' + return '<application %s/>' % ('android:usesNonSdkApi="true"' if value else '') def test_set_true(self): """Empty new_uses_libraries must not touch the manifest.""" @@ -398,12 +419,19 @@ class AddUsesNonSdkApiTest(unittest.TestCase): output = self.run_test(manifest_input) self.assert_xml_equal(output, expected) + def test_multiple_applications(self): + """new_uses_libraries must be added to all applications.""" + manifest_input = self.manifest_tmpl % (self.uses_non_sdk_api(True) + self.uses_non_sdk_api(False)) + expected = self.manifest_tmpl % (self.uses_non_sdk_api(True) + self.uses_non_sdk_api(True)) + output = self.run_test(manifest_input) + self.assert_xml_equal(output, expected) + class UseEmbeddedDexTest(unittest.TestCase): """Unit tests for add_use_embedded_dex function.""" def assert_xml_equal(self, output, expected): - self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected)) + self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected)) def run_test(self, input_manifest): doc = minidom.parseString(input_manifest) @@ -415,14 +443,14 @@ class UseEmbeddedDexTest(unittest.TestCase): manifest_tmpl = ( '<?xml version="1.0" encoding="utf-8"?>\n' '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n' - ' <application%s/>\n' + ' %s\n' '</manifest>\n') def use_embedded_dex(self, value): - return ' android:useEmbeddedDex="%s"' % value + return '<application android:useEmbeddedDex="%s" />' % value def test_manifest_with_undeclared_preference(self): - manifest_input = self.manifest_tmpl % '' + manifest_input = self.manifest_tmpl % '<application/>' expected = self.manifest_tmpl % self.use_embedded_dex('true') output = self.run_test(manifest_input) self.assert_xml_equal(output, expected) @@ -437,12 +465,24 @@ class UseEmbeddedDexTest(unittest.TestCase): manifest_input = self.manifest_tmpl % self.use_embedded_dex('false') self.assertRaises(RuntimeError, self.run_test, manifest_input) + def test_multiple_applications(self): + manifest_input = self.manifest_tmpl % ( + self.use_embedded_dex('true') + + '<application/>' + ) + expected = self.manifest_tmpl % ( + self.use_embedded_dex('true') + + self.use_embedded_dex('true') + ) + output = self.run_test(manifest_input) + self.assert_xml_equal(output, expected) + class AddExtractNativeLibsTest(unittest.TestCase): """Unit tests for add_extract_native_libs function.""" def assert_xml_equal(self, output, expected): - self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected)) + self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected)) def run_test(self, input_manifest, value): doc = minidom.parseString(input_manifest) @@ -454,20 +494,20 @@ class AddExtractNativeLibsTest(unittest.TestCase): manifest_tmpl = ( '<?xml version="1.0" encoding="utf-8"?>\n' '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n' - ' <application%s/>\n' + ' %s\n' '</manifest>\n') def extract_native_libs(self, value): - return ' android:extractNativeLibs="%s"' % value + return '<application android:extractNativeLibs="%s" />' % value def test_set_true(self): - manifest_input = self.manifest_tmpl % '' + manifest_input = self.manifest_tmpl % '<application/>' expected = self.manifest_tmpl % self.extract_native_libs('true') output = self.run_test(manifest_input, True) self.assert_xml_equal(output, expected) def test_set_false(self): - manifest_input = self.manifest_tmpl % '' + manifest_input = self.manifest_tmpl % '<application/>' expected = self.manifest_tmpl % self.extract_native_libs('false') output = self.run_test(manifest_input, False) self.assert_xml_equal(output, expected) @@ -482,12 +522,18 @@ class AddExtractNativeLibsTest(unittest.TestCase): manifest_input = self.manifest_tmpl % self.extract_native_libs('true') self.assertRaises(RuntimeError, self.run_test, manifest_input, False) + def test_multiple_applications(self): + manifest_input = self.manifest_tmpl % (self.extract_native_libs('true') + '<application/>') + expected = self.manifest_tmpl % (self.extract_native_libs('true') + self.extract_native_libs('true')) + output = self.run_test(manifest_input, True) + self.assert_xml_equal(output, expected) + class AddNoCodeApplicationTest(unittest.TestCase): """Unit tests for set_has_code_to_false function.""" def assert_xml_equal(self, output, expected): - self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected)) + self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected)) def run_test(self, input_manifest): doc = minidom.parseString(input_manifest) @@ -515,7 +561,7 @@ class AddNoCodeApplicationTest(unittest.TestCase): self.assert_xml_equal(output, expected) def test_has_application_has_code_false(self): - """ Do nothing if there's already an application elemeent. """ + """ Do nothing if there's already an application element. """ manifest_input = self.manifest_tmpl % ' <application android:hasCode="false"/>\n' output = self.run_test(manifest_input) self.assert_xml_equal(output, manifest_input) @@ -527,12 +573,25 @@ class AddNoCodeApplicationTest(unittest.TestCase): output = self.run_test(manifest_input) self.assert_xml_equal(output, manifest_input) + def test_multiple_applications(self): + """ Apply to all applications """ + manifest_input = self.manifest_tmpl % ( + ' <application android:hasCode="true" />\n' + + ' <application android:hasCode="false" />\n' + + ' <application/>\n') + expected = self.manifest_tmpl % ( + ' <application android:hasCode="true" />\n' + + ' <application android:hasCode="false" />\n' + + ' <application android:hasCode="false" />\n') + output = self.run_test(manifest_input) + self.assert_xml_equal(output, expected) + class AddTestOnlyApplicationTest(unittest.TestCase): """Unit tests for set_test_only_flag_to_true function.""" def assert_xml_equal(self, output, expected): - self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected)) + self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected)) def run_test(self, input_manifest): doc = minidom.parseString(input_manifest) @@ -571,12 +630,26 @@ class AddTestOnlyApplicationTest(unittest.TestCase): output = self.run_test(manifest_input) self.assert_xml_equal(output, manifest_input) + def test_multiple_applications(self): + manifest_input = self.manifest_tmpl % ( + ' <application android:testOnly="true" />\n' + + ' <application android:testOnly="false" />\n' + + ' <application/>\n' + ) + expected = self.manifest_tmpl % ( + ' <application android:testOnly="true" />\n' + + ' <application android:testOnly="false" />\n' + + ' <application android:testOnly="true" />\n' + ) + output = self.run_test(manifest_input) + self.assert_xml_equal(output, expected) + class SetMaxSdkVersionTest(unittest.TestCase): """Unit tests for set_max_sdk_version function.""" def assert_xml_equal(self, output, expected): - self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected)) + self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected)) def run_test(self, input_manifest, max_sdk_version): doc = minidom.parseString(input_manifest) @@ -591,15 +664,15 @@ class SetMaxSdkVersionTest(unittest.TestCase): '%s' '</manifest>\n') - def permission(self, max=None): - if max is None: + def permission(self, max_sdk=None): + if max_sdk is None: return ' <permission/>' - return ' <permission android:maxSdkVersion="%s"/>\n' % max + return ' <permission android:maxSdkVersion="%s"/>\n' % max_sdk - def uses_permission(self, max=None): - if max is None: + def uses_permission(self, max_sdk=None): + if max_sdk is None: return ' <uses-permission/>' - return ' <uses-permission android:maxSdkVersion="%s"/>\n' % max + return ' <uses-permission android:maxSdkVersion="%s"/>\n' % max_sdk def test_permission_no_max_sdk_version(self): """Tests if permission has no maxSdkVersion attribute""" @@ -643,11 +716,12 @@ class SetMaxSdkVersionTest(unittest.TestCase): output = self.run_test(manifest_input, '9000') self.assert_xml_equal(output, expected) + class OverrideDefaultVersionTest(unittest.TestCase): """Unit tests for override_default_version function.""" def assert_xml_equal(self, output, expected): - self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected)) + self.assertEqual(ElementTree.canonicalize(output), ElementTree.canonicalize(expected)) def run_test(self, input_manifest, version): doc = minidom.parseString(input_manifest) diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go index 8b994eb64..34e11f0da 100644 --- a/sdk/bootclasspath_fragment_sdk_test.go +++ b/sdk/bootclasspath_fragment_sdk_test.go @@ -46,8 +46,9 @@ func fixtureAddPlatformBootclasspathForBootclasspathFragmentWithExtra(apex, frag ], } `, apex, fragment, extraFragments)), - android.FixtureAddFile("frameworks/base/config/boot-profile.txt", nil), - android.FixtureAddFile("frameworks/base/config/boot-image-profile.txt", nil), + android.FixtureAddFile("frameworks/base/boot/boot-profile.txt", nil), + android.FixtureAddFile("frameworks/base/boot/boot-image-profile.txt", nil), + android.FixtureAddFile("art/build/boot/boot-image-profile.txt", nil), android.FixtureAddFile("build/soong/scripts/check_boot_jars/package_allowed_list.txt", nil), ) } @@ -204,7 +205,19 @@ java_import { .intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/core1.jar .intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/core2.jar `), - snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot), + snapshotTestPreparer(checkSnapshotWithoutSource, android.GroupFixturePreparers( + preparerForSnapshot, + // Flag ART prebuilts + android.FixtureMergeMockFs(android.MockFS{ + "apex_contributions/Android.bp": []byte(` + apex_contributions { + name: "prebuilt_art_contributions", + contents: ["prebuilt_com.android.art"], + api_domain: "com.android.art", + } + `)}), + android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "prebuilt_art_contributions"), + )), // Check the behavior of the snapshot without the source. snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) { @@ -212,8 +225,8 @@ java_import { checkBootJarsPackageCheckRule(t, result, append( []string{ - "out/soong/.intermediates/prebuilts/apex/prebuilt_com.android.art.deapexer/android_common/deapexer/javalib/core1.jar", - "out/soong/.intermediates/prebuilts/apex/prebuilt_com.android.art.deapexer/android_common/deapexer/javalib/core2.jar", + "out/soong/.intermediates/prebuilts/apex/com.android.art/android_common_com.android.art/deapexer/javalib/core1.jar", + "out/soong/.intermediates/prebuilts/apex/com.android.art/android_common_com.android.art/deapexer/javalib/core2.jar", "out/soong/.intermediates/default/java/framework/android_common/aligned/framework.jar", }, java.ApexBootJarDexJarPaths..., diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go index 0fb5c70d2..15e13dbcb 100644 --- a/sdk/java_sdk_test.go +++ b/sdk/java_sdk_test.go @@ -1360,10 +1360,9 @@ java_sdk_library_import { } `), snapshotTestChecker(checkSnapshotWithSourcePreferred, func(t *testing.T, result *android.TestResult) { - ctx := android.ModuleInstallPathContextForTesting(result.Config) dexJarBuildPath := func(name string, kind android.SdkKind) string { - dep := result.Module(name, "android_common").(java.SdkLibraryDependency) - path := dep.SdkApiExportableStubDexJar(ctx, kind).Path() + sdkLibInfo, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), result.Module(name, "android_common"), java.SdkLibraryInfoProvider) + path := sdkLibInfo.ExportableStubDexJarPaths[kind].Path() return path.RelativeToTop().String() } @@ -1703,61 +1702,6 @@ java_sdk_library_import { ) } -func TestSnapshotWithJavaSdkLibrary_NamingScheme(t *testing.T) { - result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, ` - sdk { - name: "mysdk", - java_sdk_libs: ["myjavalib"], - } - - java_sdk_library { - name: "myjavalib", - apex_available: ["//apex_available:anyapex"], - srcs: ["Test.java"], - sdk_version: "current", - naming_scheme: "default", - public: { - enabled: true, - }, - } - `) - - CheckSnapshot(t, result, "mysdk", "", - checkAndroidBpContents(` -// This is auto-generated. DO NOT EDIT. - -apex_contributions_defaults { - name: "mysdk.contributions", - contents: ["prebuilt_myjavalib"], -} - -java_sdk_library_import { - name: "myjavalib", - prefer: false, - visibility: ["//visibility:public"], - apex_available: ["//apex_available:anyapex"], - naming_scheme: "default", - shared_library: true, - public: { - jars: ["sdk_library/public/myjavalib-stubs.jar"], - stub_srcs: ["sdk_library/public/myjavalib_stub_sources"], - current_api: "sdk_library/public/myjavalib.txt", - removed_api: "sdk_library/public/myjavalib-removed.txt", - sdk_version: "current", - }, -} -`), - checkAllCopyRules(` -.intermediates/myjavalib.stubs.exportable/android_common/combined/myjavalib.stubs.exportable.jar -> sdk_library/public/myjavalib-stubs.jar -.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt -.intermediates/myjavalib.stubs.source/android_common/exportable/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt -`), - checkMergeZips( - ".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip", - ), - ) -} - func TestSnapshotWithJavaSdkLibrary_DoctagFiles(t *testing.T) { result := android.GroupFixturePreparers( prepareForSdkTestWithJavaSdkLibrary, diff --git a/sdk/sdk.go b/sdk/sdk.go index 2fb3a3f30..aa82abbb4 100644 --- a/sdk/sdk.go +++ b/sdk/sdk.go @@ -84,20 +84,6 @@ type sdkProperties struct { // True if this is a module_exports (or module_exports_snapshot) module type. Module_exports bool `blueprint:"mutated"` - - // The additional visibility to add to the prebuilt modules to allow them to - // reference each other. - // - // This can only be used to widen the visibility of the members: - // - // * Specifying //visibility:public here will make all members visible and - // essentially ignore their own visibility. - // * Specifying //visibility:private here is an error. - // * Specifying any other rule here will add it to the members visibility and - // be output to the member prebuilt in the snapshot. Duplicates will be - // dropped. Adding a rule to members that have //visibility:private will - // cause the //visibility:private to be discarded. - Prebuilt_visibility []string } // sdk defines an SDK which is a logical group of modules (e.g. native libs, headers, java libs, etc.) @@ -130,8 +116,6 @@ func newSdkModule(moduleExports bool) *sdk { s.AddProperties(&s.properties, s.dynamicMemberTypeListProperties, &traitsWrapper) - // Make sure that the prebuilt visibility property is verified for errors. - android.AddVisibilityProperty(s, "prebuilt_visibility", &s.properties.Prebuilt_visibility) android.InitCommonOSAndroidMultiTargetsArchModule(s, android.HostAndDeviceSupported, android.MultilibCommon) android.InitDefaultableModule(s) android.AddLoadHook(s, func(ctx android.LoadHookContext) { diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index 416dce62c..2532a2581 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -53,9 +53,6 @@ func TestSnapshotVisibility(t *testing.T) { // generated sdk_snapshot. ":__subpackages__", ], - prebuilt_visibility: [ - "//prebuilts/mysdk", - ], java_header_libs: [ "myjavalib", "mypublicjavalib", @@ -162,18 +159,6 @@ java_import { `)) } -func TestPrebuiltVisibilityProperty_IsValidated(t *testing.T) { - testSdkError(t, `prebuilt_visibility: cannot mix "//visibility:private" with any other visibility rules`, ` - sdk { - name: "mysdk", - prebuilt_visibility: [ - "//foo", - "//visibility:private", - ], - } -`) -} - func TestSdkInstall(t *testing.T) { sdk := ` sdk { diff --git a/sdk/update.go b/sdk/update.go index 93bb861e9..7f4f80a34 100644 --- a/sdk/update.go +++ b/sdk/update.go @@ -22,7 +22,6 @@ import ( "sort" "strings" - "android/soong/apex" "android/soong/cc" "android/soong/java" @@ -1137,9 +1136,6 @@ func (s *snapshotBuilder) AddPrebuiltModule(member android.SdkMember, moduleType apexAvailable = []string{android.AvailableToPlatform} } - // Add in any baseline apex available settings. - apexAvailable = append(apexAvailable, apex.BaselineApexAvailable(member.Name())...) - // Remove duplicates and sort. apexAvailable = android.FirstUniqueStrings(apexAvailable) sort.Strings(apexAvailable) @@ -1993,14 +1989,6 @@ func (m *memberContext) IsTargetBuildBeforeTiramisu() bool { return m.builder.targetBuildRelease.EarlierThan(buildReleaseT) } -func (m *memberContext) Config() android.Config { - return m.sdkMemberContext.Config() -} - -func (m *memberContext) OtherModulePropertyErrorf(module android.Module, property string, fmt string, args ...interface{}) { - m.sdkMemberContext.OtherModulePropertyErrorf(module, property, fmt, args) -} - var _ android.SdkMemberContext = (*memberContext)(nil) func (s *sdk) createMemberSnapshot(ctx *memberContext, member *sdkMember, bpModule *bpModule) { diff --git a/soong_ui.bash b/soong_ui.bash index 77378807f..be78b68fc 100755 --- a/soong_ui.bash +++ b/soong_ui.bash @@ -14,18 +14,20 @@ # See the License for the specific language governing permissions and # limitations under the License. +source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../make/shell_utils.sh +require_top + # To track how long we took to startup. case $(uname -s) in Darwin) - export TRACE_BEGIN_SOONG=`$T/prebuilts/build-tools/path/darwin-x86/date +%s%3N` + export TRACE_BEGIN_SOONG=`$TOP/prebuilts/build-tools/path/darwin-x86/date +%s%3N` ;; *) export TRACE_BEGIN_SOONG=$(date +%s%N) ;; esac -source $(cd $(dirname $BASH_SOURCE) &> /dev/null && pwd)/../make/shell_utils.sh -require_top +setup_cog_env_if_needed # Save the current PWD for use in soong_ui export ORIGINAL_PWD=${PWD} diff --git a/sysprop/Android.bp b/sysprop/Android.bp index a00a5e42c..22cba3bf1 100644 --- a/sysprop/Android.bp +++ b/sysprop/Android.bp @@ -21,4 +21,6 @@ bootstrap_go_package { "sysprop_test.go", ], pluginFor: ["soong_build"], + // Used by plugins + visibility: ["//visibility:public"], } diff --git a/testing/code_metadata_internal_proto/Android.bp b/testing/code_metadata_internal_proto/Android.bp index a534cc20b..396e44f52 100644 --- a/testing/code_metadata_internal_proto/Android.bp +++ b/testing/code_metadata_internal_proto/Android.bp @@ -20,10 +20,14 @@ bootstrap_go_package { name: "soong-testing-code_metadata_internal_proto", pkgPath: "android/soong/testing/code_metadata_internal_proto", deps: [ - "golang-protobuf-reflect-protoreflect", - "golang-protobuf-runtime-protoimpl", - ], + "golang-protobuf-reflect-protoreflect", + "golang-protobuf-runtime-protoimpl", + ], srcs: [ "code_metadata_internal.pb.go", ], + visibility: [ + "//build/make/tools/metadata", + "//build/soong:__subpackages__", + ], } diff --git a/testing/code_metadata_proto/Android.bp b/testing/code_metadata_proto/Android.bp index f07efffb3..ae41d4aa1 100644 --- a/testing/code_metadata_proto/Android.bp +++ b/testing/code_metadata_proto/Android.bp @@ -26,6 +26,7 @@ bootstrap_go_package { srcs: [ "code_metadata.pb.go", ], + visibility: ["//build/make/tools/metadata"], } python_library_host { @@ -40,4 +41,5 @@ python_library_host { proto: { canonical_path_from_root: false, }, + visibility: ["//tools/asuite/team_build_scripts"], } diff --git a/testing/test_spec_proto/Android.bp b/testing/test_spec_proto/Android.bp index d5ad70b73..1070d1a9c 100644 --- a/testing/test_spec_proto/Android.bp +++ b/testing/test_spec_proto/Android.bp @@ -26,6 +26,11 @@ bootstrap_go_package { srcs: [ "test_spec.pb.go", ], + visibility: [ + "//build/make/tools/metadata", + "//build/soong:__subpackages__", + "//vendor:__subpackages__", + ], } python_library_host { @@ -40,4 +45,5 @@ python_library_host { proto: { canonical_path_from_root: false, }, + visibility: ["//tools/asuite/team_build_scripts"], } diff --git a/tests/build_action_caching_test.sh b/tests/build_action_caching_test.sh new file mode 100755 index 000000000..8ecd037f3 --- /dev/null +++ b/tests/build_action_caching_test.sh @@ -0,0 +1,190 @@ +#!/bin/bash -u + +set -o pipefail + +# Test that the mk and ninja files generated by Soong don't change if some +# incremental modules are restored from cache. + +OUTPUT_DIR="$(mktemp -d tmp.XXXXXX)" + +echo ${OUTPUT_DIR} + +function cleanup { + rm -rf "${OUTPUT_DIR}" +} +trap cleanup EXIT + +function run_soong_build { + USE_RBE=false TARGET_PRODUCT=aosp_arm TARGET_RELEASE=trunk_staging TARGET_BUILD_VARIANT=userdebug build/soong/soong_ui.bash --make-mode "$@" nothing +} + +function run_soong_clean { + build/soong/soong_ui.bash --make-mode clean +} + +function assert_files_equal { + if [ $# -ne 2 ]; then + echo "Usage: assert_files_equal file1 file2" + exit 1 + fi + + if ! cmp -s "$1" "$2"; then + echo "Files are different: $1 $2" + exit 1 + fi +} + +function compare_mtimes() { + if [ $# -ne 2 ]; then + echo "Usage: compare_mtimes file1 file2" + exit 1 + fi + + file1_mtime=$(stat -c '%Y' $1) + file2_mtime=$(stat -c '%Y' $2) + + if [ "$file1_mtime" -eq "$file2_mtime" ]; then + return 1 + else + return 0 + fi +} + +function test_build_action_restoring() { + local test_dir="${OUTPUT_DIR}/test_build_action_restoring" + mkdir -p ${test_dir} + run_soong_clean + cat > ${test_dir}/Android.bp <<'EOF' +python_binary_host { + name: "my_little_binary_host", + srcs: ["my_little_binary_host.py"], +} +EOF + touch ${test_dir}/my_little_binary_host.py + run_soong_build --incremental-build-actions + local dir_before="${test_dir}/before" + mkdir -p ${dir_before} + cp -pr out/soong/build_aosp_arm_ninja_incremental out/soong/*.mk out/soong/build.aosp_arm*.ninja ${test_dir}/before + # add a comment to the bp file, this should force a new analysis but no module + # should be really impacted, so all the incremental modules should be skipped. + cat >> ${test_dir}/Android.bp <<'EOF' +// new comments +EOF + run_soong_build --incremental-build-actions + local dir_after="${test_dir}/after" + mkdir -p ${dir_after} + cp -pr out/soong/build_aosp_arm_ninja_incremental out/soong/*.mk out/soong/build.aosp_arm*.ninja ${test_dir}/after + + compare_incremental_files $dir_before $dir_after + rm -rf "$test_dir" + echo "test_build_action_restoring test passed" +} + +function test_incremental_build_parity() { + local test_dir="${OUTPUT_DIR}/test_incremental_build_parity" + run_soong_clean + run_soong_build + local dir_before="${test_dir}/before" + mkdir -p ${dir_before} + cp -pr out/soong/*.mk out/soong/build.aosp_arm*.ninja ${test_dir}/before + + # Now run clean build with incremental enabled + run_soong_clean + run_soong_build --incremental-build-actions + local dir_after="${test_dir}/after" + mkdir -p ${dir_after} + cp -pr out/soong/build_aosp_arm_ninja_incremental out/soong/*.mk out/soong/build.aosp_arm*.ninja ${test_dir}/after + + compare_files_parity $dir_before $dir_after + rm -rf "$test_dir" + echo "test_incremental_build_parity test passed" +} + +function compare_files_parity() { + local dir_before=$1; shift + local dir_after=$1; shift + count=0 + for file_before in ${dir_before}/*.mk; do + file_after="${dir_after}/$(basename "$file_before")" + assert_files_equal $file_before $file_after + ((count++)) + done + echo "Compared $count mk files" + + combined_before_file="${dir_before}/combined_files.ninja" + count=0 + for file in ${dir_before}/build.aosp_arm.*.ninja; do + cat $file >> $combined_before_file + ((count++)) + done + echo "Combined $count ninja files from normal build" + + combined_after_file="${dir_after}/combined_files.ninja" + count=0 + for file in ${dir_after}/build.aosp_arm.*.ninja; do + cat $file >> $combined_after_file + ((count++)) + done + echo "Combined $count ninja files from incremental build" + + combined_incremental_ninjas="${dir_after}/combined_incremental_files.ninja" + count=0 + for file in ${dir_after}/build_aosp_arm_ninja_incremental/*.ninja; do + cat $file >> $combined_incremental_ninjas + ((count++)) + done + echo "Combined $count incremental ninja files" + + cat $combined_incremental_ninjas >> $combined_after_file + sort $combined_after_file -o $combined_after_file + sort $combined_before_file -o $combined_before_file + assert_files_equal $combined_before_file $combined_after_file +} + +function compare_incremental_files() { + local dir_before=$1; shift + local dir_after=$1; shift + count=0 + for file_before in ${dir_before}/*.ninja; do + file_after="${dir_after}/$(basename "$file_before")" + assert_files_equal $file_before $file_after + compare_mtimes $file_before $file_after + if [ $? -ne 0 ]; then + echo "Files have identical mtime: $file_before $file_after" + exit 1 + fi + ((count++)) + done + echo "Compared $count ninja files" + + count=0 + for file_before in ${dir_before}/*.mk; do + file_after="${dir_after}/$(basename "$file_before")" + assert_files_equal $file_before $file_after + compare_mtimes $file_before $file_after + # mk files shouldn't be regenerated + if [ $? -ne 1 ]; then + echo "Files have different mtimes: $file_before $file_after" + exit 1 + fi + ((count++)) + done + echo "Compared $count mk files" + + count=0 + for file_before in ${dir_before}/build_aosp_arm_ninja_incremental/*.ninja; do + file_after="${dir_after}/build_aosp_arm_ninja_incremental/$(basename "$file_before")" + assert_files_equal $file_before $file_after + compare_mtimes $file_before $file_after + # ninja files of skipped modules shouldn't be regenerated + if [ $? -ne 1 ]; then + echo "Files have different mtimes: $file_before $file_after" + exit 1 + fi + ((count++)) + done + echo "Compared $count incremental ninja files" +} + +test_incremental_build_parity +test_build_action_restoring diff --git a/tradefed/autogen.go b/tradefed/autogen.go index ddd0a800c..e23079591 100644 --- a/tradefed/autogen.go +++ b/tradefed/autogen.go @@ -196,16 +196,16 @@ func AutoGenTestConfig(ctx android.ModuleContext, options AutoGenTestConfigOptio } var autogenInstrumentationTest = pctx.StaticRule("autogenInstrumentationTest", blueprint.RuleParams{ - Command: "${AutoGenTestConfigScript} $out $in ${EmptyTestConfig} $template ${extraConfigs}", + Command: "${AutoGenTestConfigScript} $out $in ${EmptyTestConfig} $template ${extraConfigs} ${extraTestRunnerConfigs}", CommandDeps: []string{ "${AutoGenTestConfigScript}", "${EmptyTestConfig}", "$template", }, -}, "name", "template", "extraConfigs") +}, "name", "template", "extraConfigs", "extraTestRunnerConfigs") func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp *string, - testConfigTemplateProp *string, manifest android.Path, testSuites []string, autoGenConfig *bool, configs []Config) android.Path { + testConfigTemplateProp *string, manifest android.Path, testSuites []string, autoGenConfig *bool, configs []Config, testRunnerConfigs []Option) android.Path { path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) var configStrings []string if autogenPath != nil { @@ -220,15 +220,26 @@ func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp extraConfigs := strings.Join(configStrings, fmt.Sprintf("\\n%s", test_xml_indent)) extraConfigs = fmt.Sprintf("--extra-configs '%s'", extraConfigs) + var testRunnerConfigStrings []string + for _, config := range testRunnerConfigs { + testRunnerConfigStrings = append(testRunnerConfigStrings, config.Config()) + } + extraTestRunnerConfigs := strings.Join(testRunnerConfigStrings, fmt.Sprintf("\\n%s%s", test_xml_indent, test_xml_indent)) + if len(extraTestRunnerConfigs) > 0 { + extraTestRunnerConfigs += fmt.Sprintf("\\n%s%s", test_xml_indent, test_xml_indent) + } + extraTestRunnerConfigs = fmt.Sprintf("--extra-test-runner-configs '%s'", extraTestRunnerConfigs) + ctx.Build(pctx, android.BuildParams{ Rule: autogenInstrumentationTest, Description: "test config", Input: manifest, Output: autogenPath, Args: map[string]string{ - "name": ctx.ModuleName(), - "template": template, - "extraConfigs": extraConfigs, + "name": ctx.ModuleName(), + "template": template, + "extraConfigs": extraConfigs, + "extraTestRunnerConfigs": extraTestRunnerConfigs, }, }) return autogenPath diff --git a/tradefed_modules/test_module_config.go b/tradefed_modules/test_module_config.go index ef181317d..7a04c1994 100644 --- a/tradefed_modules/test_module_config.go +++ b/tradefed_modules/test_module_config.go @@ -242,6 +242,7 @@ func (m *testModuleConfigModule) AndroidMkEntries() []android.AndroidMkEntries { entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", m.provider.IsUnitTest) entries.AddCompatibilityTestSuites(m.tradefedProperties.Test_suites...) + entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", m.provider.HostRequiredModuleNames...) // The app_prebuilt_internal.mk files try create a copy of the OutputFile as an .apk. // Normally, this copies the "package.apk" from the intermediate directory here. diff --git a/tradefed_modules/test_module_config_test.go b/tradefed_modules/test_module_config_test.go index 1510a0331..f76a152eb 100644 --- a/tradefed_modules/test_module_config_test.go +++ b/tradefed_modules/test_module_config_test.go @@ -40,6 +40,7 @@ const bp = ` name: "base", sdk_version: "current", data: [":HelperApp", "data/testfile"], + host_required: ["other-module"], test_suites: ["general-tests"], } @@ -80,6 +81,7 @@ func TestModuleConfigAndroidTest(t *testing.T) { android.AssertArrayString(t, "", entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{}) android.AssertArrayString(t, "", entries.EntryMap["LOCAL_REQUIRED_MODULES"], []string{"base"}) + android.AssertArrayString(t, "", entries.EntryMap["LOCAL_HOST_REQUIRED_MODULES"], []string{"other-module"}) android.AssertArrayString(t, "", entries.EntryMap["LOCAL_CERTIFICATE"], []string{"build/make/target/product/security/testkey.x509.pem"}) android.AssertStringEquals(t, "", entries.Class, "APPS") diff --git a/ui/build/androidmk_denylist.go b/ui/build/androidmk_denylist.go index 2bad5a88f..2ec897273 100644 --- a/ui/build/androidmk_denylist.go +++ b/ui/build/androidmk_denylist.go @@ -25,6 +25,8 @@ var androidmk_denylist []string = []string{ "dalvik/", "developers/", "development/", + "device/common/", + "device/google_car/", "device/sample/", "frameworks/", // Do not block other directories in kernel/, see b/319658303. @@ -41,6 +43,15 @@ var androidmk_denylist []string = []string{ "trusty/", // Add back toolchain/ once defensive Android.mk files are removed //"toolchain/", + "vendor/google_contexthub/", + "vendor/google_data/", + "vendor/google_elmyra/", + "vendor/google_mhl/", + "vendor/google_pdk/", + "vendor/google_testing/", + "vendor/partner_testing/", + "vendor/partner_tools/", + "vendor/pdk/", } func blockAndroidMks(ctx Context, androidMks []string) { diff --git a/ui/build/config.go b/ui/build/config.go index f02222e1a..bd20442f7 100644 --- a/ui/build/config.go +++ b/ui/build/config.go @@ -54,6 +54,16 @@ func init() { rbeRandPrefix = rand.Intn(1000) } +// Which builder are we using? +type ninjaCommandType = int + +const ( + _ = iota + NINJA_NINJA + NINJA_N2 + NINJA_SISO +) + type Config struct{ *configImpl } type configImpl struct { @@ -88,6 +98,7 @@ type configImpl struct { buildFromSourceStub bool incrementalBuildActions bool ensureAllowlistIntegrity bool // For CI builds - make sure modules are mixed-built + partialCompileFlags partialCompileFlags // From the product config katiArgs []string @@ -123,9 +134,18 @@ type configImpl struct { // could consider merging them. moduleDebugFile string - // Whether to use n2 instead of ninja. This is controlled with the - // environment variable SOONG_USE_N2 - useN2 bool + // Which builder are we using + ninjaCommand ninjaCommandType +} + +type partialCompileFlags struct { + // Is partial compilation enabled at all? + enabled bool + + // Whether to use d8 instead of r8 + use_d8 bool + + // Add others as needed. } type NinjaWeightListSource uint @@ -284,12 +304,22 @@ func NewConfig(ctx Context, args ...string) Config { ret.sandboxConfig.SetSrcDirIsRO(srcDirIsWritable == "false") } + ret.partialCompileFlags = parsePartialCompileFlags(ctx) + if os.Getenv("GENERATE_SOONG_DEBUG") == "true" { ret.moduleDebugFile, _ = filepath.Abs(shared.JoinPath(ret.SoongOutDir(), "soong-debug-info.json")) } - if os.Getenv("SOONG_USE_N2") == "true" { - ret.useN2 = true + ret.ninjaCommand = NINJA_NINJA + switch os.Getenv("SOONG_NINJA") { + case "n2": + ret.ninjaCommand = NINJA_N2 + case "siso": + ret.ninjaCommand = NINJA_SISO + default: + if os.Getenv("SOONG_USE_N2") == "true" { + ret.ninjaCommand = NINJA_N2 + } } ret.environ.Unset( @@ -349,8 +379,10 @@ func NewConfig(ctx Context, args ...string) Config { // We read it here already, don't let others share in the fun "GENERATE_SOONG_DEBUG", - // Use config.useN2 instead. + // Use config.ninjaCommand instead. + "SOONG_NINJA", "SOONG_USE_N2", + "SOONG_PARTIAL_COMPILE", ) if ret.UseGoma() || ret.ForceUseGoma() { @@ -469,6 +501,78 @@ func NewConfig(ctx Context, args ...string) Config { return c } +// Parse SOONG_PARTIAL_COMPILE. +// +// The user-facing documentation shows: +// +// - empty or not set: "The current default state" +// - "true" or "on": enable all stable partial compile features. +// - "false" or "off": disable partial compile completely. +// +// What we actually allow is a comma separated list of tokens, whose first +// character may be "+" (enable) or "-" (disable). If neither is present, "+" +// is assumed. For example, "on,+use_d8" will enable partial compilation, and +// additionally set the use_d8 flag (regardless of whether it is opt-in or +// opt-out). +// +// To add a new feature to the list, add the field in the struct +// `partialCompileFlags` above, and then add the name of the field in the +// switch statement below. +func parsePartialCompileFlags(ctx Context) partialCompileFlags { + defaultFlags := partialCompileFlags{ + // Set any opt-out flags here. Opt-in flags are off by default. + enabled: false, + } + value, ok := os.LookupEnv("SOONG_PARTIAL_COMPILE") + + if !ok { + return defaultFlags + } + + ret := defaultFlags + tokens := strings.Split(strings.ToLower(value), ",") + makeVal := func(state string, defaultValue bool) bool { + switch state { + case "": + return defaultValue + case "-": + return false + case "+": + return true + } + return false + } + for _, tok := range tokens { + var state string + switch tok[0:1] { + case "": + // Ignore empty tokens. + continue + case "-", "+": + state = tok[0:1] + tok = tok[1:] + default: + // Treat `feature` as `+feature`. + state = "+" + } + switch tok { + case "true", "on", "yes": + ret = defaultFlags + ret.enabled = true + case "false", "off", "no": + // Set everything to false. + ret = partialCompileFlags{} + case "enabled": + ret.enabled = makeVal(state, defaultFlags.enabled) + case "use_d8": + ret.use_d8 = makeVal(state, defaultFlags.use_d8) + default: + ctx.Fatalln("Unknown SOONG_PARTIAL_COMPILE value:", value) + } + } + return ret +} + // NewBuildActionConfig returns a build configuration based on the build action. The arguments are // processed based on the build action and extracts any arguments that belongs to the build action. func NewBuildActionConfig(action BuildAction, dir string, ctx Context, args ...string) Config { @@ -1375,8 +1479,10 @@ func (c *configImpl) shouldCleanupRBELogsDir() bool { // Perform a log directory cleanup only when the log directory // is auto created by the build rather than user-specified. for _, f := range []string{"RBE_proxy_log_dir", "FLAG_output_dir"} { - if _, ok := c.environ.Get(f); ok { - return false + if v, ok := c.environ.Get(f); ok { + if v != c.rbeTmpDir() { + return false + } } } return true @@ -1641,6 +1747,12 @@ func (c *configImpl) N2Bin() string { return strings.ReplaceAll(path, "/linux-x86/", "/linux_musl-x86/") } +func (c *configImpl) SisoBin() string { + path := c.PrebuiltBuildTool("siso") + // Use musl instead of glibc because glibc on the build server is old and has bugs + return strings.ReplaceAll(path, "/linux-x86/", "/linux_musl-x86/") +} + func (c *configImpl) PrebuiltBuildTool(name string) string { if v, ok := c.environ.Get("SANITIZE_HOST"); ok { if sanitize := strings.Fields(v); inList("address", sanitize) { @@ -1745,6 +1857,10 @@ func (c *configImpl) EnsureAllowlistIntegrity() bool { return c.ensureAllowlistIntegrity } +func (c *configImpl) PartialCompileFlags() partialCompileFlags { + return c.partialCompileFlags +} + // Returns a Time object if one was passed via a command-line flag. // Otherwise returns the passed default. func (c *configImpl) BuildStartedTimeOrDefault(defaultTime time.Time) time.Time { diff --git a/ui/build/ninja.go b/ui/build/ninja.go index 4e3e54443..def0783a2 100644 --- a/ui/build/ninja.go +++ b/ui/build/ninja.go @@ -49,14 +49,10 @@ func runNinjaForBuild(ctx Context, config Config) { nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo) defer nr.Close() - executable := config.NinjaBin() - args := []string{ - "-d", "keepdepfile", - "-d", "keeprsp", - "-d", "stats", - "--frontend_file", fifo, - } - if config.useN2 { + var executable string + var args []string + switch config.ninjaCommand { + case NINJA_N2: executable = config.N2Bin() args = []string{ "-d", "trace", @@ -66,8 +62,31 @@ func runNinjaForBuild(ctx Context, config Config) { //"-d", "stats", "--frontend-file", fifo, } + case NINJA_SISO: + executable = config.SisoBin() + args = []string{ + "ninja", + "--log_dir", config.SoongOutDir(), + // TODO: implement these features, or remove them. + //"-d", "trace", + //"-d", "keepdepfile", + //"-d", "keeprsp", + //"-d", "stats", + //"--frontend-file", fifo, + } + default: + // NINJA_NINJA is the default. + executable = config.NinjaBin() + args = []string{ + "-d", "keepdepfile", + "-d", "keeprsp", + "-d", "stats", + "--frontend_file", fifo, + "-o", "usesphonyoutputs=yes", + "-w", "dupbuild=err", + "-w", "missingdepfile=err", + } } - args = append(args, config.NinjaArgs()...) var parallel int @@ -83,17 +102,10 @@ func runNinjaForBuild(ctx Context, config Config) { args = append(args, "-f", config.CombinedNinjaFile()) - if !config.useN2 { - args = append(args, - "-o", "usesphonyoutputs=yes", - "-w", "dupbuild=err", - "-w", "missingdepfile=err") - } - if !config.BuildBrokenMissingOutputs() { // Missing outputs will be treated as errors. // BUILD_BROKEN_MISSING_OUTPUTS can be used to bypass this check. - if !config.useN2 { + if config.ninjaCommand != NINJA_N2 { args = append(args, "-w", "missingoutfile=err", ) @@ -110,21 +122,18 @@ func runNinjaForBuild(ctx Context, config Config) { cmd.Environment.AppendFromKati(config.KatiEnvFile()) } - switch config.NinjaWeightListSource() { - case NINJA_LOG: - if !config.useN2 { + // TODO(b/346806126): implement this for the other ninjaCommand values. + if config.ninjaCommand == NINJA_NINJA { + switch config.NinjaWeightListSource() { + case NINJA_LOG: cmd.Args = append(cmd.Args, "-o", "usesninjalogasweightlist=yes") - } - case EVENLY_DISTRIBUTED: - // pass empty weight list means ninja considers every tasks's weight as 1(default value). - if !config.useN2 { + case EVENLY_DISTRIBUTED: + // pass empty weight list means ninja considers every tasks's weight as 1(default value). cmd.Args = append(cmd.Args, "-o", "usesweightlist=/dev/null") - } - case EXTERNAL_FILE: - fallthrough - case HINT_FROM_SOONG: - // The weight list is already copied/generated. - if !config.useN2 { + case EXTERNAL_FILE: + fallthrough + case HINT_FROM_SOONG: + // The weight list is already copied/generated. ninjaWeightListPath := filepath.Join(config.OutDir(), ninjaWeightListFileName) cmd.Args = append(cmd.Args, "-o", "usesweightlist="+ninjaWeightListPath) } @@ -227,6 +236,8 @@ func runNinjaForBuild(ctx Context, config Config) { // We don't want this build broken flag to cause reanalysis, so allow it through to the // actions. "BUILD_BROKEN_INCORRECT_PARTITION_IMAGES", + // Do not do reanalysis just because we changed ninja commands. + "SOONG_NINJA", "SOONG_USE_N2", "RUST_BACKTRACE", "RUST_LOG", @@ -235,8 +246,11 @@ func runNinjaForBuild(ctx Context, config Config) { cmd.Environment.Set("DIST_DIR", config.DistDir()) cmd.Environment.Set("SHELL", "/bin/bash") - if config.useN2 { + switch config.ninjaCommand { + case NINJA_N2: cmd.Environment.Set("RUST_BACKTRACE", "1") + default: + // Only set RUST_BACKTRACE for n2. } // Print the environment variables that Ninja is operating in. diff --git a/ui/build/soong.go b/ui/build/soong.go index 97bc9971e..f70d9b7f3 100644 --- a/ui/build/soong.go +++ b/ui/build/soong.go @@ -27,6 +27,7 @@ import ( "strings" "sync" "sync/atomic" + "syscall" "time" "android/soong/ui/tracer" @@ -588,19 +589,11 @@ func runSoong(ctx Context, config Config) { nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo) defer nr.Close() - ninjaArgs := []string{ - "-d", "keepdepfile", - "-d", "stats", - "-o", "usesphonyoutputs=yes", - "-o", "preremoveoutputs=yes", - "-w", "dupbuild=err", - "-w", "outputdir=err", - "-w", "missingoutfile=err", - "-j", strconv.Itoa(config.Parallel()), - "--frontend_file", fifo, - "-f", filepath.Join(config.SoongOutDir(), "bootstrap.ninja"), - } - if config.useN2 { + var ninjaCmd string + var ninjaArgs []string + switch config.ninjaCommand { + case NINJA_N2: + ninjaCmd = config.N2Bin() ninjaArgs = []string{ // TODO: implement these features, or remove them. //"-d", "keepdepfile", @@ -615,6 +608,39 @@ func runSoong(ctx Context, config Config) { "--frontend-file", fifo, "-f", filepath.Join(config.SoongOutDir(), "bootstrap.ninja"), } + case NINJA_SISO: + ninjaCmd = config.SisoBin() + ninjaArgs = []string{ + "ninja", + // TODO: implement these features, or remove them. + //"-d", "keepdepfile", + //"-d", "stats", + //"-o", "usesphonyoutputs=yes", + //"-o", "preremoveoutputs=yes", + //"-w", "dupbuild=err", + //"-w", "outputdir=err", + //"-w", "missingoutfile=err", + "-v", + "-j", strconv.Itoa(config.Parallel()), + //"--frontend-file", fifo, + "--log_dir", config.SoongOutDir(), + "-f", filepath.Join(config.SoongOutDir(), "bootstrap.ninja"), + } + default: + // NINJA_NINJA is the default. + ninjaCmd = config.NinjaBin() + ninjaArgs = []string{ + "-d", "keepdepfile", + "-d", "stats", + "-o", "usesphonyoutputs=yes", + "-o", "preremoveoutputs=yes", + "-w", "dupbuild=err", + "-w", "outputdir=err", + "-w", "missingoutfile=err", + "-j", strconv.Itoa(config.Parallel()), + "--frontend_file", fifo, + "-f", filepath.Join(config.SoongOutDir(), "bootstrap.ninja"), + } } if extra, ok := config.Environment().Get("SOONG_UI_NINJA_ARGS"); ok { @@ -623,10 +649,6 @@ func runSoong(ctx Context, config Config) { } ninjaArgs = append(ninjaArgs, targets...) - ninjaCmd := config.NinjaBin() - if config.useN2 { - ninjaCmd = config.N2Bin() - } cmd := Command(ctx, config, "soong bootstrap", ninjaCmd, ninjaArgs...) @@ -744,7 +766,11 @@ func checkGlobs(ctx Context, finalOutFile string) error { globsChan := make(chan pathtools.GlobResult) errorsChan := make(chan error) wg := sync.WaitGroup{} + hasChangedGlobs := false + var changedGlobNameMutex sync.Mutex + var changedGlobName string + for i := 0; i < runtime.NumCPU()*2; i++ { wg.Add(1) go func() { @@ -759,7 +785,7 @@ func checkGlobs(ctx Context, finalOutFile string) error { hasNewDep := false for _, dep := range cachedGlob.Deps { info, err := os.Stat(dep) - if errors.Is(err, fs.ErrNotExist) { + if errors.Is(err, fs.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) { hasNewDep = true break } else if err != nil { @@ -782,6 +808,13 @@ func checkGlobs(ctx Context, finalOutFile string) error { } else { if !slices.Equal(result.Matches, cachedGlob.Matches) { hasChangedGlobs = true + + changedGlobNameMutex.Lock() + defer changedGlobNameMutex.Unlock() + changedGlobName = result.Pattern + if len(result.Excludes) > 0 { + changedGlobName += " (excluding " + strings.Join(result.Excludes, ", ") + ")" + } } } } @@ -833,6 +866,7 @@ func checkGlobs(ctx Context, finalOutFile string) error { if hasChangedGlobs { fmt.Fprintf(os.Stdout, "Globs changed, rerunning soong...\n") + fmt.Fprintf(os.Stdout, "One culprit glob (may be more): %s\n", changedGlobName) // Write the current time to the glob_results file. We just need // some unique value to trigger a rerun, it doesn't matter what it is. err = os.WriteFile( diff --git a/zip/cmd/Android.bp b/zip/cmd/Android.bp index 43bf232a0..16c3f698f 100644 --- a/zip/cmd/Android.bp +++ b/zip/cmd/Android.bp @@ -24,4 +24,6 @@ blueprint_go_binary { srcs: [ "main.go", ], + // Used by genrules + visibility: ["//visibility:public"], } |