diff --git a/Android.bp b/Android.bp index 81d91ced0..535246e65 100644 --- a/Android.bp +++ b/Android.bp @@ -1,5 +1,8 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], + default_visibility: [ + "//build/soong:__subpackages__", + ], } subdirs = [ @@ -23,6 +26,8 @@ bootstrap_go_package { srcs: [ "doc.go", ], + // Used by plugins, though probably shouldn't be. + visibility: ["//visibility:public"], } // @@ -40,6 +45,7 @@ cc_defaults { enabled: true, }, }, + defaults_visibility: ["//visibility:public"], } // @@ -51,6 +57,7 @@ kernel_headers { vendor: true, recovery_available: true, min_sdk_version: "apex_inherit", + visibility: ["//visibility:public"], } cc_genrule { @@ -75,6 +82,7 @@ cc_genrule { cmd: "$(location) -s $(out) $(in)", srcs: [":linker"], out: ["linker.s"], + visibility: ["//bionic/libc"], } cc_genrule { @@ -99,12 +107,13 @@ 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", - no_full_install: true, + visibility: ["//visibility:public"], } // Pseudo-test that's run on checkbuilds to ensure that get_clang_version can @@ -124,6 +133,7 @@ dexpreopt_systemserver_check { // container for apex_contributions selected using build flags all_apex_contributions { name: "all_apex_contributions", + visibility: ["//visibility:public"], } product_config { @@ -138,7 +148,7 @@ build_prop { // Currently, only microdroid and cf system image can refer to system-build.prop visibility: [ "//device/google/cuttlefish/system_image", - "//packages/modules/Virtualization/microdroid", + "//packages/modules/Virtualization/build/microdroid", ], } diff --git a/README.md b/README.md index 140822b70..ad282a59e 100644 --- a/README.md +++ b/README.md @@ -594,19 +594,13 @@ modules (`cc_defaults`, `java_defaults`, etc.), which can then be referenced by all of the vendor's other modules using the normal namespace and visibility rules. -`soongConfigTraceMutator` enables modules affected by soong config variables to -write outputs into a hashed directory path. It does this by recording accesses -to soong config variables on each module, and then accumulating records of each -module's all dependencies. `m soong_config_trace` builds information about -hashes to `$OUT_DIR/soong/soong_config_trace.json`. - ## Build logic The build logic is written in Go using the -[blueprint](http://godoc.org/github.com/google/blueprint) framework. Build -logic receives module definitions parsed into Go structures using reflection -and produces build rules. The build rules are collected by blueprint and -written to a [ninja](http://ninja-build.org) build file. +[blueprint](https://android.googlesource.com/platform/build/blueprint) +framework. Build logic receives module definitions parsed into Go structures +using reflection and produces build rules. The build rules are collected by +blueprint and written to a [ninja](http://ninja-build.org) build file. ## Environment Variables Config File diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go index dac0ae36d..d9a862c02 100644 --- a/aconfig/aconfig_declarations.go +++ b/aconfig/aconfig_declarations.go @@ -15,6 +15,8 @@ package aconfig import ( + "path/filepath" + "slices" "strings" "android/soong/android" @@ -22,9 +24,15 @@ import ( "github.com/google/blueprint" ) +type AconfigReleaseConfigValue struct { + ReleaseConfig string + Values []string `blueprint:"mutated"` +} + type DeclarationsModule struct { android.ModuleBase android.DefaultableModuleBase + blueprint.IncrementalModule // Properties for "aconfig_declarations" properties struct { @@ -34,8 +42,10 @@ type DeclarationsModule struct { // Release config flag package Package string - // Values from TARGET_RELEASE / RELEASE_ACONFIG_VALUE_SETS - Values []string `blueprint:"mutated"` + // Values for release configs / RELEASE_ACONFIG_VALUE_SETS + // The current release config is `ReleaseConfig: ""`, others + // are from RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS. + ReleaseConfigValues []AconfigReleaseConfigValue // Container(system/vendor/apex) that this module belongs to Container string @@ -57,6 +67,10 @@ func DeclarationsFactory() android.Module { type implicitValuesTagType struct { blueprint.BaseDependencyTag + + // The release config name for these values. + // Empty string for the actual current release config. + ReleaseConfig string } var implicitValuesTag = implicitValuesTagType{} @@ -74,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. @@ -81,6 +102,11 @@ func (module *DeclarationsModule) DepsMutator(ctx android.BottomUpMutatorContext if len(valuesFromConfig) > 0 { ctx.AddDependency(ctx.Module(), implicitValuesTag, valuesFromConfig...) } + for rcName, valueSets := range ctx.Config().ReleaseAconfigExtraReleaseConfigsValueSets() { + if len(valueSets) > 0 { + ctx.AddDependency(ctx.Module(), implicitValuesTagType{ReleaseConfig: rcName}, valueSets...) + } + } } func joinAndPrefix(prefix string, values []string) string { @@ -101,59 +127,103 @@ func optionalVariable(prefix string, value string) string { return sb.String() } +// Assemble the actual filename. +// If `rcName` is not empty, then insert "-{rcName}" into the path before the +// file extension. +func assembleFileName(rcName, path string) string { + if rcName == "" { + return path + } + dir, file := filepath.Split(path) + rcName = "-" + rcName + ext := filepath.Ext(file) + base := file[:len(file)-len(ext)] + return dir + base + rcName + ext +} + func (module *DeclarationsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { - // Get the values that came from the global RELEASE_ACONFIG_VALUE_SETS flag - valuesFiles := make([]android.Path, 0) + // Determine which release configs we are processing. + // + // We always process the current release config (empty string). + // We may have been told to also create artifacts for some others. + configs := append([]string{""}, ctx.Config().ReleaseAconfigExtraReleaseConfigs()...) + slices.Sort(configs) + + values := make(map[string][]string) + valuesFiles := make(map[string][]android.Path, 0) + providerData := android.AconfigReleaseDeclarationsProviderData{} ctx.VisitDirectDeps(func(dep android.Module) { if depData, ok := android.OtherModuleProvider(ctx, dep, valueSetProviderKey); ok { - paths, ok := depData.AvailablePackages[module.properties.Package] - if ok { - valuesFiles = append(valuesFiles, paths...) - for _, path := range paths { - module.properties.Values = append(module.properties.Values, path.String()) + depTag := ctx.OtherModuleDependencyTag(dep) + for _, config := range configs { + tag := implicitValuesTagType{ReleaseConfig: config} + if depTag == tag { + paths, ok := depData.AvailablePackages[module.properties.Package] + if ok { + valuesFiles[config] = append(valuesFiles[config], paths...) + for _, path := range paths { + values[config] = append(values[config], path.String()) + } + } } } } }) + for _, config := range configs { + module.properties.ReleaseConfigValues = append(module.properties.ReleaseConfigValues, AconfigReleaseConfigValue{ + ReleaseConfig: config, + Values: values[config], + }) - // Intermediate format - declarationFiles := android.PathsForModuleSrc(ctx, module.properties.Srcs) - intermediateCacheFilePath := android.PathForModuleOut(ctx, "intermediate.pb") - defaultPermission := ctx.Config().ReleaseAconfigFlagDefaultPermission() - inputFiles := make([]android.Path, len(declarationFiles)) - copy(inputFiles, declarationFiles) - inputFiles = append(inputFiles, valuesFiles...) - args := map[string]string{ - "release_version": ctx.Config().ReleaseVersion(), - "package": module.properties.Package, - "declarations": android.JoinPathsWithPrefix(declarationFiles, "--declarations "), - "values": joinAndPrefix(" --values ", module.properties.Values), - "default-permission": optionalVariable(" --default-permission ", defaultPermission), + // Intermediate format + declarationFiles := android.PathsForModuleSrc(ctx, module.properties.Srcs) + intermediateCacheFilePath := android.PathForModuleOut(ctx, assembleFileName(config, "intermediate.pb")) + var defaultPermission string + defaultPermission = ctx.Config().ReleaseAconfigFlagDefaultPermission() + if config != "" { + if confPerm, ok := ctx.Config().GetBuildFlag("RELEASE_ACONFIG_FLAG_DEFAULT_PERMISSION_" + config); ok { + defaultPermission = confPerm + } + } + inputFiles := make([]android.Path, len(declarationFiles)) + copy(inputFiles, declarationFiles) + inputFiles = append(inputFiles, valuesFiles[config]...) + args := map[string]string{ + "release_version": ctx.Config().ReleaseVersion(), + "package": module.properties.Package, + "declarations": android.JoinPathsWithPrefix(declarationFiles, "--declarations "), + "values": joinAndPrefix(" --values ", values[config]), + "default-permission": optionalVariable(" --default-permission ", defaultPermission), + } + if len(module.properties.Container) > 0 { + args["container"] = "--container " + module.properties.Container + } + ctx.Build(pctx, android.BuildParams{ + Rule: aconfigRule, + Output: intermediateCacheFilePath, + Inputs: inputFiles, + Description: "aconfig_declarations", + Args: args, + }) + + intermediateDumpFilePath := android.PathForModuleOut(ctx, assembleFileName(config, "intermediate.txt")) + ctx.Build(pctx, android.BuildParams{ + Rule: aconfigTextRule, + Output: intermediateDumpFilePath, + Inputs: android.Paths{intermediateCacheFilePath}, + Description: "aconfig_text", + }) + + providerData[config] = android.AconfigDeclarationsProviderData{ + Package: module.properties.Package, + Container: module.properties.Container, + Exportable: module.properties.Exportable, + IntermediateCacheOutputPath: intermediateCacheFilePath, + IntermediateDumpOutputPath: intermediateDumpFilePath, + } } - if len(module.properties.Container) > 0 { - args["container"] = "--container " + module.properties.Container - } - ctx.Build(pctx, android.BuildParams{ - Rule: aconfigRule, - Output: intermediateCacheFilePath, - Inputs: inputFiles, - Description: "aconfig_declarations", - Args: args, - }) - - intermediateDumpFilePath := android.PathForModuleOut(ctx, "intermediate.txt") - ctx.Build(pctx, android.BuildParams{ - Rule: aconfigTextRule, - Output: intermediateDumpFilePath, - Inputs: android.Paths{intermediateCacheFilePath}, - Description: "aconfig_text", - }) - - android.SetProvider(ctx, android.AconfigDeclarationsProviderKey, android.AconfigDeclarationsProviderData{ - Package: module.properties.Package, - Container: module.properties.Container, - Exportable: module.properties.Exportable, - IntermediateCacheOutputPath: intermediateCacheFilePath, - IntermediateDumpOutputPath: intermediateDumpFilePath, - }) + android.SetProvider(ctx, android.AconfigDeclarationsProviderKey, providerData[""]) + android.SetProvider(ctx, android.AconfigReleaseDeclarationsProviderKey, providerData) } + +var _ blueprint.Incremental = &DeclarationsModule{} diff --git a/aconfig/aconfig_declarations_test.go b/aconfig/aconfig_declarations_test.go index c37274c71..e89cd316f 100644 --- a/aconfig/aconfig_declarations_test.go +++ b/aconfig/aconfig_declarations_test.go @@ -15,6 +15,7 @@ package aconfig import ( + "slices" "strings" "testing" @@ -39,7 +40,7 @@ func TestAconfigDeclarations(t *testing.T) { module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule) // Check that the provider has the right contents - depData, _ := android.SingletonModuleProvider(result, module, android.AconfigDeclarationsProviderKey) + depData, _ := android.OtherModuleProvider(result, module, android.AconfigDeclarationsProviderKey) android.AssertStringEquals(t, "package", depData.Package, "com.example.package") android.AssertStringEquals(t, "container", depData.Container, "com.android.foo") android.AssertBoolEquals(t, "exportable", depData.Exportable, true) @@ -66,7 +67,7 @@ func TestAconfigDeclarationsWithExportableUnset(t *testing.T) { result := runTest(t, android.FixtureExpectsNoErrors, bp) module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule) - depData, _ := android.SingletonModuleProvider(result, module, android.AconfigDeclarationsProviderKey) + depData, _ := android.OtherModuleProvider(result, module, android.AconfigDeclarationsProviderKey) android.AssertBoolEquals(t, "exportable", depData.Exportable, false) } @@ -134,3 +135,95 @@ func TestMandatoryProperties(t *testing.T) { }) } } + +func TestAssembleFileName(t *testing.T) { + testCases := []struct { + name string + releaseConfig string + path string + expectedValue string + }{ + { + name: "active release config", + path: "file.path", + expectedValue: "file.path", + }, + { + name: "release config FOO", + releaseConfig: "FOO", + path: "file.path", + expectedValue: "file-FOO.path", + }, + } + for _, test := range testCases { + actualValue := assembleFileName(test.releaseConfig, test.path) + if actualValue != test.expectedValue { + t.Errorf("Expected %q found %q", test.expectedValue, actualValue) + } + } +} + +func TestGenerateAndroidBuildActions(t *testing.T) { + testCases := []struct { + name string + buildFlags map[string]string + bp string + errorHandler android.FixtureErrorHandler + }{ + { + name: "generate extra", + buildFlags: map[string]string{ + "RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS": "config2", + "RELEASE_ACONFIG_VALUE_SETS": "aconfig_value_set-config1", + "RELEASE_ACONFIG_VALUE_SETS_config2": "aconfig_value_set-config2", + }, + bp: ` + aconfig_declarations { + name: "module_name", + package: "com.example.package", + container: "com.android.foo", + srcs: [ + "foo.aconfig", + "bar.aconfig", + ], + } + aconfig_value_set { + name: "aconfig_value_set-config1", + values: [] + } + aconfig_value_set { + name: "aconfig_value_set-config2", + values: [] + } + `, + }, + } + for _, test := range testCases { + fixture := PrepareForTest(t, addBuildFlagsForTest(test.buildFlags)) + if test.errorHandler != nil { + fixture = fixture.ExtendWithErrorHandler(test.errorHandler) + } + result := fixture.RunTestWithBp(t, test.bp) + module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule) + depData, _ := android.OtherModuleProvider(result, module, android.AconfigReleaseDeclarationsProviderKey) + expectedKeys := []string{""} + for _, rc := range strings.Split(test.buildFlags["RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS"], " ") { + expectedKeys = append(expectedKeys, rc) + } + slices.Sort(expectedKeys) + actualKeys := []string{} + for rc := range depData { + actualKeys = append(actualKeys, rc) + } + slices.Sort(actualKeys) + android.AssertStringEquals(t, "provider keys", strings.Join(expectedKeys, " "), strings.Join(actualKeys, " ")) + for _, rc := range actualKeys { + if !strings.HasSuffix(depData[rc].IntermediateCacheOutputPath.String(), assembleFileName(rc, "/intermediate.pb")) { + t.Errorf("Incorrect intermediates proto path in provider for release config %s: %s", rc, depData[rc].IntermediateCacheOutputPath.String()) + } + if !strings.HasSuffix(depData[rc].IntermediateDumpOutputPath.String(), assembleFileName(rc, "/intermediate.txt")) { + t.Errorf("Incorrect intermediates text path in provider for release config %s: %s", rc, depData[rc].IntermediateDumpOutputPath.String()) + } + } + } +} 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 7d1899926..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) { @@ -38,6 +40,115 @@ func TestAconfigValueSet(t *testing.T) { module := result.ModuleForTests("module_name", "").Module().(*ValueSetModule) // Check that the provider has the right contents - depData, _ := android.SingletonModuleProvider(result, module, valueSetProviderKey) + 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/aconfig_values_test.go b/aconfig/aconfig_values_test.go index 526579c40..ddbea57a8 100644 --- a/aconfig/aconfig_values_test.go +++ b/aconfig/aconfig_values_test.go @@ -33,7 +33,7 @@ func TestAconfigValues(t *testing.T) { module := result.ModuleForTests("module_name", "").Module().(*ValuesModule) // Check that the provider has the right contents - depData, _ := android.SingletonModuleProvider(result, module, valuesProviderKey) + depData, _ := android.OtherModuleProvider(result, module, valuesProviderKey) android.AssertStringEquals(t, "package", "foo.package", depData.Package) android.AssertPathsEndWith(t, "srcs", []string{"blah.aconfig_values"}, depData.Values) } diff --git a/aconfig/all_aconfig_declarations.go b/aconfig/all_aconfig_declarations.go index e771d0597..6ad54da4a 100644 --- a/aconfig/all_aconfig_declarations.go +++ b/aconfig/all_aconfig_declarations.go @@ -15,8 +15,10 @@ package aconfig import ( - "android/soong/android" "fmt" + "slices" + + "android/soong/android" ) // A singleton module that collects all of the aconfig flags declared in the @@ -27,70 +29,90 @@ import ( // ones that are relevant to the product currently being built, so that that infra // doesn't need to pull from multiple builds and merge them. func AllAconfigDeclarationsFactory() android.Singleton { - return &allAconfigDeclarationsSingleton{} + return &allAconfigDeclarationsSingleton{releaseMap: make(map[string]allAconfigReleaseDeclarationsSingleton)} } -type allAconfigDeclarationsSingleton struct { +type allAconfigReleaseDeclarationsSingleton struct { intermediateBinaryProtoPath android.OutputPath intermediateTextProtoPath android.OutputPath } +type allAconfigDeclarationsSingleton struct { + releaseMap map[string]allAconfigReleaseDeclarationsSingleton +} + +func (this *allAconfigDeclarationsSingleton) sortedConfigNames() []string { + var names []string + for k := range this.releaseMap { + names = append(names, k) + } + slices.Sort(names) + return names +} + func (this *allAconfigDeclarationsSingleton) GenerateBuildActions(ctx android.SingletonContext) { - // Find all of the aconfig_declarations modules - var packages = make(map[string]int) - var cacheFiles android.Paths - ctx.VisitAllModules(func(module android.Module) { - decl, ok := android.SingletonModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey) - if !ok { - return + for _, rcName := range append([]string{""}, ctx.Config().ReleaseAconfigExtraReleaseConfigs()...) { + // Find all of the aconfig_declarations modules + var packages = make(map[string]int) + var cacheFiles android.Paths + ctx.VisitAllModules(func(module android.Module) { + decl, ok := android.OtherModuleProvider(ctx, module, android.AconfigReleaseDeclarationsProviderKey) + if !ok { + return + } + cacheFiles = append(cacheFiles, decl[rcName].IntermediateCacheOutputPath) + packages[decl[rcName].Package]++ + }) + + var numOffendingPkg = 0 + for pkg, cnt := range packages { + if cnt > 1 { + fmt.Printf("%d aconfig_declarations found for package %s\n", cnt, pkg) + numOffendingPkg++ + } } - cacheFiles = append(cacheFiles, decl.IntermediateCacheOutputPath) - packages[decl.Package]++ - }) - var numOffendingPkg = 0 - for pkg, cnt := range packages { - if cnt > 1 { - fmt.Printf("%d aconfig_declarations found for package %s\n", cnt, pkg) - numOffendingPkg++ + if numOffendingPkg > 0 { + panic(fmt.Errorf("Only one aconfig_declarations allowed for each package.")) } + + // Generate build action for aconfig (binary proto output) + paths := allAconfigReleaseDeclarationsSingleton{ + intermediateBinaryProtoPath: android.PathForIntermediates(ctx, assembleFileName(rcName, "all_aconfig_declarations.pb")), + intermediateTextProtoPath: android.PathForIntermediates(ctx, assembleFileName(rcName, "all_aconfig_declarations.textproto")), + } + this.releaseMap[rcName] = paths + ctx.Build(pctx, android.BuildParams{ + Rule: AllDeclarationsRule, + Inputs: cacheFiles, + Output: this.releaseMap[rcName].intermediateBinaryProtoPath, + Description: "all_aconfig_declarations", + Args: map[string]string{ + "cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "), + }, + }) + ctx.Phony("all_aconfig_declarations", this.releaseMap[rcName].intermediateBinaryProtoPath) + + // Generate build action for aconfig (text proto output) + ctx.Build(pctx, android.BuildParams{ + Rule: AllDeclarationsRuleTextProto, + Inputs: cacheFiles, + Output: this.releaseMap[rcName].intermediateTextProtoPath, + Description: "all_aconfig_declarations_textproto", + Args: map[string]string{ + "cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "), + }, + }) + ctx.Phony("all_aconfig_declarations_textproto", this.releaseMap[rcName].intermediateTextProtoPath) } - - if numOffendingPkg > 0 { - panic(fmt.Errorf("Only one aconfig_declarations allowed for each package.")) - } - - // Generate build action for aconfig (binary proto output) - this.intermediateBinaryProtoPath = android.PathForIntermediates(ctx, "all_aconfig_declarations.pb") - ctx.Build(pctx, android.BuildParams{ - Rule: AllDeclarationsRule, - Inputs: cacheFiles, - Output: this.intermediateBinaryProtoPath, - Description: "all_aconfig_declarations", - Args: map[string]string{ - "cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "), - }, - }) - ctx.Phony("all_aconfig_declarations", this.intermediateBinaryProtoPath) - - // Generate build action for aconfig (text proto output) - this.intermediateTextProtoPath = android.PathForIntermediates(ctx, "all_aconfig_declarations.textproto") - ctx.Build(pctx, android.BuildParams{ - Rule: AllDeclarationsRuleTextProto, - Inputs: cacheFiles, - Output: this.intermediateTextProtoPath, - Description: "all_aconfig_declarations_textproto", - Args: map[string]string{ - "cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "), - }, - }) - ctx.Phony("all_aconfig_declarations_textproto", this.intermediateTextProtoPath) } func (this *allAconfigDeclarationsSingleton) MakeVars(ctx android.MakeVarsContext) { - ctx.DistForGoal("droid", this.intermediateBinaryProtoPath) - for _, goal := range []string{"docs", "droid", "sdk"} { - ctx.DistForGoalWithFilename(goal, this.intermediateBinaryProtoPath, "flags.pb") - ctx.DistForGoalWithFilename(goal, this.intermediateTextProtoPath, "flags.textproto") + for _, rcName := range this.sortedConfigNames() { + ctx.DistForGoal("droid", this.releaseMap[rcName].intermediateBinaryProtoPath) + for _, goal := range []string{"docs", "droid", "sdk"} { + ctx.DistForGoalWithFilename(goal, this.releaseMap[rcName].intermediateBinaryProtoPath, assembleFileName(rcName, "flags.pb")) + ctx.DistForGoalWithFilename(goal, this.releaseMap[rcName].intermediateTextProtoPath, assembleFileName(rcName, "flags.textproto")) + } } } diff --git a/aconfig/build_flags/all_build_flag_declarations.go b/aconfig/build_flags/all_build_flag_declarations.go index 282c9dcf8..5f0291262 100644 --- a/aconfig/build_flags/all_build_flag_declarations.go +++ b/aconfig/build_flags/all_build_flag_declarations.go @@ -38,7 +38,7 @@ func (this *allBuildFlagDeclarationsSingleton) GenerateBuildActions(ctx android. // Find all of the build_flag_declarations modules var intermediateFiles android.Paths ctx.VisitAllModules(func(module android.Module) { - decl, ok := android.SingletonModuleProvider(ctx, module, BuildFlagDeclarationsProviderKey) + decl, ok := android.OtherModuleProvider(ctx, module, BuildFlagDeclarationsProviderKey) if !ok { return } diff --git a/aconfig/codegen/Android.bp b/aconfig/codegen/Android.bp index 0c78b946b..5fac0a8d1 100644 --- a/aconfig/codegen/Android.bp +++ b/aconfig/codegen/Android.bp @@ -12,7 +12,6 @@ bootstrap_go_package { "soong", "soong-aconfig", "soong-android", - "soong-bazel", "soong-java", "soong-rust", ], diff --git a/aconfig/codegen/init.go b/aconfig/codegen/init.go index 98d288f0a..ed0b3ed7f 100644 --- a/aconfig/codegen/init.go +++ b/aconfig/codegen/init.go @@ -32,6 +32,7 @@ var ( ` --mode ${mode}` + ` --cache ${in}` + ` --out ${out}.tmp` + + ` --allow-instrumentation ${debug}` + ` && $soong_zip -write_if_changed -jar -o ${out} -C ${out}.tmp -D ${out}.tmp` + ` && rm -rf ${out}.tmp`, CommandDeps: []string{ @@ -39,7 +40,7 @@ var ( "$soong_zip", }, Restat: true, - }, "mode") + }, "mode", "debug") // For cc_aconfig_library: Generate C++ library cppRule = pctx.AndroidStaticRule("cc_aconfig_library", diff --git a/aconfig/codegen/java_aconfig_library.go b/aconfig/codegen/java_aconfig_library.go index 673ac2afe..ebca4134c 100644 --- a/aconfig/codegen/java_aconfig_library.go +++ b/aconfig/codegen/java_aconfig_library.go @@ -20,6 +20,7 @@ import ( "github.com/google/blueprint" "github.com/google/blueprint/proptools" + "strconv" ) type declarationsTagType struct { @@ -71,6 +72,7 @@ func (callbacks *JavaAconfigDeclarationsLibraryCallbacks) DepsMutator(module *ja module.AddSharedLibrary("aconfig-annotations-lib") // TODO(b/303773055): Remove the annotation after access issue is resolved. module.AddSharedLibrary("unsupportedappusage") + module.AddSharedLibrary("aconfig_storage_reader_java") } } @@ -102,7 +104,8 @@ func (callbacks *JavaAconfigDeclarationsLibraryCallbacks) GenerateSourceJarBuild Output: srcJarPath, Description: "aconfig.srcjar", Args: map[string]string{ - "mode": mode, + "mode": mode, + "debug": strconv.FormatBool(ctx.Config().ReleaseReadFromNewStorage()), }, }) 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/aconfig/exported_java_aconfig_library.go b/aconfig/exported_java_aconfig_library.go index 291938fa8..a64cac882 100644 --- a/aconfig/exported_java_aconfig_library.go +++ b/aconfig/exported_java_aconfig_library.go @@ -30,7 +30,7 @@ func (this *exportedJavaDeclarationsLibrarySingleton) GenerateBuildActions(ctx a // Find all of the aconfig_declarations modules var cacheFiles android.Paths ctx.VisitAllModules(func(module android.Module) { - decl, ok := android.SingletonModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey) + decl, ok := android.OtherModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey) if !ok { return } diff --git a/aconfig/init.go b/aconfig/init.go index 46554676b..6f91d8edd 100644 --- a/aconfig/init.go +++ b/aconfig/init.go @@ -44,7 +44,7 @@ var ( // For create-device-config-sysprops: Generate aconfig flag value map text file aconfigTextRule = pctx.AndroidStaticRule("aconfig_text", blueprint.RuleParams{ - Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}={state:bool}'` + + Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}:{permission}={state:bool}'` + ` --cache ${in}` + ` --out ${out}.tmp` + ` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`, diff --git a/aconfig/testing.go b/aconfig/testing.go index f6489ec3f..4ceb6b3e5 100644 --- a/aconfig/testing.go +++ b/aconfig/testing.go @@ -23,7 +23,25 @@ import ( var PrepareForTestWithAconfigBuildComponents = android.FixtureRegisterWithContext(RegisterBuildComponents) func runTest(t *testing.T, errorHandler android.FixtureErrorHandler, bp string) *android.TestResult { - return android.GroupFixturePreparers(PrepareForTestWithAconfigBuildComponents). + return PrepareForTest(t). ExtendWithErrorHandler(errorHandler). RunTestWithBp(t, bp) } + +func PrepareForTest(t *testing.T, preparers ...android.FixturePreparer) android.FixturePreparer { + preparers = append([]android.FixturePreparer{PrepareForTestWithAconfigBuildComponents}, preparers...) + return android.GroupFixturePreparers(preparers...) +} + +func addBuildFlagsForTest(buildFlags map[string]string) android.FixturePreparer { + return android.GroupFixturePreparers( + android.FixtureModifyProductVariables(func(vars android.FixtureProductVariables) { + if vars.BuildFlags == nil { + vars.BuildFlags = make(map[string]string) + } + for k, v := range buildFlags { + vars.BuildFlags[k] = v + } + }), + ) +} 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/aidl_library/aidl_library_test.go b/aidl_library/aidl_library_test.go index 01eab0eaf..166045635 100644 --- a/aidl_library/aidl_library_test.go +++ b/aidl_library/aidl_library_test.go @@ -15,8 +15,9 @@ package aidl_library import ( - "android/soong/android" "testing" + + "android/soong/android" ) func TestAidlLibrary(t *testing.T) { @@ -46,7 +47,7 @@ func TestAidlLibrary(t *testing.T) { ).RunTest(t).TestContext foo := ctx.ModuleForTests("foo", "").Module().(*AidlLibrary) - actualInfo, _ := android.SingletonModuleProvider(ctx, foo, AidlLibraryProvider) + actualInfo, _ := android.OtherModuleProvider(ctx, foo, AidlLibraryProvider) android.AssertArrayString( t, @@ -95,7 +96,7 @@ func TestAidlLibraryWithoutStripImportPrefix(t *testing.T) { ).RunTest(t).TestContext foo := ctx.ModuleForTests("foo", "").Module().(*AidlLibrary) - actualInfo, _ := android.SingletonModuleProvider(ctx, foo, AidlLibraryProvider) + actualInfo, _ := android.OtherModuleProvider(ctx, foo, AidlLibraryProvider) android.AssertArrayString( t, diff --git a/android/Android.bp b/android/Android.bp index 9746840c1..2adedfea7 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -39,7 +39,10 @@ bootstrap_go_package { "arch_module_context.go", "base_module_context.go", "build_prop.go", + "compliance_metadata.go", "config.go", + "container_violations.go", + "container.go", "test_config.go", "configurable_properties.go", "configured_jars.go", @@ -56,6 +59,7 @@ bootstrap_go_package { "gen_notice.go", "hooks.go", "image.go", + "init.go", "license.go", "license_kind.go", "license_metadata.go", @@ -84,12 +88,14 @@ bootstrap_go_package { "prebuilt.go", "prebuilt_build_tool.go", "product_config.go", + "product_config_to_bp.go", "proto.go", "provider.go", "raw_files.go", "register.go", "rule_builder.go", "sandbox.go", + "sbom.go", "sdk.go", "sdk_version.go", "shared_properties.go", @@ -103,6 +109,7 @@ bootstrap_go_package { "updatable_modules.go", "util.go", "variable.go", + "vintf_fragment.go", "visibility.go", ], testSrcs: [ @@ -145,6 +152,9 @@ bootstrap_go_package { "test_suites_test.go", "util_test.go", "variable_test.go", + "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 ee9891df1..d2a9622e3 100644 --- a/android/aconfig_providers.go +++ b/android/aconfig_providers.go @@ -43,6 +43,10 @@ type AconfigDeclarationsProviderData struct { var AconfigDeclarationsProviderKey = blueprint.NewProvider[AconfigDeclarationsProviderData]() +type AconfigReleaseDeclarationsProviderData map[string]AconfigDeclarationsProviderData + +var AconfigReleaseDeclarationsProviderKey = blueprint.NewProvider[AconfigReleaseDeclarationsProviderData]() + type ModeInfo struct { Container string Mode string @@ -112,6 +116,8 @@ func aconfigUpdateAndroidBuildActions(ctx ModuleContext) { if dep, ok := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); ok { mergedAconfigFiles[dep.Container] = append(mergedAconfigFiles[dep.Container], dep.IntermediateCacheOutputPath) } + // If we were generating on-device artifacts for other release configs, we would need to add code here to propagate + // those artifacts as well. See also b/298444886. if dep, ok := OtherModuleProvider(ctx, module, AconfigPropagatingProviderKey); ok { for container, v := range dep.AconfigFiles { mergedAconfigFiles[container] = append(mergedAconfigFiles[container], v...) @@ -130,12 +136,12 @@ func aconfigUpdateAndroidBuildActions(ctx ModuleContext) { AconfigFiles: mergedAconfigFiles, ModeInfos: mergedModeInfos, }) - ctx.Module().base().aconfigFilePaths = getAconfigFilePaths(ctx.Module().base(), mergedAconfigFiles) + ctx.setAconfigPaths(getAconfigFilePaths(ctx.Module().base(), mergedAconfigFiles)) } } func aconfigUpdateAndroidMkData(ctx fillInEntriesContext, mod Module, data *AndroidMkData) { - info, ok := SingletonModuleProvider(ctx, mod, AconfigPropagatingProviderKey) + info, ok := OtherModuleProvider(ctx, mod, AconfigPropagatingProviderKey) // If there is no aconfigPropagatingProvider, or there are no AconfigFiles, then we are done. if !ok || len(info.AconfigFiles) == 0 { return @@ -166,7 +172,7 @@ func aconfigUpdateAndroidMkEntries(ctx fillInEntriesContext, mod Module, entries if len(*entries) == 0 { return } - info, ok := SingletonModuleProvider(ctx, mod, AconfigPropagatingProviderKey) + info, ok := OtherModuleProvider(ctx, mod, AconfigPropagatingProviderKey) if !ok || len(info.AconfigFiles) == 0 { return } @@ -181,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 { @@ -213,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/all_teams.go b/android/all_teams.go index d4bf7d0c4..01be396d5 100644 --- a/android/all_teams.go +++ b/android/all_teams.go @@ -1,9 +1,11 @@ package android import ( - "android/soong/android/team_proto" + "path" "path/filepath" + "android/soong/android/team_proto" + "google.golang.org/protobuf/proto" ) @@ -93,7 +95,7 @@ func (t *allTeamsSingleton) GenerateBuildActions(ctx SingletonContext) { } testModInfo := TestModuleInformation{} - if tmi, ok := SingletonModuleProvider(ctx, module, TestOnlyProviderKey); ok { + if tmi, ok := OtherModuleProvider(ctx, module, TestOnlyProviderKey); ok { testModInfo = tmi } @@ -152,6 +154,11 @@ func (t *allTeamsSingleton) lookupTeamForAllModules() *team_proto.AllTeams { } else { teamProperties, found = t.lookupDefaultTeam(m.bpFile) } + // Deal with one blueprint file including another by looking up the default + // in the main Android.bp rather than one listed with "build = [My.bp]" + if !found { + teamProperties, found = t.lookupDefaultTeam(path.Join(path.Dir(m.bpFile), "Android.bp")) + } trendy_team_id := "" if found { diff --git a/android/all_teams_test.go b/android/all_teams_test.go index 96ed92fc5..fa8c048d0 100644 --- a/android/all_teams_test.go +++ b/android/all_teams_test.go @@ -264,6 +264,84 @@ func TestPackageLookup(t *testing.T) { AssertDeepEquals(t, "compare maps", expectedTeams, actualTeams) } +func TestPackageLookupForIncludedBlueprintFiles(t *testing.T) { + t.Parallel() + rootBp := ` + package { default_team: "team_top"} + team { + name: "team_top", + trendy_team_id: "trendy://team_top", + } + build = ["include.bp"] + ` + includeBp := ` + fake { + name: "IncludedModule", + } ` + + ctx := GroupFixturePreparers( + prepareForTestWithTeamAndFakes, + PrepareForTestWithPackageModule, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterParallelSingletonType("all_teams", AllTeamsFactory) + }), + FixtureAddTextFile("Android.bp", rootBp), + FixtureAddTextFile("include.bp", includeBp), + ).RunTest(t) + + var teams *team_proto.AllTeams + teams = getTeamProtoOutput(t, ctx) + + // map of module name -> trendy team name. + actualTeams := make(map[string]*string) + for _, teamProto := range teams.Teams { + actualTeams[teamProto.GetTargetName()] = teamProto.TrendyTeamId + } + expectedTeams := map[string]*string{ + "IncludedModule": proto.String("trendy://team_top"), + } + AssertDeepEquals(t, "compare maps", expectedTeams, actualTeams) +} + +func TestPackageLookupForIncludedBlueprintFilesWithPackageInChildBlueprint(t *testing.T) { + t.Parallel() + rootBp := ` + team { + name: "team_top", + trendy_team_id: "trendy://team_top", + } + build = ["include.bp"] + ` + includeBp := ` + package { default_team: "team_top"} + fake { + name: "IncludedModule", + } ` + + ctx := GroupFixturePreparers( + prepareForTestWithTeamAndFakes, + PrepareForTestWithPackageModule, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterParallelSingletonType("all_teams", AllTeamsFactory) + }), + FixtureAddTextFile("Android.bp", rootBp), + FixtureAddTextFile("include.bp", includeBp), + ).RunTest(t) + + var teams *team_proto.AllTeams + teams = getTeamProtoOutput(t, ctx) + + // map of module name -> trendy team name. + actualTeams := make(map[string]*string) + for _, teamProto := range teams.Teams { + actualTeams[teamProto.GetTargetName()] = teamProto.TrendyTeamId + } + expectedTeams := map[string]*string{ + "IncludedModule": proto.String("trendy://team_top"), + } + AssertDeepEquals(t, "compare maps", expectedTeams, actualTeams) +} + type fakeForTests struct { ModuleBase diff --git a/android/androidmk.go b/android/androidmk.go index 9699ce5b8..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" ) @@ -157,6 +156,7 @@ type AndroidMkEntries struct { } type AndroidMkEntriesContext interface { + OtherModuleProviderContext Config() Config } @@ -170,7 +170,7 @@ type androidMkExtraEntriesContext struct { } func (a *androidMkExtraEntriesContext) Provider(provider blueprint.AnyProviderKey) (any, bool) { - return a.ctx.moduleProvider(a.mod, provider) + return a.ctx.otherModuleProvider(a.mod, provider) } type AndroidMkExtraEntriesFunc func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) @@ -354,14 +354,15 @@ func (a *AndroidMkEntries) getDistContributions(mod blueprint.Module) *distContr availableTaggedDists = availableTaggedDists.addPathsForTag(DefaultDistTag, a.OutputFile.Path()) } + info := OtherModuleProviderOrDefault(a.entryContext, mod, InstallFilesProvider) // If the distFiles created by GenerateTaggedDistFiles contains paths for the // DefaultDistTag then that takes priority so delete any existing paths. - if _, ok := amod.distFiles[DefaultDistTag]; ok { + if _, ok := info.DistFiles[DefaultDistTag]; ok { delete(availableTaggedDists, DefaultDistTag) } // Finally, merge the distFiles created by GenerateTaggedDistFiles. - availableTaggedDists = availableTaggedDists.merge(amod.distFiles) + availableTaggedDists = availableTaggedDists.merge(info.DistFiles) if len(availableTaggedDists) == 0 { // Nothing dist-able for this module. @@ -372,7 +373,7 @@ func (a *AndroidMkEntries) getDistContributions(mod blueprint.Module) *distContr distContributions := &distContributions{} if !exemptFromRequiredApplicableLicensesProperty(mod.(Module)) { - distContributions.licenseMetadataFile = amod.licenseMetadataFile + distContributions.licenseMetadataFile = info.LicenseMetadataFile } // Iterate over this module's dist structs, merged from the dist and dists properties. @@ -497,9 +498,10 @@ type fillInEntriesContext interface { ModuleDir(module blueprint.Module) string ModuleSubDir(module blueprint.Module) string Config() Config - moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) + 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) { @@ -516,6 +518,7 @@ func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint 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()...) @@ -536,13 +539,14 @@ func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint a.AddStrings("LOCAL_SOONG_MODULE_TYPE", ctx.ModuleType(amod)) // If the install rule was generated by Soong tell Make about it. - if len(base.katiInstalls) > 0 { + 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. - a.SetPath("LOCAL_SOONG_INSTALLED_MODULE", base.katiInstalls[len(base.katiInstalls)-1].to) - a.SetString("LOCAL_SOONG_INSTALL_PAIRS", base.katiInstalls.BuiltInstalled()) - a.SetPaths("LOCAL_SOONG_INSTALL_SYMLINKS", base.katiSymlinks.InstallPaths().Paths()) + a.SetPath("LOCAL_SOONG_INSTALLED_MODULE", info.KatiInstalls[len(info.KatiInstalls)-1].to) + a.SetString("LOCAL_SOONG_INSTALL_PAIRS", info.KatiInstalls.BuiltInstalled()) + a.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 @@ -550,8 +554,16 @@ func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint a.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", proptools.Bool(base.commonProperties.No_full_install)) } - if len(base.testData) > 0 { - a.AddStrings("LOCAL_TEST_DATA", androidMkDataPaths(base.testData)...) + if info.UncheckedModule { + a.SetBool("LOCAL_DONT_CHECK_MODULE", true) + } else if info.CheckbuildTarget != nil { + a.SetPath("LOCAL_CHECKED_MODULE", info.CheckbuildTarget) + } else { + a.SetOptionalPath("LOCAL_CHECKED_MODULE", a.OutputFile) + } + + if len(info.TestData) > 0 { + a.AddStrings("LOCAL_TEST_DATA", androidMkDataPaths(info.TestData)...) } if am, ok := mod.(ApexModule); ok { @@ -588,10 +600,10 @@ func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint } if !base.InVendorRamdisk() { - a.AddPaths("LOCAL_FULL_INIT_RC", base.initRcPaths) + a.AddPaths("LOCAL_FULL_INIT_RC", info.InitRcPaths) } - if len(base.vintfFragmentsPaths) > 0 { - a.AddPaths("LOCAL_FULL_VINTF_FRAGMENTS", base.vintfFragmentsPaths) + if len(info.VintfFragmentsPaths) > 0 { + a.AddPaths("LOCAL_FULL_VINTF_FRAGMENTS", info.VintfFragmentsPaths) } a.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(base.commonProperties.Proprietary)) if Bool(base.commonProperties.Vendor) || Bool(base.commonProperties.Soc_specific) { @@ -633,11 +645,11 @@ func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint } } - if licenseMetadata, ok := SingletonModuleProvider(ctx, mod, LicenseMetadataProvider); ok { + if licenseMetadata, ok := OtherModuleProvider(ctx, mod, LicenseMetadataProvider); ok { a.SetPath("LOCAL_SOONG_LICENSE_METADATA", licenseMetadata.LicenseMetadataPath) } - if _, ok := SingletonModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok { + if _, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok { a.SetBool("LOCAL_SOONG_MODULE_INFO_JSON", true) } @@ -794,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 { @@ -812,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{ @@ -861,6 +860,7 @@ func translateAndroidModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs * } data := provider.AndroidMk() + if data.Include == "" { data.Include = "$(BUILD_PREBUILT)" } @@ -900,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 @@ -907,6 +908,7 @@ func translateAndroidModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs * case "*phony.PhonyRule": // writes phony deps and acts like `.PHONY` case "*selinux.selinuxContextsModule": // license properties written case "*sysprop.syspropLibrary": // license properties written + case "*vintf.vintfCompatibilityMatrixRule": // use case like phony default: if !ctx.Config().IsEnvFalse("ANDROID_REQUIRE_LICENSES") { return fmt.Errorf("custom make rules not allowed for %q (%q) module %q", ctx.ModuleType(mod), reflect.TypeOf(mod), ctx.ModuleName(mod)) @@ -918,7 +920,7 @@ func translateAndroidModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs * } if !data.Entries.disabled() { - if moduleInfoJSON, ok := SingletonModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok { + if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok { *moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON) } } @@ -960,7 +962,7 @@ func translateAndroidMkEntriesModule(ctx SingletonContext, w io.Writer, moduleIn } if len(entriesList) > 0 && !entriesList[0].disabled() { - if moduleInfoJSON, ok := SingletonModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok { + if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok { *moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON) } } @@ -968,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? @@ -1048,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-.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/androidmk_test.go b/android/androidmk_test.go index ae2187f48..c37eeabff 100644 --- a/android/androidmk_test.go +++ b/android/androidmk_test.go @@ -36,10 +36,6 @@ type customModule struct { data AndroidMkData distFiles TaggedDistFiles outputFile OptionalPath - - // The paths that will be used as the default dist paths if no tag is - // specified. - defaultDistPaths Paths } const ( @@ -50,7 +46,7 @@ const ( func (m *customModule) GenerateAndroidBuildActions(ctx ModuleContext) { - m.base().licenseMetadataFile = PathForOutput(ctx, "meta_lic") + var defaultDistPaths Paths // If the dist_output_file: true then create an output file that is stored in // the OutputFile property of the AndroidMkEntry. @@ -62,7 +58,7 @@ func (m *customModule) GenerateAndroidBuildActions(ctx ModuleContext) { // property in AndroidMkEntry when determining the default dist paths. // Setting this first allows it to be overridden based on the // default_dist_files setting replicating that previous behavior. - m.defaultDistPaths = Paths{path} + defaultDistPaths = Paths{path} } // Based on the setting of the default_dist_files property possibly create a @@ -71,29 +67,40 @@ func (m *customModule) GenerateAndroidBuildActions(ctx ModuleContext) { defaultDistFiles := proptools.StringDefault(m.properties.Default_dist_files, defaultDistFiles_Tagged) switch defaultDistFiles { case defaultDistFiles_None: - // Do nothing + m.setOutputFiles(ctx, defaultDistPaths) case defaultDistFiles_Default: path := PathForTesting("default-dist.out") - m.defaultDistPaths = Paths{path} + defaultDistPaths = Paths{path} + m.setOutputFiles(ctx, defaultDistPaths) m.distFiles = MakeDefaultDistFiles(path) case defaultDistFiles_Tagged: // Module types that set AndroidMkEntry.DistFiles to the result of calling // GenerateTaggedDistFiles(ctx) relied on no tag being treated as "" which - // meant that the default dist paths would be whatever was returned by - // OutputFiles(""). In order to preserve that behavior when treating no tag - // as being equal to DefaultDistTag this ensures that - // OutputFiles(DefaultDistTag) will return the same as OutputFiles(""). - m.defaultDistPaths = PathsForTesting("one.out") + // meant that the default dist paths would be the same as empty-string-tag + // output files. In order to preserve that behavior when treating no tag + // as being equal to DefaultDistTag this ensures that DefaultDistTag output + // will be the same as empty-string-tag output. + defaultDistPaths = PathsForTesting("one.out") + m.setOutputFiles(ctx, defaultDistPaths) // This must be called after setting defaultDistPaths/outputFile as - // GenerateTaggedDistFiles calls into OutputFiles(tag) which may use those - // fields. + // GenerateTaggedDistFiles calls into outputFiles property which may use + // those fields. m.distFiles = m.GenerateTaggedDistFiles(ctx) } } +func (m *customModule) setOutputFiles(ctx ModuleContext, defaultDistPaths Paths) { + ctx.SetOutputFiles(PathsForTesting("one.out"), "") + ctx.SetOutputFiles(PathsForTesting("two.out", "three/four.out"), ".multiple") + ctx.SetOutputFiles(PathsForTesting("another.out"), ".another-tag") + if defaultDistPaths != nil { + ctx.SetOutputFiles(defaultDistPaths, DefaultDistTag) + } +} + func (m *customModule) AndroidMk() AndroidMkData { return AndroidMkData{ Custom: func(w io.Writer, name, prefix, moduleDir string, data AndroidMkData) { @@ -102,25 +109,6 @@ func (m *customModule) AndroidMk() AndroidMkData { } } -func (m *customModule) OutputFiles(tag string) (Paths, error) { - switch tag { - case DefaultDistTag: - if m.defaultDistPaths != nil { - return m.defaultDistPaths, nil - } else { - return nil, fmt.Errorf("default dist tag is not available") - } - case "": - return PathsForTesting("one.out"), nil - case ".multiple": - return PathsForTesting("two.out", "three/four.out"), nil - case ".another-tag": - return PathsForTesting("another.out"), nil - default: - return nil, fmt.Errorf("unsupported module reference tag %q", tag) - } -} - func (m *customModule) AndroidMkEntries() []AndroidMkEntries { return []AndroidMkEntries{ { @@ -287,7 +275,8 @@ func TestGetDistForGoals(t *testing.T) { ) } for idx, line := range androidMkLines { - expectedLine := strings.ReplaceAll(expectedAndroidMkLines[idx], "meta_lic", module.base().licenseMetadataFile.String()) + expectedLine := strings.ReplaceAll(expectedAndroidMkLines[idx], "meta_lic", + OtherModuleProviderOrDefault(ctx, module, InstallFilesProvider).LicenseMetadataFile.String()) if line != expectedLine { t.Errorf( "Expected AndroidMk line to be '%s', got '%s'", diff --git a/android/apex.go b/android/apex.go index 2ac6ed08d..114fe2988 100644 --- a/android/apex.go +++ b/android/apex.go @@ -16,6 +16,7 @@ package android import ( "fmt" + "reflect" "slices" "sort" "strconv" @@ -84,6 +85,12 @@ type ApexInfo struct { // Returns the name of the test apexes that this module is included in. TestApexes []string + + // Returns the name of the overridden apex (com.android.foo) + BaseApexName string + + // Returns the value of `apex_available_name` + ApexAvailableName string } // AllApexInfo holds the ApexInfo of all apexes that include this module. @@ -142,6 +149,17 @@ func (i ApexInfo) InApexModule(apexModuleName string) bool { return false } +// To satisfy the comparable interface +func (i ApexInfo) Equal(other any) bool { + otherApexInfo, ok := other.(ApexInfo) + return ok && i.ApexVariationName == otherApexInfo.ApexVariationName && + i.MinSdkVersion == otherApexInfo.MinSdkVersion && + i.Updatable == otherApexInfo.Updatable && + i.UsePlatformApis == otherApexInfo.UsePlatformApis && + reflect.DeepEqual(i.InApexVariants, otherApexInfo.InApexVariants) && + reflect.DeepEqual(i.InApexModules, otherApexInfo.InApexModules) +} + // ApexTestForInfo stores the contents of APEXes for which this module is a test - although this // module is not part of the APEX - and thus has access to APEX internals. type ApexTestForInfo struct { @@ -277,7 +295,7 @@ type ApexProperties struct { // // "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX. // "//apex_available:platform" refers to non-APEX partitions like "system.img". - // "com.android.gki.*" matches any APEX module name with the prefix "com.android.gki.". + // Prefix pattern (com.foo.*) can be used to match with any APEX name with the prefix(com.foo.). // Default is ["//apex_available:platform"]. Apex_available []string @@ -470,15 +488,6 @@ func (m *ApexModuleBase) DepIsInSameApex(ctx BaseModuleContext, dep Module) bool const ( AvailableToPlatform = "//apex_available:platform" AvailableToAnyApex = "//apex_available:anyapex" - AvailableToGkiApex = "com.android.gki.*" -) - -var ( - AvailableToRecognziedWildcards = []string{ - AvailableToPlatform, - AvailableToAnyApex, - AvailableToGkiApex, - } ) // CheckAvailableForApex provides the default algorithm for checking the apex availability. When the @@ -491,11 +500,27 @@ func CheckAvailableForApex(what string, apex_available []string) bool { if len(apex_available) == 0 { return what == AvailableToPlatform } - return InList(what, apex_available) || - (what != AvailableToPlatform && InList(AvailableToAnyApex, apex_available)) || - (strings.HasPrefix(what, "com.android.gki.") && InList(AvailableToGkiApex, apex_available)) || - (what == "com.google.mainline.primary.libs") || // TODO b/248601389 - (what == "com.google.mainline.go.primary.libs") // TODO b/248601389 + + // TODO b/248601389 + if what == "com.google.mainline.primary.libs" || what == "com.google.mainline.go.primary.libs" { + return true + } + + for _, apex_name := range apex_available { + // exact match. + if apex_name == what { + return true + } + // //apex_available:anyapex matches with any apex name, but not //apex_available:platform + if apex_name == AvailableToAnyApex && what != AvailableToPlatform { + return true + } + // prefix match. + if strings.HasSuffix(apex_name, ".*") && strings.HasPrefix(what, strings.TrimSuffix(apex_name, "*")) { + return true + } + } + return false } // Implements ApexModule @@ -521,7 +546,20 @@ func (m *ApexModuleBase) SetNotAvailableForPlatform() { // This function makes sure that the apex_available property is valid func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) { for _, n := range m.ApexProperties.Apex_available { - if n == AvailableToPlatform || n == AvailableToAnyApex || n == AvailableToGkiApex { + if n == AvailableToPlatform || n == AvailableToAnyApex { + continue + } + // Prefix pattern should end with .* and has at least two components. + if strings.Contains(n, "*") { + if !strings.HasSuffix(n, ".*") { + mctx.PropertyErrorf("apex_available", "Wildcard should end with .* like com.foo.*") + } + if strings.Count(n, ".") < 2 { + mctx.PropertyErrorf("apex_available", "Wildcard requires two or more components like com.foo.*") + } + if strings.Count(n, "*") != 1 { + mctx.PropertyErrorf("apex_available", "Wildcard is not allowed in the middle.") + } continue } if !mctx.OtherModuleExists(n) && !mctx.Config().AllowMissingDependencies() { @@ -607,9 +645,15 @@ func IncomingApexTransition(ctx IncomingTransitionContext, incomingVariation str return "" } - // If this module has no apex variations the use the platform variation. if len(apexInfos) == 0 { - return "" + if ctx.IsAddingDependency() { + // If this module has no apex variations we can't do any mapping on the incoming variation, just return it + // and let the caller get a "missing variant" error. + return incomingVariation + } else { + // If this module has no apex variations the use the platform variation. + return "" + } } // Convert the list of apex infos into from the AllApexInfoProvider into the merged list @@ -671,7 +715,7 @@ func MutateApexTransition(ctx BaseModuleContext, variation string) { base.ApexProperties.InAnyApex = true base.ApexProperties.DirectlyInAnyApex = inApex == directlyInApex - if platformVariation && !ctx.Host() && !module.AvailableFor(AvailableToPlatform) { + if platformVariation && !ctx.Host() && !module.AvailableFor(AvailableToPlatform) && module.NotAvailableForPlatform() { // Do not install the module for platform, but still allow it to output // uninstallable AndroidMk entries in certain cases when they have side // effects. TODO(jiyong): move this routine to somewhere else diff --git a/android/arch.go b/android/arch.go index e0c6908c1..942727ace 100644 --- a/android/arch.go +++ b/android/arch.go @@ -19,10 +19,10 @@ import ( "fmt" "reflect" "runtime" + "slices" "strings" "github.com/google/blueprint" - "github.com/google/blueprint/bootstrap" "github.com/google/blueprint/proptools" ) @@ -396,45 +396,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) +type allOsInfo struct { + Os map[string]OsType + Variations []string +} - base := module.base() +var allOsProvider = blueprint.NewMutatorProvider[*allOsInfo]("os_propagate") - // Nothing to do for modules that are not architecture specific (e.g. a genrule). - if !base.ArchSpecific() { - return - } - - // 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 @@ -442,53 +418,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 + 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")) + } + + // Annotate this variant 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) - } + base.commonProperties.CompileOS = allOsInfo.Os[variation] + base.setOSProperties(ctx) - if createCommonOSVariant { + 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()) } } } @@ -521,7 +535,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 @@ -552,54 +566,47 @@ 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{} - // 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) +type allArchInfo struct { + Targets map[string]Target + MultiTargets []Target + Primary string + Multilib string +} +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. // Skip creating native bridge variants for non-core modules. if os == Android && !(base.IsNativeBridgeSupported() && image == CoreVariation) { + osTargets = slices.DeleteFunc(slices.Clone(osTargets), func(t Target) bool { + return bool(t.NativeBridge) + }) + } - var targets []Target - for _, t := range osTargets { - if !t.NativeBridge { - targets = append(targets, t) - } - } - - osTargets = targets + // Filter HostCross targets if disabled. + if base.HostSupported() && !base.HostCrossSupported() { + osTargets = slices.DeleteFunc(slices.Clone(osTargets), func(t Target) bool { + return t.HostCross + }) } // only the primary arch in the ramdisk / vendor_ramdisk / recovery partition @@ -611,19 +618,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 @@ -632,7 +638,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) } @@ -640,7 +646,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) } @@ -648,37 +654,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 @@ -695,7 +773,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 42334568f..9501c877c 100644 --- a/android/arch_list.go +++ b/android/arch_list.go @@ -16,7 +16,6 @@ package android var archVariants = map[ArchType][]string{ Arm: { - "armv7-a", "armv7-a-neon", "armv8-a", "armv8-2a", @@ -27,6 +26,7 @@ var archVariants = map[ArchType][]string{ "armv8-2a", "armv8-2a-dotprod", "armv9-a", + "armv9-2a", }, X86: { "amberlake", @@ -160,6 +160,9 @@ var androidArchFeatureMap = map[ArchType]map[string][]string{ "armv9-a": { "dotprod", }, + "armv9-2a": { + "dotprod", + }, }, X86: { "amberlake": { diff --git a/android/arch_test.go b/android/arch_test.go index f0a58a90b..57c901032 100644 --- a/android/arch_test.go +++ b/android/arch_test.go @@ -331,6 +331,12 @@ func TestArchMutator(t *testing.T) { host_supported: true, } + module { + name: "nohostcross", + host_supported: true, + host_cross_supported: false, + } + module { name: "baz", device_supported: false, @@ -355,13 +361,14 @@ func TestArchMutator(t *testing.T) { ` testCases := []struct { - name string - preparer FixturePreparer - fooVariants []string - barVariants []string - bazVariants []string - quxVariants []string - firstVariants []string + name string + preparer FixturePreparer + fooVariants []string + barVariants []string + noHostCrossVariants []string + bazVariants []string + quxVariants []string + firstVariants []string multiTargetVariants []string multiTargetVariantsMap map[string][]string @@ -373,6 +380,7 @@ func TestArchMutator(t *testing.T) { preparer: nil, fooVariants: []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"}, barVariants: append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"), + noHostCrossVariants: append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"), bazVariants: nil, quxVariants: append(buildOS32Variants, "android_arm_armv7-a-neon"), firstVariants: append(buildOS64Variants, "android_arm64_armv8-a"), @@ -390,6 +398,7 @@ func TestArchMutator(t *testing.T) { }), fooVariants: nil, barVariants: buildOSVariants, + noHostCrossVariants: buildOSVariants, bazVariants: nil, quxVariants: buildOS32Variants, firstVariants: buildOS64Variants, @@ -406,6 +415,7 @@ func TestArchMutator(t *testing.T) { }), fooVariants: []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"}, barVariants: []string{"linux_musl_x86_64", "linux_musl_arm64", "linux_musl_x86", "android_arm64_armv8-a", "android_arm_armv7-a-neon"}, + noHostCrossVariants: []string{"linux_musl_x86_64", "linux_musl_x86", "android_arm64_armv8-a", "android_arm_armv7-a-neon"}, bazVariants: nil, quxVariants: []string{"linux_musl_x86", "android_arm_armv7-a-neon"}, firstVariants: []string{"linux_musl_x86_64", "linux_musl_arm64", "android_arm64_armv8-a"}, @@ -441,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) } @@ -461,6 +471,10 @@ func TestArchMutator(t *testing.T) { t.Errorf("want bar variants:\n%q\ngot:\n%q\n", w, g) } + if g, w := enabledVariants(ctx, "nohostcross"), tt.noHostCrossVariants; !reflect.DeepEqual(w, g) { + t.Errorf("want nohostcross variants:\n%q\ngot:\n%q\n", w, g) + } + if g, w := enabledVariants(ctx, "baz"), tt.bazVariants; !reflect.DeepEqual(w, g) { t.Errorf("want baz variants:\n%q\ngot:\n%q\n", w, g) } diff --git a/android/base_module_context.go b/android/base_module_context.go index 550600052..bb8137720 100644 --- a/android/base_module_context.go +++ b/android/base_module_context.go @@ -220,6 +220,10 @@ type BaseModuleContext interface { // EvaluateConfiguration makes ModuleContext a valid proptools.ConfigurableEvaluator, so this context // can be used to evaluate the final value of Configurable properties. EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue + + // HasMutatorFinished returns true if the given mutator has finished running. + // It will panic if given an invalid mutator name. + HasMutatorFinished(mutatorName string) bool } type baseModuleContext struct { @@ -270,6 +274,10 @@ func (b *baseModuleContext) setProvider(provider blueprint.AnyProviderKey, value b.bp.SetProvider(provider, value) } +func (b *baseModuleContext) HasMutatorFinished(mutatorName string) bool { + return b.bp.HasMutatorFinished(mutatorName) +} + func (b *baseModuleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module { return b.bp.GetDirectDepWithTag(name, tag) } diff --git a/android/compliance_metadata.go b/android/compliance_metadata.go new file mode 100644 index 000000000..38f138237 --- /dev/null +++ b/android/compliance_metadata.go @@ -0,0 +1,337 @@ +// 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 + +import ( + "bytes" + "encoding/csv" + "encoding/gob" + "fmt" + "slices" + "strconv" + "strings" + + "github.com/google/blueprint" +) + +var ( + // Constants of property names used in compliance metadata of modules + ComplianceMetadataProp = struct { + NAME string + PACKAGE string + MODULE_TYPE string + OS string + ARCH string + IS_PRIMARY_ARCH string + VARIANT string + IS_STATIC_LIB string + INSTALLED_FILES string + BUILT_FILES string + STATIC_DEPS string + STATIC_DEP_FILES string + WHOLE_STATIC_DEPS string + WHOLE_STATIC_DEP_FILES string + LICENSES string + + // module_type=package + PKG_DEFAULT_APPLICABLE_LICENSES string + + // module_type=license + LIC_LICENSE_KINDS string + LIC_LICENSE_TEXT string + LIC_PACKAGE_NAME string + + // module_type=license_kind + LK_CONDITIONS string + LK_URL string + }{ + "name", + "package", + "module_type", + "os", + "arch", + "is_primary_arch", + "variant", + "is_static_lib", + "installed_files", + "built_files", + "static_deps", + "static_dep_files", + "whole_static_deps", + "whole_static_dep_files", + "licenses", + + "pkg_default_applicable_licenses", + + "lic_license_kinds", + "lic_license_text", + "lic_package_name", + + "lk_conditions", + "lk_url", + } + + // A constant list of all property names in compliance metadata + // Order of properties here is the order of columns in the exported CSV file. + COMPLIANCE_METADATA_PROPS = []string{ + ComplianceMetadataProp.NAME, + ComplianceMetadataProp.PACKAGE, + ComplianceMetadataProp.MODULE_TYPE, + ComplianceMetadataProp.OS, + ComplianceMetadataProp.ARCH, + ComplianceMetadataProp.VARIANT, + ComplianceMetadataProp.IS_STATIC_LIB, + ComplianceMetadataProp.IS_PRIMARY_ARCH, + // Space separated installed files + ComplianceMetadataProp.INSTALLED_FILES, + // Space separated built files + ComplianceMetadataProp.BUILT_FILES, + // Space separated module names of static dependencies + ComplianceMetadataProp.STATIC_DEPS, + // Space separated file paths of static dependencies + ComplianceMetadataProp.STATIC_DEP_FILES, + // Space separated module names of whole static dependencies + ComplianceMetadataProp.WHOLE_STATIC_DEPS, + // Space separated file paths of whole static dependencies + ComplianceMetadataProp.WHOLE_STATIC_DEP_FILES, + ComplianceMetadataProp.LICENSES, + // module_type=package + ComplianceMetadataProp.PKG_DEFAULT_APPLICABLE_LICENSES, + // module_type=license + ComplianceMetadataProp.LIC_LICENSE_KINDS, + ComplianceMetadataProp.LIC_LICENSE_TEXT, // resolve to file paths + ComplianceMetadataProp.LIC_PACKAGE_NAME, + // module_type=license_kind + ComplianceMetadataProp.LK_CONDITIONS, + ComplianceMetadataProp.LK_URL, + } +) + +// ComplianceMetadataInfo provides all metadata of a module, e.g. name, module type, package, license, +// dependencies, built/installed files, etc. It is a wrapper on a map[string]string with some utility +// methods to get/set properties' values. +type ComplianceMetadataInfo struct { + properties map[string]string +} + +func NewComplianceMetadataInfo() *ComplianceMetadataInfo { + return &ComplianceMetadataInfo{ + properties: map[string]string{}, + } +} + +func (c *ComplianceMetadataInfo) GobEncode() ([]byte, error) { + w := new(bytes.Buffer) + encoder := gob.NewEncoder(w) + err := encoder.Encode(c.properties) + if err != nil { + return nil, err + } + + return w.Bytes(), nil +} + +func (c *ComplianceMetadataInfo) GobDecode(data []byte) error { + r := bytes.NewBuffer(data) + decoder := gob.NewDecoder(r) + err := decoder.Decode(&c.properties) + if err != nil { + return err + } + + return nil +} + +func (c *ComplianceMetadataInfo) SetStringValue(propertyName string, value string) { + if !slices.Contains(COMPLIANCE_METADATA_PROPS, propertyName) { + panic(fmt.Errorf("Unknown metadata property: %s.", propertyName)) + } + c.properties[propertyName] = value +} + +func (c *ComplianceMetadataInfo) SetListValue(propertyName string, value []string) { + c.SetStringValue(propertyName, strings.TrimSpace(strings.Join(value, " "))) +} + +func (c *ComplianceMetadataInfo) getStringValue(propertyName string) string { + if !slices.Contains(COMPLIANCE_METADATA_PROPS, propertyName) { + panic(fmt.Errorf("Unknown metadata property: %s.", propertyName)) + } + return c.properties[propertyName] +} + +func (c *ComplianceMetadataInfo) getAllValues() map[string]string { + return c.properties +} + +var ( + ComplianceMetadataProvider = blueprint.NewProvider[*ComplianceMetadataInfo]() +) + +// buildComplianceMetadataProvider starts with the ModuleContext.ComplianceMetadataInfo() and fills in more common metadata +// for different module types without accessing their private fields but through android.Module interface +// and public/private fields of package android. The final metadata is stored to a module's ComplianceMetadataProvider. +func buildComplianceMetadataProvider(ctx *moduleContext, m *ModuleBase) { + complianceMetadataInfo := ctx.ComplianceMetadataInfo() + complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.NAME, m.Name()) + complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.PACKAGE, ctx.ModuleDir()) + complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.MODULE_TYPE, ctx.ModuleType()) + + switch ctx.ModuleType() { + case "license": + licenseModule := m.module.(*licenseModule) + complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LIC_LICENSE_KINDS, licenseModule.properties.License_kinds) + complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LIC_LICENSE_TEXT, PathsForModuleSrc(ctx, licenseModule.properties.License_text).Strings()) + complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.LIC_PACKAGE_NAME, String(licenseModule.properties.Package_name)) + case "license_kind": + licenseKindModule := m.module.(*licenseKindModule) + complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LK_CONDITIONS, licenseKindModule.properties.Conditions) + complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.LK_URL, licenseKindModule.properties.Url) + default: + complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.OS, ctx.Os().String()) + complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.ARCH, ctx.Arch().String()) + complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.IS_PRIMARY_ARCH, strconv.FormatBool(ctx.PrimaryArch())) + complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.VARIANT, ctx.ModuleSubDir()) + if m.primaryLicensesProperty != nil && m.primaryLicensesProperty.getName() == "licenses" { + complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LICENSES, m.primaryLicensesProperty.getStrings()) + } + + var installed InstallPaths + installed = append(installed, ctx.installFiles...) + installed = append(installed, ctx.katiInstalls.InstallPaths()...) + installed = append(installed, ctx.katiSymlinks.InstallPaths()...) + installed = append(installed, ctx.katiInitRcInstalls.InstallPaths()...) + installed = append(installed, ctx.katiVintfInstalls.InstallPaths()...) + complianceMetadataInfo.SetListValue(ComplianceMetadataProp.INSTALLED_FILES, FirstUniqueStrings(installed.Strings())) + } + ctx.setProvider(ComplianceMetadataProvider, complianceMetadataInfo) +} + +func init() { + RegisterComplianceMetadataSingleton(InitRegistrationContext) +} + +func RegisterComplianceMetadataSingleton(ctx RegistrationContext) { + ctx.RegisterParallelSingletonType("compliance_metadata_singleton", complianceMetadataSingletonFactory) +} + +var ( + // sqlite3 command line tool + sqlite3 = pctx.HostBinToolVariable("sqlite3", "sqlite3") + + // Command to import .csv files to sqlite3 database + importCsv = pctx.AndroidStaticRule("importCsv", + blueprint.RuleParams{ + Command: `rm -rf $out && ` + + `${sqlite3} $out ".import --csv $in modules" && ` + + `${sqlite3} $out ".import --csv ${make_metadata} make_metadata" && ` + + `${sqlite3} $out ".import --csv ${make_modules} make_modules"`, + CommandDeps: []string{"${sqlite3}"}, + }, "make_metadata", "make_modules") +) + +func complianceMetadataSingletonFactory() Singleton { + return &complianceMetadataSingleton{} +} + +type complianceMetadataSingleton struct { +} + +func writerToCsv(csvWriter *csv.Writer, row []string) { + err := csvWriter.Write(row) + if err != nil { + panic(err) + } +} + +// Collect compliance metadata from all Soong modules, write to a CSV file and +// import compliance metadata from Make and Soong to a sqlite3 database. +func (c *complianceMetadataSingleton) GenerateBuildActions(ctx SingletonContext) { + if !ctx.Config().HasDeviceProduct() { + return + } + var buffer bytes.Buffer + csvWriter := csv.NewWriter(&buffer) + + // Collect compliance metadata of modules in Soong and write to out/soong/compliance-metadata//soong-modules.csv file. + columnNames := []string{"id"} + columnNames = append(columnNames, COMPLIANCE_METADATA_PROPS...) + writerToCsv(csvWriter, columnNames) + + rowId := -1 + ctx.VisitAllModules(func(module Module) { + if !module.Enabled(ctx) { + return + } + moduleType := ctx.ModuleType(module) + if moduleType == "package" { + metadataMap := map[string]string{ + ComplianceMetadataProp.NAME: ctx.ModuleName(module), + ComplianceMetadataProp.MODULE_TYPE: ctx.ModuleType(module), + ComplianceMetadataProp.PKG_DEFAULT_APPLICABLE_LICENSES: strings.Join(module.base().primaryLicensesProperty.getStrings(), " "), + } + rowId = rowId + 1 + metadata := []string{strconv.Itoa(rowId)} + for _, propertyName := range COMPLIANCE_METADATA_PROPS { + metadata = append(metadata, metadataMap[propertyName]) + } + writerToCsv(csvWriter, metadata) + return + } + if provider, ok := ctx.otherModuleProvider(module, ComplianceMetadataProvider); ok { + metadataInfo := provider.(*ComplianceMetadataInfo) + rowId = rowId + 1 + metadata := []string{strconv.Itoa(rowId)} + for _, propertyName := range COMPLIANCE_METADATA_PROPS { + metadata = append(metadata, metadataInfo.getStringValue(propertyName)) + } + writerToCsv(csvWriter, metadata) + return + } + }) + csvWriter.Flush() + + deviceProduct := ctx.Config().DeviceProduct() + modulesCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "soong-modules.csv") + WriteFileRuleVerbatim(ctx, modulesCsv, buffer.String()) + + // Metadata generated in Make + makeMetadataCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-metadata.csv") + makeModulesCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-modules.csv") + + // Import metadata from Make and Soong to sqlite3 database + complianceMetadataDb := PathForOutput(ctx, "compliance-metadata", deviceProduct, "compliance-metadata.db") + ctx.Build(pctx, BuildParams{ + Rule: importCsv, + Input: modulesCsv, + Implicits: []Path{ + makeMetadataCsv, + makeModulesCsv, + }, + Output: complianceMetadataDb, + Args: map[string]string{ + "make_metadata": makeMetadataCsv.String(), + "make_modules": makeModulesCsv.String(), + }, + }) + + // Phony rule "compliance-metadata.db". "m compliance-metadata.db" to create the compliance metadata database. + ctx.Build(pctx, BuildParams{ + Rule: blueprint.Phony, + Inputs: []Path{complianceMetadataDb}, + Output: PathForPhony(ctx, "compliance-metadata.db"), + }) + +} diff --git a/android/config.go b/android/config.go index 9849c7de2..cbc6fcd72 100644 --- a/android/config.go +++ b/android/config.go @@ -173,6 +173,13 @@ func (c Config) DisableVerifyOverlaps() bool { return c.IsEnvTrue("DISABLE_VERIFY_OVERLAPS") || c.ReleaseDisableVerifyOverlaps() || !c.ReleaseDefaultModuleBuildFromSource() } +func (c Config) CoverageSuffix() string { + if v := c.IsEnvTrue("EMMA_INSTRUMENT"); v { + return "coverage." + } + return "" +} + // MaxPageSizeSupported returns the max page size supported by the device. This // value will define the ELF segment alignment for binaries (executables and // shared libraries). @@ -198,12 +205,44 @@ func (c Config) ReleaseAconfigValueSets() []string { return c.config.productVariables.ReleaseAconfigValueSets } +func (c Config) ReleaseAconfigExtraReleaseConfigs() []string { + result := []string{} + if val, ok := c.config.productVariables.BuildFlags["RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS"]; ok { + if len(val) > 0 { + // Remove any duplicates from the list. + found := make(map[string]bool) + for _, k := range strings.Split(val, " ") { + if !found[k] { + found[k] = true + result = append(result, k) + } + } + } + } + return result +} + +func (c Config) ReleaseAconfigExtraReleaseConfigsValueSets() map[string][]string { + result := make(map[string][]string) + for _, rcName := range c.ReleaseAconfigExtraReleaseConfigs() { + if value, ok := c.config.productVariables.BuildFlags["RELEASE_ACONFIG_VALUE_SETS_"+rcName]; ok { + result[rcName] = strings.Split(value, " ") + } + } + return result +} + // The flag default permission value passed to aconfig // derived from RELEASE_ACONFIG_FLAG_DEFAULT_PERMISSION 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 { @@ -321,9 +360,6 @@ type config struct { // modules that aren't mixed-built for at least one variant will cause a build // failure ensureAllowlistIntegrity bool - - // List of Api libraries that contribute to Api surfaces. - apiLibraries map[string]struct{} } type deviceConfig struct { @@ -573,40 +609,6 @@ func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) setBuildMode(cmdArgs.ModuleGraphFile, GenerateModuleGraph) setBuildMode(cmdArgs.DocFile, GenerateDocFile) - // TODO(b/276958307): Replace the hardcoded list to a sdk_library local prop. - config.apiLibraries = map[string]struct{}{ - "android.net.ipsec.ike": {}, - "art.module.public.api": {}, - "conscrypt.module.public.api": {}, - "framework-adservices": {}, - "framework-appsearch": {}, - "framework-bluetooth": {}, - "framework-configinfrastructure": {}, - "framework-connectivity": {}, - "framework-connectivity-t": {}, - "framework-devicelock": {}, - "framework-graphics": {}, - "framework-healthfitness": {}, - "framework-location": {}, - "framework-media": {}, - "framework-mediaprovider": {}, - "framework-nfc": {}, - "framework-ondevicepersonalization": {}, - "framework-pdf": {}, - "framework-pdf-v": {}, - "framework-permission": {}, - "framework-permission-s": {}, - "framework-scheduling": {}, - "framework-sdkextensions": {}, - "framework-statsd": {}, - "framework-sdksandbox": {}, - "framework-tethering": {}, - "framework-uwb": {}, - "framework-virtualization": {}, - "framework-wifi": {}, - "i18n.module.public.api": {}, - } - config.productVariables.Build_from_text_stub = boolPtr(config.BuildFromTextStub()) return Config{config}, err @@ -1033,6 +1035,22 @@ func (c *config) DefaultAppCertificate(ctx PathContext) (pem, key SourcePath) { return defaultDir.Join(ctx, "testkey.x509.pem"), defaultDir.Join(ctx, "testkey.pk8") } +func (c *config) ExtraOtaKeys(ctx PathContext, recovery bool) []SourcePath { + var otaKeys []string + if recovery { + otaKeys = c.productVariables.ExtraOtaRecoveryKeys + } else { + otaKeys = c.productVariables.ExtraOtaKeys + } + + otaPaths := make([]SourcePath, len(otaKeys)) + for i, key := range otaKeys { + otaPaths[i] = PathForSource(ctx, key+".x509.pem") + } + + return otaPaths +} + func (c *config) BuildKeys() string { defaultCert := String(c.productVariables.DefaultAppCertificate) if defaultCert == "" || defaultCert == filepath.Join(testKeyDir, "testkey") { @@ -1157,6 +1175,10 @@ func (c *config) UseGoma() bool { return Bool(c.productVariables.UseGoma) } +func (c *config) UseABFS() bool { + return Bool(c.productVariables.UseABFS) +} + func (c *config) UseRBE() bool { return Bool(c.productVariables.UseRBE) } @@ -1318,10 +1340,6 @@ func (c *config) FrameworksBaseDirExists(ctx PathGlobContext) bool { return ExistentPathForSource(ctx, "frameworks", "base", "Android.bp").Valid() } -func (c *config) VndkSnapshotBuildArtifacts() bool { - return Bool(c.productVariables.VndkSnapshotBuildArtifacts) -} - func (c *config) HasMultilibConflict(arch ArchType) bool { return c.multilibConflicts[arch] } @@ -1385,10 +1403,6 @@ func (c *deviceConfig) VendorPath() string { return "vendor" } -func (c *deviceConfig) RecoverySnapshotVersion() string { - return String(c.config.productVariables.RecoverySnapshotVersion) -} - func (c *deviceConfig) CurrentApiLevelForVendorModules() string { return StringDefault(c.config.productVariables.DeviceCurrentApiLevelForVendorModules, "current") } @@ -1490,11 +1504,6 @@ func (c *deviceConfig) NativeCoverageEnabledForPath(path string) bool { } } if coverage && len(c.config.productVariables.NativeCoverageExcludePaths) > 0 { - // Workaround coverage boot failure. - // http://b/269981180 - if strings.HasPrefix(path, "external/protobuf") { - coverage = false - } if HasAnyPrefix(path, c.config.productVariables.NativeCoverageExcludePaths) { coverage = false } @@ -1663,6 +1672,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) } @@ -1770,22 +1790,6 @@ func (c *deviceConfig) IsPartnerTrebleSepolicyTestEnabled() bool { return c.SystemExtSepolicyPrebuiltApiDir() != "" || c.ProductSepolicyPrebuiltApiDir() != "" } -func (c *deviceConfig) DirectedVendorSnapshot() bool { - return c.config.productVariables.DirectedVendorSnapshot -} - -func (c *deviceConfig) VendorSnapshotModules() map[string]bool { - return c.config.productVariables.VendorSnapshotModules -} - -func (c *deviceConfig) DirectedRecoverySnapshot() bool { - return c.config.productVariables.DirectedRecoverySnapshot -} - -func (c *deviceConfig) RecoverySnapshotModules() map[string]bool { - return c.config.productVariables.RecoverySnapshotModules -} - func createDirsMap(previous map[string]bool, dirs []string) (map[string]bool, error) { var ret = make(map[string]bool) for _, dir := range dirs { @@ -1812,40 +1816,6 @@ func (c *deviceConfig) createDirsMapOnce(onceKey OnceKey, previous map[string]bo return dirMap.(map[string]bool) } -var vendorSnapshotDirsExcludedKey = NewOnceKey("VendorSnapshotDirsExcludedMap") - -func (c *deviceConfig) VendorSnapshotDirsExcludedMap() map[string]bool { - return c.createDirsMapOnce(vendorSnapshotDirsExcludedKey, nil, - c.config.productVariables.VendorSnapshotDirsExcluded) -} - -var vendorSnapshotDirsIncludedKey = NewOnceKey("VendorSnapshotDirsIncludedMap") - -func (c *deviceConfig) VendorSnapshotDirsIncludedMap() map[string]bool { - excludedMap := c.VendorSnapshotDirsExcludedMap() - return c.createDirsMapOnce(vendorSnapshotDirsIncludedKey, excludedMap, - c.config.productVariables.VendorSnapshotDirsIncluded) -} - -var recoverySnapshotDirsExcludedKey = NewOnceKey("RecoverySnapshotDirsExcludedMap") - -func (c *deviceConfig) RecoverySnapshotDirsExcludedMap() map[string]bool { - return c.createDirsMapOnce(recoverySnapshotDirsExcludedKey, nil, - c.config.productVariables.RecoverySnapshotDirsExcluded) -} - -var recoverySnapshotDirsIncludedKey = NewOnceKey("RecoverySnapshotDirsIncludedMap") - -func (c *deviceConfig) RecoverySnapshotDirsIncludedMap() map[string]bool { - excludedMap := c.RecoverySnapshotDirsExcludedMap() - return c.createDirsMapOnce(recoverySnapshotDirsIncludedKey, excludedMap, - c.config.productVariables.RecoverySnapshotDirsIncluded) -} - -func (c *deviceConfig) HostFakeSnapshotEnabled() bool { - return c.config.productVariables.HostFakeSnapshotEnabled -} - func (c *deviceConfig) ShippingApiLevel() ApiLevel { if c.config.productVariables.Shipping_api_level == nil { return NoneApiLevel @@ -1878,10 +1848,6 @@ func (c *deviceConfig) BuildBrokenTrebleSyspropNeverallow() bool { return c.config.productVariables.BuildBrokenTrebleSyspropNeverallow } -func (c *deviceConfig) BuildBrokenUsesSoongPython2Modules() bool { - return c.config.productVariables.BuildBrokenUsesSoongPython2Modules -} - func (c *deviceConfig) BuildDebugfsRestrictionsEnabled() bool { return c.config.productVariables.BuildDebugfsRestrictionsEnabled } @@ -1999,17 +1965,6 @@ func (c *config) SetBuildFromTextStub(b bool) { c.productVariables.Build_from_text_stub = boolPtr(b) } -func (c *config) SetApiLibraries(libs []string) { - c.apiLibraries = make(map[string]struct{}) - for _, lib := range libs { - c.apiLibraries[lib] = struct{}{} - } -} - -func (c *config) GetApiLibraries() map[string]struct{} { - return c.apiLibraries -} - func (c *deviceConfig) CheckVendorSeappViolations() bool { return Bool(c.config.productVariables.CheckVendorSeappViolations) } @@ -2019,10 +1974,18 @@ 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") } +func (c *config) UseTransitiveJarsInClasspath() bool { + return c.productVariables.GetBuildFlagBool("RELEASE_USE_TRANSITIVE_JARS_IN_CLASSPATH") +} + var ( mainlineApexContributionBuildFlagsToApexNames = map[string]string{ "RELEASE_APEX_CONTRIBUTIONS_ADBD": "com.android.adbd", @@ -2125,3 +2088,19 @@ func (c *config) OdmPropFiles(ctx PathContext) Paths { func (c *config) EnableUffdGc() string { return String(c.productVariables.EnableUffdGc) } + +func (c *config) DeviceFrameworkCompatibilityMatrixFile() []string { + return c.productVariables.DeviceFrameworkCompatibilityMatrixFile +} + +func (c *config) DeviceProductCompatibilityMatrixFile() []string { + return c.productVariables.DeviceProductCompatibilityMatrixFile +} + +func (c *config) BoardAvbEnable() bool { + return Bool(c.productVariables.BoardAvbEnable) +} + +func (c *config) BoardAvbSystemAddHashtreeFooterArgs() []string { + return c.productVariables.BoardAvbSystemAddHashtreeFooterArgs +} diff --git a/android/config_test.go b/android/config_test.go index 7d327a27e..773216844 100644 --- a/android/config_test.go +++ b/android/config_test.go @@ -125,6 +125,38 @@ func assertStringEquals(t *testing.T, expected, actual string) { } } +func TestReleaseAconfigExtraReleaseConfigs(t *testing.T) { + testCases := []struct { + name string + flag string + expected []string + }{ + { + name: "empty", + flag: "", + expected: []string{}, + }, + { + name: "specified", + flag: "bar foo", + expected: []string{"bar", "foo"}, + }, + { + name: "duplicates", + flag: "foo bar foo", + expected: []string{"foo", "bar"}, + }, + } + + for _, tc := range testCases { + fixture := GroupFixturePreparers( + PrepareForTestWithBuildFlag("RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS", tc.flag), + ) + actual := fixture.RunTest(t).Config.ReleaseAconfigExtraReleaseConfigs() + AssertArrayString(t, tc.name, tc.expected, actual) + } +} + func TestConfiguredJarList(t *testing.T) { list1 := CreateTestConfiguredJarList([]string{"apex1:jarA"}) diff --git a/android/configurable_properties.go b/android/configurable_properties.go index dad42fa1d..2c794a186 100644 --- a/android/configurable_properties.go +++ b/android/configurable_properties.go @@ -26,3 +26,9 @@ func CreateSelectOsToBool(cases map[string]*bool) proptools.Configurable[bool] { resultCases, ) } + +func NewSimpleConfigurable[T proptools.ConfigurableElements](value T) proptools.Configurable[T] { + return proptools.NewConfigurable(nil, []proptools.ConfigurableCase[T]{ + proptools.NewConfigurableCase(nil, &value), + }) +} diff --git a/android/container.go b/android/container.go new file mode 100644 index 000000000..c048d6c73 --- /dev/null +++ b/android/container.go @@ -0,0 +1,506 @@ +// 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 + +import ( + "fmt" + "reflect" + "slices" + "strings" + + "github.com/google/blueprint" +) + +// ---------------------------------------------------------------------------- +// Start of the definitions of exception functions and the lookup table. +// +// Functions cannot be used as a value passed in providers, because functions are not +// hashable. As a workaround, the [exceptionHandleFuncLabel] enum values are passed using providers, +// and the corresponding functions are called from [exceptionHandleFunctionsTable] map. +// ---------------------------------------------------------------------------- + +type exceptionHandleFunc func(ModuleContext, Module, Module) bool + +type StubsAvailableModule interface { + IsStubsModule() bool +} + +// Returns true if the dependency module is a stubs module +var depIsStubsModule exceptionHandleFunc = func(_ ModuleContext, _, dep Module) bool { + if stubsModule, ok := dep.(StubsAvailableModule); ok { + return stubsModule.IsStubsModule() + } + return false +} + +// Returns true if the dependency module belongs to any of the apexes. +var depIsApexModule exceptionHandleFunc = func(mctx ModuleContext, _, dep Module) bool { + depContainersInfo, _ := getContainerModuleInfo(mctx, dep) + return InList(ApexContainer, depContainersInfo.belongingContainers) +} + +// Returns true if the module and the dependent module belongs to common apexes. +var belongsToCommonApexes exceptionHandleFunc = func(mctx ModuleContext, m, dep Module) bool { + mContainersInfo, _ := getContainerModuleInfo(mctx, m) + depContainersInfo, _ := getContainerModuleInfo(mctx, dep) + + return HasIntersection(mContainersInfo.ApexNames(), depContainersInfo.ApexNames()) +} + +// Returns true when all apexes that the module belongs to are non updatable. +// For an apex module to be allowed to depend on a non-apex partition module, +// all apexes that the module belong to must be non updatable. +var belongsToNonUpdatableApex exceptionHandleFunc = func(mctx ModuleContext, m, _ Module) bool { + mContainersInfo, _ := getContainerModuleInfo(mctx, m) + + return !mContainersInfo.UpdatableApex() +} + +// Returns true if the dependency is added via dependency tags that are not used to tag dynamic +// dependency tags. +var depIsNotDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m, dep Module) bool { + mInstallable, _ := m.(InstallableModule) + depTag := ctx.OtherModuleDependencyTag(dep) + return !InList(depTag, mInstallable.DynamicDependencyTags()) +} + +// Returns true if the dependency is added via dependency tags that are not used to tag static +// or dynamic dependency tags. These dependencies do not affect the module in compile time or in +// runtime, thus are not significant enough to raise an error. +var depIsNotStaticOrDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m, dep Module) bool { + mInstallable, _ := m.(InstallableModule) + depTag := ctx.OtherModuleDependencyTag(dep) + return !InList(depTag, append(mInstallable.StaticDependencyTags(), mInstallable.DynamicDependencyTags()...)) +} + +var globallyAllowlistedDependencies = []string{ + // Modules that provide annotations used within the platform and apexes. + "aconfig-annotations-lib", + "framework-annotations-lib", + "unsupportedappusage", + + // TODO(b/363016634): Remove from the allowlist when the module is converted + // to java_sdk_library and the java_aconfig_library modules depend on the stub. + "aconfig_storage_reader_java", + + // framework-res provides core resources essential for building apps and system UI. + // This module is implicitly added as a dependency for java modules even when the + // dependency specifies sdk_version. + "framework-res", + + // jacocoagent is implicitly added as a dependency in coverage builds, and is not installed + // on the device. + "jacocoagent", +} + +// Returns true when the dependency is globally allowlisted for inter-container dependency +var depIsGloballyAllowlisted exceptionHandleFunc = func(_ ModuleContext, _, dep Module) bool { + return InList(dep.Name(), globallyAllowlistedDependencies) +} + +// Labels of exception functions, which are used to determine special dependencies that allow +// otherwise restricted inter-container dependencies +type exceptionHandleFuncLabel int + +const ( + checkStubs exceptionHandleFuncLabel = iota + checkApexModule + checkInCommonApexes + checkApexIsNonUpdatable + checkNotDynamicDepTag + checkNotStaticOrDynamicDepTag + checkGlobalAllowlistedDep +) + +// Map of [exceptionHandleFuncLabel] to the [exceptionHandleFunc] +var exceptionHandleFunctionsTable = map[exceptionHandleFuncLabel]exceptionHandleFunc{ + checkStubs: depIsStubsModule, + checkApexModule: depIsApexModule, + checkInCommonApexes: belongsToCommonApexes, + checkApexIsNonUpdatable: belongsToNonUpdatableApex, + checkNotDynamicDepTag: depIsNotDynamicDepTag, + checkNotStaticOrDynamicDepTag: depIsNotStaticOrDynamicDepTag, + checkGlobalAllowlistedDep: depIsGloballyAllowlisted, +} + +// ---------------------------------------------------------------------------- +// Start of the definitions of container determination functions. +// +// Similar to the above section, below defines the functions used to determine +// the container of each modules. +// ---------------------------------------------------------------------------- + +type containerBoundaryFunc func(mctx ModuleContext) bool + +var vendorContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool { + m, ok := mctx.Module().(ImageInterface) + return mctx.Module().InstallInVendor() || (ok && m.VendorVariantNeeded(mctx)) +} + +var systemContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool { + module := mctx.Module() + + return !module.InstallInTestcases() && + !module.InstallInData() && + !module.InstallInRamdisk() && + !module.InstallInVendorRamdisk() && + !module.InstallInDebugRamdisk() && + !module.InstallInRecovery() && + !module.InstallInVendor() && + !module.InstallInOdm() && + !module.InstallInProduct() && + determineModuleKind(module.base(), mctx.blueprintBaseModuleContext()) == platformModule +} + +var productContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool { + m, ok := mctx.Module().(ImageInterface) + return mctx.Module().InstallInProduct() || (ok && m.ProductVariantNeeded(mctx)) +} + +var apexContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool { + _, ok := ModuleProvider(mctx, AllApexInfoProvider) + return ok +} + +var ctsContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool { + props := mctx.Module().GetProperties() + for _, prop := range props { + val := reflect.ValueOf(prop).Elem() + if val.Kind() == reflect.Struct { + testSuites := val.FieldByName("Test_suites") + if testSuites.IsValid() && testSuites.Kind() == reflect.Slice && slices.Contains(testSuites.Interface().([]string), "cts") { + return true + } + } + } + return false +} + +type unstableInfo struct { + // Determines if the module contains the private APIs of the platform. + ContainsPlatformPrivateApis bool +} + +var unstableInfoProvider = blueprint.NewProvider[unstableInfo]() + +func determineUnstableModule(mctx ModuleContext) bool { + module := mctx.Module() + unstableModule := module.Name() == "framework-minus-apex" + if installable, ok := module.(InstallableModule); ok { + for _, staticDepTag := range installable.StaticDependencyTags() { + mctx.VisitDirectDepsWithTag(staticDepTag, func(dep Module) { + if unstableInfo, ok := OtherModuleProvider(mctx, dep, unstableInfoProvider); ok { + unstableModule = unstableModule || unstableInfo.ContainsPlatformPrivateApis + } + }) + } + } + return unstableModule +} + +var unstableContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool { + return determineUnstableModule(mctx) +} + +// Map of [*container] to the [containerBoundaryFunc] +var containerBoundaryFunctionsTable = map[*container]containerBoundaryFunc{ + VendorContainer: vendorContainerBoundaryFunc, + SystemContainer: systemContainerBoundaryFunc, + ProductContainer: productContainerBoundaryFunc, + ApexContainer: apexContainerBoundaryFunc, + CtsContainer: ctsContainerBoundaryFunc, + UnstableContainer: unstableContainerBoundaryFunc, +} + +// ---------------------------------------------------------------------------- +// End of the definitions of container determination functions. +// ---------------------------------------------------------------------------- + +type InstallableModule interface { + StaticDependencyTags() []blueprint.DependencyTag + DynamicDependencyTags() []blueprint.DependencyTag +} + +type restriction struct { + // container of the dependency + dependency *container + + // Error message to be emitted to the user when the dependency meets this restriction + errorMessage string + + // List of labels of allowed exception functions that allows bypassing this restriction. + // If any of the functions mapped to each labels returns true, this dependency would be + // considered allowed and an error will not be thrown. + allowedExceptions []exceptionHandleFuncLabel +} +type container struct { + // The name of the container i.e. partition, api domain + name string + + // Map of dependency restricted containers. + restricted []restriction +} + +var ( + VendorContainer = &container{ + name: VendorVariation, + restricted: nil, + } + + SystemContainer = &container{ + name: "system", + restricted: []restriction{ + { + dependency: VendorContainer, + errorMessage: "Module belonging to the system partition other than HALs is " + + "not allowed to depend on the vendor partition module, in order to support " + + "independent development/update cycles and to support the Generic System " + + "Image. Try depending on HALs, VNDK or AIDL instead.", + allowedExceptions: []exceptionHandleFuncLabel{ + checkStubs, + checkNotDynamicDepTag, + checkGlobalAllowlistedDep, + }, + }, + }, + } + + ProductContainer = &container{ + name: ProductVariation, + restricted: []restriction{ + { + dependency: VendorContainer, + errorMessage: "Module belonging to the product partition is not allowed to " + + "depend on the vendor partition module, as this may lead to security " + + "vulnerabilities. Try depending on the HALs or utilize AIDL instead.", + allowedExceptions: []exceptionHandleFuncLabel{ + checkStubs, + checkNotDynamicDepTag, + checkGlobalAllowlistedDep, + }, + }, + }, + } + + ApexContainer = initializeApexContainer() + + CtsContainer = &container{ + name: "cts", + restricted: []restriction{ + { + dependency: UnstableContainer, + errorMessage: "CTS module should not depend on the modules that contain the " + + "platform implementation details, including \"framework\". Depending on these " + + "modules may lead to disclosure of implementation details and regression " + + "due to API changes across platform versions. Try depending on the stubs instead " + + "and ensure that the module sets an appropriate 'sdk_version'.", + allowedExceptions: []exceptionHandleFuncLabel{ + checkStubs, + checkNotStaticOrDynamicDepTag, + checkGlobalAllowlistedDep, + }, + }, + }, + } + + // Container signifying that the module contains unstable platform private APIs + UnstableContainer = &container{ + name: "unstable", + restricted: nil, + } + + allContainers = []*container{ + VendorContainer, + SystemContainer, + ProductContainer, + ApexContainer, + CtsContainer, + UnstableContainer, + } +) + +func initializeApexContainer() *container { + apexContainer := &container{ + name: "apex", + restricted: []restriction{ + { + dependency: SystemContainer, + errorMessage: "Module belonging to Apex(es) is not allowed to depend on the " + + "modules belonging to the system partition. Either statically depend on the " + + "module or convert the depending module to java_sdk_library and depend on " + + "the stubs.", + allowedExceptions: []exceptionHandleFuncLabel{ + checkStubs, + checkApexModule, + checkInCommonApexes, + checkApexIsNonUpdatable, + checkNotStaticOrDynamicDepTag, + checkGlobalAllowlistedDep, + }, + }, + }, + } + + apexContainer.restricted = append(apexContainer.restricted, restriction{ + dependency: apexContainer, + errorMessage: "Module belonging to Apex(es) is not allowed to depend on the " + + "modules belonging to other Apex(es). Either include the depending " + + "module in the Apex or convert the depending module to java_sdk_library " + + "and depend on its stubs.", + allowedExceptions: []exceptionHandleFuncLabel{ + checkStubs, + checkInCommonApexes, + checkNotStaticOrDynamicDepTag, + checkGlobalAllowlistedDep, + }, + }) + + return apexContainer +} + +type ContainersInfo struct { + belongingContainers []*container + + belongingApexes []ApexInfo +} + +func (c *ContainersInfo) BelongingContainers() []*container { + return c.belongingContainers +} + +func (c *ContainersInfo) ApexNames() (ret []string) { + for _, apex := range c.belongingApexes { + ret = append(ret, apex.InApexModules...) + } + slices.Sort(ret) + return ret +} + +// Returns true if any of the apex the module belongs to is updatable. +func (c *ContainersInfo) UpdatableApex() bool { + for _, apex := range c.belongingApexes { + if apex.Updatable { + return true + } + } + return false +} + +var ContainersInfoProvider = blueprint.NewProvider[ContainersInfo]() + +func satisfyAllowedExceptions(ctx ModuleContext, allowedExceptionLabels []exceptionHandleFuncLabel, m, dep Module) bool { + for _, label := range allowedExceptionLabels { + if exceptionHandleFunctionsTable[label](ctx, m, dep) { + return true + } + } + return false +} + +func (c *ContainersInfo) GetViolations(mctx ModuleContext, m, dep Module, depInfo ContainersInfo) []string { + var violations []string + + // Any containers that the module belongs to but the dependency does not belong to must be examined. + _, containersUniqueToModule, _ := ListSetDifference(c.belongingContainers, depInfo.belongingContainers) + + // Apex container should be examined even if both the module and the dependency belong to + // the apex container to check that the two modules belong to the same apex. + if InList(ApexContainer, c.belongingContainers) && !InList(ApexContainer, containersUniqueToModule) { + containersUniqueToModule = append(containersUniqueToModule, ApexContainer) + } + + for _, containerUniqueToModule := range containersUniqueToModule { + for _, restriction := range containerUniqueToModule.restricted { + if InList(restriction.dependency, depInfo.belongingContainers) { + if !satisfyAllowedExceptions(mctx, restriction.allowedExceptions, m, dep) { + violations = append(violations, restriction.errorMessage) + } + } + } + } + + return violations +} + +func generateContainerInfo(ctx ModuleContext) ContainersInfo { + var containers []*container + + for _, cnt := range allContainers { + if containerBoundaryFunctionsTable[cnt](ctx) { + containers = append(containers, cnt) + } + } + + var belongingApexes []ApexInfo + if apexInfo, ok := ModuleProvider(ctx, AllApexInfoProvider); ok { + belongingApexes = apexInfo.ApexInfos + } + + return ContainersInfo{ + belongingContainers: containers, + belongingApexes: belongingApexes, + } +} + +func getContainerModuleInfo(ctx ModuleContext, module Module) (ContainersInfo, bool) { + if ctx.Module() == module { + return ctx.getContainersInfo(), true + } + + return OtherModuleProvider(ctx, module, ContainersInfoProvider) +} + +func setContainerInfo(ctx ModuleContext) { + // Required to determine the unstable container. This provider is set here instead of the + // unstableContainerBoundaryFunc in order to prevent setting the provider multiple times. + SetProvider(ctx, unstableInfoProvider, unstableInfo{ + ContainsPlatformPrivateApis: determineUnstableModule(ctx), + }) + + if _, ok := ctx.Module().(InstallableModule); ok { + containersInfo := generateContainerInfo(ctx) + ctx.setContainersInfo(containersInfo) + SetProvider(ctx, ContainersInfoProvider, containersInfo) + } +} + +func checkContainerViolations(ctx ModuleContext) { + if _, ok := ctx.Module().(InstallableModule); ok { + containersInfo, _ := getContainerModuleInfo(ctx, ctx.Module()) + ctx.VisitDirectDepsIgnoreBlueprint(func(dep Module) { + if !dep.Enabled(ctx) { + return + } + + // Pre-existing violating dependencies are tracked in containerDependencyViolationAllowlist. + // If this dependency is allowlisted, do not check for violation. + // If not, check if this dependency matches any restricted dependency and + // satisfies any exception functions, which allows bypassing the + // restriction. If all of the exceptions are not satisfied, throw an error. + if depContainersInfo, ok := getContainerModuleInfo(ctx, dep); ok { + if allowedViolations, ok := ContainerDependencyViolationAllowlist[ctx.ModuleName()]; ok && InList(dep.Name(), allowedViolations) { + return + } else { + violations := containersInfo.GetViolations(ctx, ctx.Module(), dep, depContainersInfo) + if len(violations) > 0 { + errorMessage := fmt.Sprintf("%s cannot depend on %s. ", ctx.ModuleName(), dep.Name()) + errorMessage += strings.Join(violations, " ") + ctx.ModuleErrorf(errorMessage) + } + } + } + }) + } +} diff --git a/android/container_violations.go b/android/container_violations.go new file mode 100644 index 000000000..efbc8da1a --- /dev/null +++ b/android/container_violations.go @@ -0,0 +1,1145 @@ +// 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 + +var ContainerDependencyViolationAllowlist = map[string][]string{ + "adservices-service-core": { + "gson", // apex [com.android.adservices, com.android.extservices] -> apex [com.android.virt] + }, + + "android.car-module.impl": { + "modules-utils-preconditions", // apex [com.android.car.framework] -> apex [com.android.adservices, com.android.appsearch, com.android.cellbroadcast, com.android.extservices, com.android.ondevicepersonalization, com.android.tethering, com.android.uwb, com.android.wifi, test_com.android.cellbroadcast, test_com.android.wifi] + }, + + "AppInstalledOnMultipleUsers": { + "framework", // cts -> unstable + }, + + "art-aconfig-flags-java-lib": { + "framework-api-annotations-lib", // apex [com.android.art, com.android.art.debug, com.android.art.testing, test_imgdiag_com.android.art, test_jitzygote_com.android.art] -> system + }, + + "Bluetooth": { + "app-compat-annotations", // apex [com.android.btservices] -> system + "framework-bluetooth-pre-jarjar", // apex [com.android.btservices] -> system + }, + + "bluetooth-nano-protos": { + "libprotobuf-java-nano", // apex [com.android.btservices] -> apex [com.android.wifi, test_com.android.wifi] + }, + + "bluetooth.change-ids": { + "app-compat-annotations", // apex [com.android.btservices] -> system + }, + + "CarServiceUpdatable": { + "modules-utils-os", // apex [com.android.car.framework] -> apex [com.android.permission, test_com.android.permission] + "modules-utils-preconditions", // apex [com.android.car.framework] -> apex [com.android.adservices, com.android.appsearch, com.android.cellbroadcast, com.android.extservices, com.android.ondevicepersonalization, com.android.tethering, com.android.uwb, com.android.wifi, test_com.android.cellbroadcast, test_com.android.wifi] + "modules-utils-shell-command-handler", // apex [com.android.car.framework] -> apex [com.android.adservices, com.android.art, com.android.art.debug, com.android.art.testing, com.android.btservices, com.android.configinfrastructure, com.android.mediaprovider, com.android.nfcservices, com.android.permission, com.android.scheduling, com.android.tethering, com.android.uwb, com.android.wifi, test_com.android.mediaprovider, test_com.android.permission, test_com.android.wifi, test_imgdiag_com.android.art, test_jitzygote_com.android.art] + }, + + "cellbroadcastreceiver_aconfig_flags_lib": { + "ext", // apex [com.android.cellbroadcast, test_com.android.cellbroadcast] -> system + "framework", // apex [com.android.cellbroadcast, test_com.android.cellbroadcast] -> system + }, + + "connectivity-net-module-utils-bpf": { + "net-utils-device-common-struct-base", // apex [com.android.tethering] -> system + }, + + "conscrypt-aconfig-flags-lib": { + "aconfig-annotations-lib-sdk-none", // apex [com.android.conscrypt, test_com.android.conscrypt] -> system + }, + + "cronet_aml_base_base_java": { + "framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system + "jsr305", // apex [com.android.tethering] -> apex [com.android.adservices, com.android.devicelock, com.android.extservices, com.android.healthfitness, com.android.media, com.android.mediaprovider, test_com.android.media, test_com.android.mediaprovider] + }, + + "cronet_aml_build_android_build_java": { + "framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system + }, + + "cronet_aml_components_cronet_android_base_feature_overrides_java_proto": { + "framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system + }, + + "cronet_aml_components_cronet_android_cronet_api_java": { + "framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system + }, + + "cronet_aml_components_cronet_android_cronet_impl_common_java": { + "framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system + }, + + "cronet_aml_components_cronet_android_cronet_impl_native_java": { + "framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system + "jsr305", // apex [com.android.tethering] -> apex [com.android.adservices, com.android.devicelock, com.android.extservices, com.android.healthfitness, com.android.media, com.android.mediaprovider, test_com.android.media, test_com.android.mediaprovider] + }, + + "cronet_aml_components_cronet_android_cronet_jni_registration_java": { + "framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system + }, + + "cronet_aml_components_cronet_android_cronet_shared_java": { + "framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system + }, + + "cronet_aml_components_cronet_android_cronet_stats_log_java": { + "framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system + }, + + "cronet_aml_components_cronet_android_cronet_urlconnection_impl_java": { + "framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system + }, + + "cronet_aml_components_cronet_android_flags_java_proto": { + "framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system + }, + + "cronet_aml_components_cronet_android_request_context_config_java_proto": { + "framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system + }, + + "cronet_aml_net_android_net_java": { + "framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system + "jsr305", // apex [com.android.tethering] -> apex [com.android.adservices, com.android.devicelock, com.android.extservices, com.android.healthfitness, com.android.media, com.android.mediaprovider, test_com.android.media, test_com.android.mediaprovider] + }, + + "cronet_aml_net_android_net_thread_stats_uid_java": { + "framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system + }, + + "cronet_aml_third_party_jni_zero_jni_zero_java": { + "framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system + }, + + "cronet_aml_url_url_java": { + "framework-connectivity-pre-jarjar-without-cronet", // apex [com.android.tethering] -> system + }, + + "CtsAdservicesHostTestApp": { + "framework", // cts -> unstable + }, + + "CtsAdServicesNotInAllowListEndToEndTests": { + "framework", // cts -> unstable + }, + + "CtsAdServicesPermissionsAppOptOutEndToEndTests": { + "framework", // cts -> unstable + }, + + "CtsAdServicesPermissionsNoPermEndToEndTests": { + "framework", // cts -> unstable + }, + + "CtsAdServicesPermissionsValidEndToEndTests": { + "framework", // cts -> unstable + }, + + "CtsAlarmManagerTestCases": { + "framework", // cts -> unstable + }, + + "CtsAndroidAppTestCases": { + "framework", // cts -> unstable + }, + + "CtsAppExitTestCases": { + "framework", // cts -> unstable + }, + + "CtsAppFgsStartTestCases": { + "framework", // cts -> unstable + }, + + "CtsAppFgsTestCases": { + "framework", // cts -> unstable + }, + + "CtsAppFunctionTestCases": { + "framework", // cts -> unstable + }, + + "CtsAppOpsTestCases": { + "framework", // cts -> unstable + }, + + "CtsAppSearchTestCases": { + "framework", // cts -> unstable + }, + + "CtsAppStartTestCases": { + "framework", // cts -> unstable + }, + + "CtsAppTestStubsApp2": { + "framework", // cts -> unstable + }, + + "CtsAudioHostTestApp": { + "framework", // cts -> unstable + }, + + "CtsBackgroundActivityAppAllowCrossUidFlagDefault": { + "framework", // cts -> unstable + }, + + "CtsBatterySavingTestCases": { + "framework", // cts -> unstable + }, + + "CtsBluetoothTestCases": { + "framework", // cts -> unstable + }, + + "CtsBootDisplayModeApp": { + "framework", // cts -> unstable + }, + + "CtsBroadcastTestCases": { + "framework", // cts -> unstable + }, + + "CtsBRSTestCases": { + "framework", // cts -> unstable + }, + + "CtsCompanionDeviceManagerCoreTestCases": { + "framework", // cts -> unstable + }, + + "CtsCompanionDeviceManagerMultiProcessTestCases": { + "framework", // cts -> unstable + }, + + "CtsCompanionDeviceManagerUiAutomationTestCases": { + "framework", // cts -> unstable + }, + + "CtsContentSuggestionsTestCases": { + "framework", // cts -> unstable + }, + + "CtsContentTestCases": { + "framework", // cts -> unstable + }, + + "CtsCredentialManagerBackupRestoreApp": { + "framework", // cts -> unstable + }, + + "CtsCrossProfileEnabledApp": { + "framework", // cts -> unstable + }, + + "CtsCrossProfileEnabledNoPermsApp": { + "framework", // cts -> unstable + }, + + "CtsCrossProfileNotEnabledApp": { + "framework", // cts -> unstable + }, + + "CtsCrossProfileUserEnabledApp": { + "framework", // cts -> unstable + }, + + "CtsDeviceAndProfileOwnerApp": { + "framework", // cts -> unstable + }, + + "CtsDeviceAndProfileOwnerApp23": { + "framework", // cts -> unstable + }, + + "CtsDeviceAndProfileOwnerApp25": { + "framework", // cts -> unstable + }, + + "CtsDeviceAndProfileOwnerApp30": { + "framework", // cts -> unstable + }, + + "CtsDeviceLockTestCases": { + "framework", // cts -> unstable + }, + + "CtsDeviceOwnerApp": { + "framework", // cts -> unstable + }, + + "CtsDevicePolicySimTestCases": { + "framework", // cts -> unstable + }, + + "CtsDevicePolicyTestCases": { + "framework", // cts -> unstable + }, + + "CtsDocumentContentTestCases": { + "framework", // cts -> unstable + }, + + "CtsDreamsTestCases": { + "framework", // cts -> unstable + }, + + "CtsDrmTestCases": { + "framework", // cts -> unstable + }, + + "CtsEmptyTestApp_RejectedByVerifier": { + "framework", // cts -> unstable + }, + + "CtsEphemeralTestsEphemeralApp1": { + "framework", // cts -> unstable + }, + + "CtsFgsBootCompletedTestCases": { + "framework", // cts -> unstable + }, + + "CtsFgsBootCompletedTestCasesApi35": { + "framework", // cts -> unstable + }, + + "CtsFgsStartTestHelperApi34": { + "framework", // cts -> unstable + }, + + "CtsFgsStartTestHelperCurrent": { + "framework", // cts -> unstable + }, + + "CtsFgsTimeoutTestCases": { + "framework", // cts -> unstable + }, + + "CtsFileDescriptorTestCases": { + "framework", // cts -> unstable + }, + + "CtsFingerprintTestCases": { + "framework", // cts -> unstable + }, + + "CtsHostsideCompatChangeTestsApp": { + "framework", // cts -> unstable + }, + + "CtsHostsideNetworkPolicyTestsApp2": { + "framework", // cts -> unstable + }, + + "CtsIdentityTestCases": { + "framework", // cts -> unstable + }, + + "CtsIkeTestCases": { + "framework", // cts -> unstable + }, + + "CtsInstalledLoadingProgressDeviceTests": { + "framework", // cts -> unstable + }, + + "CtsInstantAppTests": { + "framework", // cts -> unstable + }, + + "CtsIntentSenderApp": { + "framework", // cts -> unstable + }, + + "CtsJobSchedulerTestCases": { + "framework", // cts -> unstable + }, + + "CtsKeystoreTestCases": { + "framework", // cts -> unstable + }, + + "CtsLegacyNotification27TestCases": { + "framework", // cts -> unstable + }, + + "CtsLibcoreTestCases": { + "framework", // cts -> unstable + }, + + "CtsLibcoreWycheproofConscryptTestCases": { + "framework", // cts -> unstable + }, + + "CtsListeningPortsTest": { + "framework", // cts -> unstable + }, + + "CtsLocationCoarseTestCases": { + "framework", // cts -> unstable + }, + + "CtsLocationFineTestCases": { + "framework", // cts -> unstable + }, + + "CtsLocationNoneTestCases": { + "framework", // cts -> unstable + }, + + "CtsLocationPrivilegedTestCases": { + "framework", // cts -> unstable + }, + + "CtsManagedProfileApp": { + "framework", // cts -> unstable + }, + + "CtsMediaAudioTestCases": { + "framework", // cts -> unstable + }, + + "CtsMediaBetterTogetherTestCases": { + "framework", // cts -> unstable + }, + + "CtsMediaCodecTestCases": { + "framework", // cts -> unstable + }, + + "CtsMediaDecoderTestCases": { + "framework", // cts -> unstable + }, + + "CtsMediaDrmFrameworkTestCases": { + "framework", // cts -> unstable + }, + + "CtsMediaEncoderTestCases": { + "framework", // cts -> unstable + }, + + "CtsMediaExtractorTestCases": { + "framework", // cts -> unstable + }, + + "CtsMediaMiscTestCases": { + "framework", // cts -> unstable + }, + + "CtsMediaMuxerTestCases": { + "framework", // cts -> unstable + }, + + "CtsMediaPerformanceClassTestCases": { + "framework", // cts -> unstable + }, + + "CtsMediaPlayerTestCases": { + "framework", // cts -> unstable + }, + + "CtsMediaProjectionSDK33TestCases": { + "framework", // cts -> unstable + }, + + "CtsMediaProjectionSDK34TestCases": { + "framework", // cts -> unstable + }, + + "CtsMediaProjectionTestCases": { + "framework", // cts -> unstable + }, + + "CtsMediaProviderTestCases": { + "framework", // cts -> unstable + }, + + "CtsMediaProviderTranscodeTests": { + "framework", // cts -> unstable + }, + + "CtsMediaRecorderTestCases": { + "framework", // cts -> unstable + }, + + "CtsMediaRouterHostSideTestBluetoothPermissionsApp": { + "framework", // cts -> unstable + }, + + "CtsMediaRouterHostSideTestMediaRoutingControlApp": { + "framework", // cts -> unstable + }, + + "CtsMediaRouterHostSideTestModifyAudioRoutingApp": { + "framework", // cts -> unstable + }, + + "CtsMediaV2TestCases": { + "framework", // cts -> unstable + }, + + "CtsMimeMapTestCases": { + "framework", // cts -> unstable + }, + + "CtsModifyQuietModeEnabledApp": { + "framework", // cts -> unstable + }, + + "CtsMusicRecognitionTestCases": { + "framework", // cts -> unstable + }, + + "CtsNativeMediaAAudioTestCases": { + "framework", // cts -> unstable + }, + + "CtsNetTestCases": { + "framework", // cts -> unstable + }, + + "CtsNetTestCasesLegacyApi22": { + "framework", // cts -> unstable + }, + + "CtsNetTestCasesMaxTargetSdk30": { + "framework", // cts -> unstable + }, + + "CtsNetTestCasesMaxTargetSdk31": { + "framework", // cts -> unstable + }, + + "CtsNetTestCasesMaxTargetSdk33": { + "framework", // cts -> unstable + }, + + "CtsNetTestCasesUpdateStatsPermission": { + "framework", // cts -> unstable + }, + + "CtsNfcTestCases": { + "framework", // cts -> unstable + }, + + "CtsOnDeviceIntelligenceServiceTestCases": { + "framework", // cts -> unstable + }, + + "CtsOnDevicePersonalizationTestCases": { + "framework", // cts -> unstable + }, + + "CtsPackageInstallerApp": { + "framework", // cts -> unstable + }, + + "CtsPackageManagerTestCases": { + "framework", // cts -> unstable + }, + + "CtsPackageSchemeTestsWithoutVisibility": { + "framework", // cts -> unstable + }, + + "CtsPackageSchemeTestsWithVisibility": { + "framework", // cts -> unstable + }, + + "CtsPackageWatchdogTestCases": { + "framework", // cts -> unstable + }, + + "CtsPermissionsSyncTestApp": { + "framework", // cts -> unstable + }, + + "CtsPreservedSettingsApp": { + "framework", // cts -> unstable + }, + + "CtsProtoTestCases": { + "framework", // cts -> unstable + }, + + "CtsProviderTestCases": { + "framework", // cts -> unstable + }, + + "CtsProxyMediaRouterTestHelperApp": { + "framework", // cts -> unstable + }, + + "CtsRebootReadinessTestCases": { + "framework", // cts -> unstable + }, + + "CtsResourcesLoaderTests": { + "framework", // cts -> unstable + }, + + "CtsResourcesTestCases": { + "framework", // cts -> unstable + }, + + "CtsSandboxedAdIdManagerTests": { + "framework", // cts -> unstable + }, + + "CtsSandboxedAppSetIdManagerTests": { + "framework", // cts -> unstable + }, + + "CtsSandboxedFledgeManagerTests": { + "framework", // cts -> unstable + }, + + "CtsSandboxedMeasurementManagerTests": { + "framework", // cts -> unstable + }, + + "CtsSandboxedTopicsManagerTests": { + "framework", // cts -> unstable + }, + + "CtsSdkExtensionsTestCases": { + "framework", // cts -> unstable + }, + + "CtsSdkSandboxInprocessTests": { + "framework", // cts -> unstable + }, + + "CtsSecureElementTestCases": { + "framework", // cts -> unstable + }, + + "CtsSecurityTestCases": { + "framework", // cts -> unstable + }, + + "CtsSelinuxEphemeralTestCases": { + "framework", // cts -> unstable + }, + + "CtsSelinuxTargetSdk25TestCases": { + "framework", // cts -> unstable + }, + + "CtsSelinuxTargetSdk27TestCases": { + "framework", // cts -> unstable + }, + + "CtsSelinuxTargetSdk28TestCases": { + "framework", // cts -> unstable + }, + + "CtsSelinuxTargetSdk29TestCases": { + "framework", // cts -> unstable + }, + + "CtsSelinuxTargetSdk30TestCases": { + "framework", // cts -> unstable + }, + + "CtsSelinuxTargetSdkCurrentTestCases": { + "framework", // cts -> unstable + }, + + "CtsSettingsDeviceOwnerApp": { + "framework", // cts -> unstable + }, + + "CtsSharedUserMigrationTestCases": { + "framework", // cts -> unstable + }, + + "CtsShortFgsTestCases": { + "framework", // cts -> unstable + }, + + "CtsSimRestrictedApisTestCases": { + "framework", // cts -> unstable + }, + + "CtsSliceTestCases": { + "framework", // cts -> unstable + }, + + "CtsSpeechTestCases": { + "framework", // cts -> unstable + }, + + "CtsStatsSecurityApp": { + "framework", // cts -> unstable + }, + + "CtsSuspendAppsTestCases": { + "framework", // cts -> unstable + }, + + "CtsSystemUiTestCases": { + "framework", // cts -> unstable + }, + + "CtsTareTestCases": { + "framework", // cts -> unstable + }, + + "CtsTelephonyTestCases": { + "framework", // cts -> unstable + }, + + "CtsTetheringTest": { + "framework", // cts -> unstable + }, + + "CtsThreadNetworkTestCases": { + "framework", // cts -> unstable + }, + + "CtsTvInputTestCases": { + "framework", // cts -> unstable + }, + + "CtsTvTunerTestCases": { + "framework", // cts -> unstable + }, + + "CtsUsageStatsTestCases": { + "framework", // cts -> unstable + }, + + "CtsUsbManagerTestCases": { + "framework", // cts -> unstable + }, + + "CtsUserRestrictionTestCases": { + "framework", // cts -> unstable + }, + + "CtsUtilTestCases": { + "framework", // cts -> unstable + }, + + "CtsUwbTestCases": { + "framework", // cts -> unstable + }, + + "CtsVcnTestCases": { + "framework", // cts -> unstable + }, + + "CtsVideoCodecTestCases": { + "framework", // cts -> unstable + }, + + "CtsVideoTestCases": { + "framework", // cts -> unstable + }, + + "CtsViewReceiveContentTestCases": { + "framework", // cts -> unstable + }, + + "CtsVirtualDevicesAppLaunchTestCases": { + "framework", // cts -> unstable + }, + + "CtsVirtualDevicesAudioTestCases": { + "framework", // cts -> unstable + }, + + "CtsVirtualDevicesCameraTestCases": { + "framework", // cts -> unstable + }, + + "CtsVirtualDevicesSensorTestCases": { + "framework", // cts -> unstable + }, + + "CtsVirtualDevicesTestCases": { + "framework", // cts -> unstable + }, + + "CtsWearableSensingServiceTestCases": { + "framework", // cts -> unstable + }, + + "CtsWebViewCompatChangeApp": { + "framework", // cts -> unstable + }, + + "CtsWidgetTestCases": { + "framework", // cts -> unstable + }, + + "CtsWidgetTestCases29": { + "framework", // cts -> unstable + }, + + "CtsWifiNonUpdatableTestCases": { + "framework", // cts -> unstable + }, + + "CtsWifiTestCases": { + "framework", // cts -> unstable + }, + + "CtsWindowManagerExternalApp": { + "framework", // cts -> unstable + }, + + "CtsWindowManagerTestCases": { + "framework", // cts -> unstable + }, + + "CtsZipValidateApp": { + "framework", // cts -> unstable + }, + + "CVE-2021-0965": { + "framework", // cts -> unstable + }, + + "device_config_reboot_flags_java_lib": { + "ext", // apex [com.android.configinfrastructure] -> system + "framework", // apex [com.android.configinfrastructure] -> system + }, + + "devicelockcontroller-lib": { + "modules-utils-expresslog", // apex [com.android.devicelock] -> apex [com.android.btservices, com.android.car.framework] + }, + + "FederatedCompute": { + "auto_value_annotations", // apex [com.android.ondevicepersonalization] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus] + }, + + "framework-adservices.impl": { + "adservices_flags_lib", // apex [com.android.adservices, com.android.extservices] -> system + }, + + "framework-bluetooth.impl": { + "app-compat-annotations", // apex [com.android.btservices] -> system + }, + + "framework-configinfrastructure.impl": { + "configinfra_framework_flags_java_lib", // apex [com.android.configinfrastructure] -> system + }, + + "framework-connectivity-t.impl": { + "app-compat-annotations", // apex [com.android.tethering] -> system + "framework-connectivity-pre-jarjar", // apex [com.android.tethering] -> system + }, + + "framework-connectivity.impl": { + "app-compat-annotations", // apex [com.android.tethering] -> system + }, + + "framework-ondevicepersonalization.impl": { + "app-compat-annotations", // apex [com.android.ondevicepersonalization] -> system + "ondevicepersonalization_flags_lib", // apex [com.android.ondevicepersonalization] -> system + }, + + "framework-pdf-v.impl": { + "app-compat-annotations", // apex [com.android.mediaprovider, test_com.android.mediaprovider] -> system + "modules-utils-preconditions", // apex [com.android.mediaprovider, test_com.android.mediaprovider] -> apex [com.android.adservices, com.android.appsearch, com.android.cellbroadcast, com.android.extservices, com.android.ondevicepersonalization, com.android.tethering, com.android.uwb, com.android.wifi, test_com.android.cellbroadcast, test_com.android.wifi] + }, + + "framework-pdf.impl": { + "modules-utils-preconditions", // apex [com.android.mediaprovider, test_com.android.mediaprovider] -> apex [com.android.adservices, com.android.appsearch, com.android.cellbroadcast, com.android.extservices, com.android.ondevicepersonalization, com.android.tethering, com.android.uwb, com.android.wifi, test_com.android.cellbroadcast, test_com.android.wifi] + }, + + "framework-permission-s.impl": { + "app-compat-annotations", // apex [com.android.permission, test_com.android.permission] -> system + }, + + "framework-wifi.impl": { + "aconfig_storage_reader_java", // apex [com.android.wifi, test_com.android.wifi] -> system + "app-compat-annotations", // apex [com.android.wifi, test_com.android.wifi] -> system + }, + + "grpc-java-core-internal": { + "gson", // apex [com.android.adservices, com.android.devicelock, com.android.extservices] -> apex [com.android.virt] + "perfmark-api-lib", // apex [com.android.adservices, com.android.devicelock, com.android.extservices] -> system + }, + + "httpclient_impl": { + "httpclient_api", // apex [com.android.tethering] -> system + }, + + "IncrementalTestAppValidator": { + "framework", // cts -> unstable + }, + + "libcore-aconfig-flags-lib": { + "framework-api-annotations-lib", // apex [com.android.art, com.android.art.debug, com.android.art.testing, test_imgdiag_com.android.art, test_jitzygote_com.android.art] -> system + }, + + "loadlibrarytest_product_app": { + "libnativeloader_vendor_shared_lib", // product -> vendor + }, + + "loadlibrarytest_testlib": { + "libnativeloader_vendor_shared_lib", // system -> vendor + }, + + "MctsMediaBetterTogetherTestCases": { + "framework", // cts -> unstable + }, + + "MctsMediaCodecTestCases": { + "framework", // cts -> unstable + }, + + "MctsMediaDecoderTestCases": { + "framework", // cts -> unstable + }, + + "MctsMediaDrmFrameworkTestCases": { + "framework", // cts -> unstable + }, + + "MctsMediaEncoderTestCases": { + "framework", // cts -> unstable + }, + + "MctsMediaExtractorTestCases": { + "framework", // cts -> unstable + }, + + "MctsMediaMiscTestCases": { + "framework", // cts -> unstable + }, + + "MctsMediaMuxerTestCases": { + "framework", // cts -> unstable + }, + + "MctsMediaPlayerTestCases": { + "framework", // cts -> unstable + }, + + "MctsMediaRecorderTestCases": { + "framework", // cts -> unstable + }, + + "MctsMediaTranscodingTestCases": { + "framework", // cts -> unstable + }, + + "MctsMediaV2TestCases": { + "framework", // cts -> unstable + }, + + "MediaProvider": { + "app-compat-annotations", // apex [com.android.mediaprovider, test_com.android.mediaprovider] -> system + }, + + "mediaprovider_flags_java_lib": { + "ext", // apex [com.android.mediaprovider, test_com.android.mediaprovider] -> system + "framework", // apex [com.android.mediaprovider, test_com.android.mediaprovider] -> system + }, + + "MockSatelliteGatewayServiceApp": { + "framework", // cts -> unstable + }, + + "MockSatelliteServiceApp": { + "framework", // cts -> unstable + }, + + "net-utils-device-common-netlink": { + "net-utils-device-common-struct-base", // apex [com.android.tethering] -> system + }, + + "net-utils-device-common-struct": { + "net-utils-device-common-struct-base", // apex [com.android.tethering] -> system + }, + + "NfcNciApex": { + "android.permission.flags-aconfig-java", // apex [com.android.nfcservices] -> apex [com.android.permission, test_com.android.permission] + }, + + "okhttp-norepackage": { + "okhttp-android-util-log", // apex [com.android.adservices, com.android.devicelock, com.android.extservices] -> system + }, + + "ondevicepersonalization-plugin-lib": { + "auto_value_annotations", // apex [com.android.ondevicepersonalization] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus] + }, + + "opencensus-java-api": { + "auto_value_annotations", // apex [com.android.devicelock] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus] + }, + + "PermissionController-lib": { + "safety-center-annotations", // apex [com.android.permission, test_com.android.permission] -> system + }, + + "PlatformProperties": { + "sysprop-library-stub-platform", // apex [com.android.btservices, com.android.nfcservices, com.android.tethering, com.android.virt, com.android.wifi, test_com.android.wifi] -> system + }, + + "safety-center-config": { + "safety-center-annotations", // apex [com.android.permission, test_com.android.permission] -> system + }, + + "safety-center-internal-data": { + "safety-center-annotations", // apex [com.android.permission, test_com.android.permission] -> system + }, + + "safety-center-pending-intents": { + "safety-center-annotations", // apex [com.android.permission, test_com.android.permission] -> system + }, + + "safety-center-persistence": { + "safety-center-annotations", // apex [com.android.permission, test_com.android.permission] -> system + }, + + "safety-center-resources-lib": { + "safety-center-annotations", // apex [com.android.permission, test_com.android.permission] -> system + }, + + "SdkSandboxManagerDisabledTests": { + "framework", // cts -> unstable + }, + + "SdkSandboxManagerTests": { + "framework", // cts -> unstable + }, + + "service-art.impl": { + "auto_value_annotations", // apex [com.android.art, com.android.art.debug, com.android.art.testing, test_imgdiag_com.android.art, test_jitzygote_com.android.art] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus] + }, + + "service-bluetooth-pre-jarjar": { + "framework-bluetooth-pre-jarjar", // apex [com.android.btservices] -> system + "service-bluetooth.change-ids", // apex [com.android.btservices] -> system + }, + + "service-connectivity": { + "libprotobuf-java-nano", // apex [com.android.tethering] -> apex [com.android.wifi, test_com.android.wifi] + }, + + "service-connectivity-pre-jarjar": { + "framework-connectivity-pre-jarjar", // apex [com.android.tethering] -> system + }, + + "service-connectivity-protos": { + "libprotobuf-java-nano", // apex [com.android.tethering] -> apex [com.android.wifi, test_com.android.wifi] + }, + + "service-connectivity-tiramisu-pre-jarjar": { + "framework-connectivity-pre-jarjar", // apex [com.android.tethering] -> system + "framework-connectivity-t-pre-jarjar", // apex [com.android.tethering] -> system + }, + + "service-entitlement": { + "auto_value_annotations", // apex [com.android.wifi, test_com.android.wifi] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus] + }, + + "service-entitlement-api": { + "auto_value_annotations", // apex [com.android.wifi, test_com.android.wifi] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus] + }, + + "service-entitlement-data": { + "auto_value_annotations", // apex [com.android.wifi, test_com.android.wifi] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus] + }, + + "service-entitlement-impl": { + "auto_value_annotations", // apex [com.android.wifi, test_com.android.wifi] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus] + }, + + "service-healthfitness.impl": { + "modules-utils-preconditions", // apex [com.android.healthfitness] -> apex [com.android.adservices, com.android.appsearch, com.android.cellbroadcast, com.android.extservices, com.android.ondevicepersonalization, com.android.tethering, com.android.uwb, com.android.wifi, test_com.android.cellbroadcast, test_com.android.wifi] + }, + + "service-networksecurity-pre-jarjar": { + "framework-connectivity-pre-jarjar", // apex [com.android.tethering] -> system + }, + + "service-permission.impl": { + "jsr305", // apex [com.android.permission, test_com.android.permission] -> apex [com.android.adservices, com.android.devicelock, com.android.extservices, com.android.healthfitness, com.android.media, com.android.mediaprovider, test_com.android.media, test_com.android.mediaprovider] + "safety-center-annotations", // apex [com.android.permission, test_com.android.permission] -> system + }, + + "service-remoteauth-pre-jarjar": { + "framework-connectivity-pre-jarjar", // apex [com.android.tethering] -> system + "framework-connectivity-t-pre-jarjar", // apex [com.android.tethering] -> system + }, + + "service-thread-pre-jarjar": { + "framework-connectivity-pre-jarjar", // apex [com.android.tethering] -> system + "framework-connectivity-t-pre-jarjar", // apex [com.android.tethering] -> system + }, + + "service-uwb-pre-jarjar": { + "framework-uwb-pre-jarjar", // apex [com.android.uwb] -> system + }, + + "service-wifi": { + "auto_value_annotations", // apex [com.android.wifi, test_com.android.wifi] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus] + }, + + "TelephonyDeviceTest": { + "framework", // cts -> unstable + }, + + "tensorflowlite_java": { + "android-support-annotations", // apex [com.android.adservices, com.android.extservices, com.android.ondevicepersonalization] -> system + }, + + "TestExternalImsServiceApp": { + "framework", // cts -> unstable + }, + + "TestSmsRetrieverApp": { + "framework", // cts -> unstable + }, + + "TetheringApiCurrentLib": { + "connectivity-internal-api-util", // apex [com.android.tethering] -> system + }, + + "TetheringNext": { + "connectivity-internal-api-util", // apex [com.android.tethering] -> system + }, + + "tetheringstatsprotos": { + "ext", // apex [com.android.tethering] -> system + "framework", // apex [com.android.tethering] -> system + }, + + "uwb_aconfig_flags_lib": { + "ext", // apex [com.android.uwb] -> system + "framework", // apex [com.android.uwb] -> system + }, + + "uwb_androidx_backend": { + "android-support-annotations", // apex [com.android.tethering] -> system + }, + + "wifi-service-pre-jarjar": { + "app-compat-annotations", // apex [com.android.wifi, test_com.android.wifi] -> system + "auto_value_annotations", // apex [com.android.wifi, test_com.android.wifi] -> apex [com.android.adservices, com.android.extservices, com.android.extservices_tplus] + "framework-wifi-pre-jarjar", // apex [com.android.wifi, test_com.android.wifi] -> system + "jsr305", // apex [com.android.wifi, test_com.android.wifi] -> apex [com.android.adservices, com.android.devicelock, com.android.extservices, com.android.healthfitness, com.android.media, com.android.mediaprovider, test_com.android.media, test_com.android.mediaprovider] + }, +} diff --git a/android/deapexer.go b/android/deapexer.go index 61ae64ea5..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,41 +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, _ := OtherModuleProvider(ctx, m, DeapexerProvider) - 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 c0a2fc68f..3d06c69c9 100644 --- a/android/defaults.go +++ b/android/defaults.go @@ -28,7 +28,7 @@ type defaultsDependencyTag struct { var DefaultsDepTag defaultsDependencyTag type defaultsProperties struct { - Defaults proptools.Configurable[[]string] + Defaults []string } type DefaultableModuleBase struct { @@ -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,18 +274,22 @@ 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) { if defaultable, ok := ctx.Module().(Defaultable); ok { - ctx.AddDependency(ctx.Module(), DefaultsDepTag, defaultable.defaults().Defaults.GetOrDefault(ctx, nil)...) + ctx.AddDependency(ctx.Module(), DefaultsDepTag, defaultable.defaults().Defaults...) } } -func defaultsMutator(ctx TopDownMutatorContext) { +func defaultsMutator(ctx BottomUpMutatorContext) { if defaultable, ok := ctx.Module().(Defaultable); ok { - defaults := defaultable.defaults().Defaults.GetOrDefault(ctx, nil) + 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 seen := make(map[Defaults]bool) @@ -295,7 +300,7 @@ func defaultsMutator(ctx TopDownMutatorContext) { if !seen[defaults] { seen[defaults] = true defaultsList = append(defaultsList, defaults) - return len(defaults.defaults().Defaults.GetOrDefault(ctx, nil)) > 0 + return len(defaults.defaults().Defaults) > 0 } } else { ctx.PropertyErrorf("defaults", "module %s is not an defaults module", diff --git a/android/defs.go b/android/defs.go index 78cdea2ba..9f3fb1ee6 100644 --- a/android/defs.go +++ b/android/defs.go @@ -16,7 +16,6 @@ package android import ( "github.com/google/blueprint" - "github.com/google/blueprint/bootstrap" ) var ( @@ -120,8 +119,3 @@ func init() { return ctx.Config().RBEWrapper() }) } - -// GlobToListFileRule creates a rule that writes a list of files matching a pattern to a file. -func GlobToListFileRule(ctx ModuleContext, pattern string, excludes []string, file WritablePath) { - bootstrap.GlobFile(ctx.blueprintModuleContext(), pattern, excludes, file.String()) -} diff --git a/android/depset_generic.go b/android/depset_generic.go index 45c193715..690987a0c 100644 --- a/android/depset_generic.go +++ b/android/depset_generic.go @@ -15,6 +15,9 @@ package android import ( + "bytes" + "encoding/gob" + "errors" "fmt" ) @@ -65,6 +68,30 @@ type DepSet[T depSettableType] struct { transitive []*DepSet[T] } +func (d *DepSet[T]) GobEncode() ([]byte, error) { + w := new(bytes.Buffer) + encoder := gob.NewEncoder(w) + err := errors.Join(encoder.Encode(d.preorder), encoder.Encode(d.reverse), + encoder.Encode(d.order), encoder.Encode(d.direct), encoder.Encode(d.transitive)) + if err != nil { + return nil, err + } + + return w.Bytes(), nil +} + +func (d *DepSet[T]) GobDecode(data []byte) error { + r := bytes.NewBuffer(data) + decoder := gob.NewDecoder(r) + err := errors.Join(decoder.Decode(&d.preorder), decoder.Decode(&d.reverse), + decoder.Decode(&d.order), decoder.Decode(&d.direct), decoder.Decode(&d.transitive)) + if err != nil { + return err + } + + return nil +} + // NewDepSet returns an immutable DepSet with the given order, direct and transitive contents. func NewDepSet[T depSettableType](order DepSetOrder, direct []T, transitive []*DepSet[T]) *DepSet[T] { var directCopy []T diff --git a/android/image.go b/android/image.go index 9cad05656..6e5a551df 100644 --- a/android/image.go +++ b/android/image.go @@ -14,11 +14,17 @@ package android -// ImageInterface is implemented by modules that need to be split by the imageMutator. +// ImageInterface is implemented by modules that need to be split by the imageTransitionMutator. type ImageInterface interface { // ImageMutatorBegin is called before any other method in the ImageInterface. ImageMutatorBegin(ctx BaseModuleContext) + // VendorVariantNeeded should return true if the module needs a vendor variant (installed on the vendor image). + VendorVariantNeeded(ctx BaseModuleContext) bool + + // ProductVariantNeeded should return true if the module needs a product variant (installed on the product image). + ProductVariantNeeded(ctx BaseModuleContext) bool + // CoreVariantNeeded should return true if the module needs a core variant (installed on the system image). CoreVariantNeeded(ctx BaseModuleContext) bool @@ -49,6 +55,14 @@ type ImageInterface interface { } const ( + // VendorVariation is the variant name used for /vendor code that does not + // compile against the VNDK. + VendorVariation string = "vendor" + + // ProductVariation is the variant name used for /product code that does not + // compile against the VNDK. + ProductVariation string = "product" + // CoreVariation is the variant used for framework-private libraries, or // SDK libraries. (which framework-private libraries can use), which // will be installed to the system image. @@ -67,18 +81,16 @@ const ( DebugRamdiskVariation string = "debug_ramdisk" ) -// imageMutator creates variants for modules that implement the ImageInterface that +// imageTransitionMutator creates variants for modules that implement the ImageInterface that // allow them to build differently for each partition (recovery, core, vendor, etc.). -func imageMutator(ctx BottomUpMutatorContext) { - if ctx.Os() != Android { - return - } +type imageTransitionMutator struct{} - if m, ok := ctx.Module().(ImageInterface); ok { +func (imageTransitionMutator) Split(ctx BaseModuleContext) []string { + var variations []string + + if m, ok := ctx.Module().(ImageInterface); ctx.Os() == Android && ok { m.ImageMutatorBegin(ctx) - var variations []string - if m.CoreVariantNeeded(ctx) { variations = append(variations, CoreVariation) } @@ -94,18 +106,38 @@ func imageMutator(ctx BottomUpMutatorContext) { if m.RecoveryVariantNeeded(ctx) { variations = append(variations, RecoveryVariation) } + if m.VendorVariantNeeded(ctx) { + variations = append(variations, VendorVariation) + } + if m.ProductVariantNeeded(ctx) { + variations = append(variations, ProductVariation) + } extraVariations := m.ExtraImageVariations(ctx) variations = append(variations, extraVariations...) + } - if len(variations) == 0 { - return - } + if len(variations) == 0 { + variations = append(variations, "") + } - mod := ctx.CreateVariations(variations...) - for i, v := range variations { - mod[i].base().setImageVariation(v) - mod[i].(ImageInterface).SetImageVariation(ctx, v) - } + return variations +} + +func (imageTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string { + return sourceVariation +} + +func (imageTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string { + if _, ok := ctx.Module().(ImageInterface); ctx.Os() != Android || !ok { + return CoreVariation + } + return incomingVariation +} + +func (imageTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) { + ctx.Module().base().setImageVariation(variation) + if m, ok := ctx.Module().(ImageInterface); ok { + m.SetImageVariation(ctx, variation) } } diff --git a/android/init.go b/android/init.go new file mode 100644 index 000000000..b46229282 --- /dev/null +++ b/android/init.go @@ -0,0 +1,23 @@ +// 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 + +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 3fea02909..0ac975fa2 100644 --- a/android/license_metadata.go +++ b/android/license_metadata.go @@ -33,7 +33,7 @@ var ( }, "args") ) -func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) { +func buildLicenseMetadata(ctx *moduleContext, licenseMetadataFile WritablePath) { base := ctx.Module().base() if !base.Enabled(ctx) { @@ -45,16 +45,15 @@ func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) { } var outputFiles Paths - if outputFileProducer, ok := ctx.Module().(OutputFileProducer); ok { - outputFiles, _ = outputFileProducer.OutputFiles("") + if outputFiles, err := outputFilesForModule(ctx, ctx.Module(), ""); err == nil { outputFiles = PathsIfNonNil(outputFiles...) } // Only pass the last installed file to isContainerFromFileExtensions so a *.zip file in test data // doesn't mark the whole module as a container. var installFiles InstallPaths - if len(base.installFiles) > 0 { - installFiles = InstallPaths{base.installFiles[len(base.installFiles)-1]} + if len(ctx.installFiles) > 0 { + installFiles = InstallPaths{ctx.installFiles[len(ctx.installFiles)-1]} } isContainer := isContainerFromFileExtensions(installFiles, outputFiles) @@ -93,7 +92,7 @@ func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) { allDepMetadataArgs = append(allDepMetadataArgs, info.LicenseMetadataPath.String()+depAnnotations) - if depInstallFiles := dep.base().installFiles; len(depInstallFiles) > 0 { + if depInstallFiles := OtherModuleProviderOrDefault(ctx, dep, InstallFilesProvider).InstallFiles; len(depInstallFiles) > 0 { allDepOutputFiles = append(allDepOutputFiles, depInstallFiles.Paths()...) } else if depOutputFiles, err := outputFilesForModule(ctx, dep, ""); err == nil { depOutputFiles = PathsIfNonNil(depOutputFiles...) @@ -153,7 +152,7 @@ func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) { // Install map args = append(args, - JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.licenseInstallMap), "-m ")) + JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(ctx.licenseInstallMap), "-m ")) // Built files if len(outputFiles) > 0 { @@ -163,7 +162,7 @@ func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) { // Installed files args = append(args, - JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.installFiles.Strings()), "-i ")) + JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(ctx.installFiles.Strings()), "-i ")) if isContainer { args = append(args, "--is_container") diff --git a/android/logtags.go b/android/logtags.go index d11cccf4f..7929057ff 100644 --- a/android/logtags.go +++ b/android/logtags.go @@ -42,7 +42,7 @@ func (l *logtagsSingleton) GenerateBuildActions(ctx SingletonContext) { if !module.ExportedToMake() { return } - if logtagsInfo, ok := SingletonModuleProvider(ctx, module, LogtagsProviderKey); ok { + if logtagsInfo, ok := OtherModuleProvider(ctx, module, LogtagsProviderKey); ok { allLogtags = append(allLogtags, logtagsInfo.Logtags...) } }) diff --git a/android/makevars.go b/android/makevars.go index f92f4581e..8305d8e00 100644 --- a/android/makevars.go +++ b/android/makevars.go @@ -94,7 +94,7 @@ type MakeVarsContext interface { ModuleDir(module blueprint.Module) string ModuleSubDir(module blueprint.Module) string ModuleType(module blueprint.Module) string - moduleProvider(module blueprint.Module, key blueprint.AnyProviderKey) (any, bool) + otherModuleProvider(module blueprint.Module, key blueprint.AnyProviderKey) (any, bool) BlueprintFile(module blueprint.Module) string ModuleErrorf(module blueprint.Module, format string, args ...interface{}) @@ -279,10 +279,11 @@ func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) { } if m.ExportedToMake() { - katiInstalls = append(katiInstalls, m.base().katiInstalls...) - katiInitRcInstalls = append(katiInitRcInstalls, m.base().katiInitRcInstalls...) - katiVintfManifestInstalls = append(katiVintfManifestInstalls, m.base().katiVintfInstalls...) - katiSymlinks = append(katiSymlinks, m.base().katiSymlinks...) + info := OtherModuleProviderOrDefault(ctx, m, InstallFilesProvider) + katiInstalls = append(katiInstalls, info.KatiInstalls...) + katiInitRcInstalls = append(katiInitRcInstalls, info.KatiInitRcInstalls...) + katiVintfManifestInstalls = append(katiVintfManifestInstalls, info.KatiVintfInstalls...) + katiSymlinks = append(katiSymlinks, info.KatiSymlinks...) } }) diff --git a/android/module.go b/android/module.go index b4f31f775..e2b7e119a 100644 --- a/android/module.go +++ b/android/module.go @@ -15,9 +15,9 @@ package android import ( - "crypto/md5" - "encoding/hex" - "encoding/json" + "bytes" + "encoding/gob" + "errors" "fmt" "net/url" "path/filepath" @@ -26,8 +26,6 @@ import ( "sort" "strings" - "android/soong/bazel" - "github.com/google/blueprint" "github.com/google/blueprint/proptools" ) @@ -60,7 +58,7 @@ type Module interface { base() *ModuleBase Disable() - Enabled(ctx ConfigAndErrorContext) bool + Enabled(ctx ConfigurableEvaluatorContext) bool Target() Target MultiTargets() []Target @@ -92,8 +90,6 @@ type Module interface { ReplacedByPrebuilt() IsReplacedByPrebuilt() bool ExportedToMake() bool - InitRc() Paths - VintfFragments() Paths EffectiveLicenseKinds() []string EffectiveLicenseFiles() Paths @@ -113,18 +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 ConfigurableEvaluatorContext) []string - FilesToInstall() InstallPaths - PackagingSpecs() []PackagingSpec - - // TransitivePackagingSpecs returns the PackagingSpecs for this module and any transitive - // dependencies with dependency tags for which IsInstallDepNeeded() returns true. - TransitivePackagingSpecs() []PackagingSpec - - ConfigurableEvaluator(ctx ConfigAndErrorContext) proptools.ConfigurableEvaluator + ConfigurableEvaluator(ctx ConfigurableEvaluatorContext) proptools.ConfigurableEvaluator } // Qualified id for a module @@ -249,31 +239,6 @@ func SortedUniqueNamedPaths(l NamedPaths) NamedPaths { return l[:k+1] } -// soongConfigTrace holds all references to VendorVars. Uses []string for blueprint:"mutated" -type soongConfigTrace struct { - Bools []string `json:",omitempty"` - Strings []string `json:",omitempty"` - IsSets []string `json:",omitempty"` -} - -func (c *soongConfigTrace) isEmpty() bool { - return len(c.Bools) == 0 && len(c.Strings) == 0 && len(c.IsSets) == 0 -} - -// Returns hash of serialized trace records (empty string if there's no trace recorded) -func (c *soongConfigTrace) hash() string { - // Use MD5 for speed. We don't care collision or preimage attack - if c.isEmpty() { - return "" - } - j, err := json.Marshal(c) - if err != nil { - panic(fmt.Errorf("json marshal of %#v failed: %#v", *c, err)) - } - hash := md5.Sum(j) - return hex.EncodeToString(hash[:]) -} - type nameProperties struct { // The name of the module. Must be unique across all modules. Name *string @@ -416,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"` @@ -478,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//). It can be installed only to the packaging // modules like android_filesystem. @@ -525,16 +484,15 @@ type commonProperties struct { // constants in image.go, but can also be set to a custom value by individual module types. ImageVariation string `blueprint:"mutated"` - // SoongConfigTrace records accesses to VendorVars (soong_config). The trace will be hashed - // and used as a subdir of PathForModuleOut. Note that we mainly focus on incremental - // builds among similar products (e.g. aosp_cf_x86_64_phone and aosp_cf_x86_64_foldable), - // and there are variables other than soong_config, which isn't captured by soong config - // trace, but influence modules among products. - SoongConfigTrace soongConfigTrace `blueprint:"mutated"` - SoongConfigTraceHash string `blueprint:"mutated"` - // The team (defined by the owner/vendor) who owns the property. Team *string `android:"path"` + + // vintf_fragment Modules required from this module. + Vintf_fragment_modules proptools.Configurable[[]string] `android:"path"` + + // List of module names that are prevented from being installed when this module gets + // installed. + Overrides []string } type distProperties struct { @@ -641,6 +599,11 @@ type hostAndDeviceProperties struct { Device_supported *bool } +type hostCrossProperties struct { + // If set to true, build a variant of the module for the host cross. Defaults to true. + Host_cross_supported *bool +} + type Multilib string const ( @@ -756,6 +719,10 @@ func InitAndroidArchModule(m Module, hod HostOrDeviceSupported, defaultMultilib m.AddProperties(&base.hostAndDeviceProperties) } + if hod&hostCrossSupported != 0 { + m.AddProperties(&base.hostCrossProperties) + } + initArchModule(m) } @@ -841,6 +808,7 @@ type ModuleBase struct { distProperties distProperties variableProperties interface{} hostAndDeviceProperties hostAndDeviceProperties + hostCrossProperties hostCrossProperties // Arch specific versions of structs in GetProperties() prior to // initialization in InitAndroidArchModule, lets call it `generalProperties`. @@ -849,9 +817,6 @@ type ModuleBase struct { // archPropRoot that is filled with arch specific values by the arch mutator. archProperties [][]interface{} - // Properties specific to the Blueprint to BUILD migration. - bazelTargetModuleProperties bazel.BazelTargetModuleProperties - // Information about all the properties on the module that contains visibility rules that need // checking. visibilityPropertyInfo []visibilityProperty @@ -862,30 +827,7 @@ type ModuleBase struct { // The primary licenses property, may be nil, records license metadata for the module. primaryLicensesProperty applicableLicensesProperty - noAddressSanitizer bool - installFiles InstallPaths - installFilesDepSet *DepSet[InstallPath] - checkbuildFiles Paths - packagingSpecs []PackagingSpec - packagingSpecsDepSet *DepSet[PackagingSpec] - // katiInstalls tracks the install rules that were created by Soong but are being exported - // to Make to convert to ninja rules so that Make can add additional dependencies. - katiInstalls katiInstalls - // katiInitRcInstalls and katiVintfInstalls track the install rules created by Soong that are - // allowed to have duplicates across modules and variants. - katiInitRcInstalls katiInstalls - katiVintfInstalls katiInstalls - katiSymlinks katiInstalls - testData []DataPath - - // The files to copy to the dist as explicitly specified in the .bp file. - distFiles TaggedDistFiles - - // Used by buildTargetSingleton to create checkbuild and per-directory build targets - // Only set on the final variant of each module - installTarget WritablePath - checkbuildTarget WritablePath - blueprintDir string + noAddressSanitizer bool hooks hooks @@ -895,30 +837,6 @@ type ModuleBase struct { buildParams []BuildParams ruleParams map[blueprint.Rule]blueprint.RuleParams variables map[string]string - - initRcPaths Paths - vintfFragmentsPaths Paths - - installedInitRcPaths InstallPaths - installedVintfFragmentsPaths InstallPaths - - // Merged Aconfig files for all transitive deps. - aconfigFilePaths Paths - - // set of dependency module:location mappings used to populate the license metadata for - // apex containers. - licenseInstallMap []string - - // The path to the generated license metadata file for the module. - licenseMetadataFile WritablePath - - // moduleInfoJSON can be filled out by GenerateAndroidBuildActions to write a JSON file that will - // be included in the final module-info.json produced by Make. - moduleInfoJSON *ModuleInfoJSON - - // outputFiles stores the output of a module by tag and is used to set - // the OutputFilesProvider in GenerateBuildActions - outputFiles OutputFilesInfo } func (m *ModuleBase) AddJSONData(d *map[string]interface{}) { @@ -1055,6 +973,7 @@ func (m *ModuleBase) baseDepsMutator(ctx BottomUpMutatorContext) { fullManifest := pv.DeviceArch != nil && pv.DeviceName != nil if fullManifest { addRequiredDeps(ctx) + addVintfFragmentDeps(ctx) } } @@ -1132,6 +1051,16 @@ func addRequiredDeps(ctx BottomUpMutatorContext) { } } +var vintfDepTag = struct { + blueprint.BaseDependencyTag + InstallAlwaysNeededDependencyTag +}{} + +func addVintfFragmentDeps(ctx BottomUpMutatorContext) { + mod := ctx.Module() + ctx.AddDependency(mod, vintfDepTag, mod.VintfFragmentModuleNames(ctx)...) +} + // AddProperties "registers" the provided props // each value in props MUST be a pointer to a struct func (m *ModuleBase) AddProperties(props ...interface{}) { @@ -1235,27 +1164,16 @@ func (m *ModuleBase) GenerateTaggedDistFiles(ctx BaseModuleContext) TaggedDistFi // the special tag name which represents that. tag := proptools.StringDefault(dist.Tag, DefaultDistTag) - if outputFileProducer, ok := m.module.(OutputFileProducer); ok { - // Call the OutputFiles(tag) method to get the paths associated with the tag. - distFilesForTag, err := outputFileProducer.OutputFiles(tag) - - // If the tag was not supported and is not DefaultDistTag then it is an error. - // Failing to find paths for DefaultDistTag is not an error. It just means - // that the module type requires the legacy behavior. + distFileForTagFromProvider, err := outputFilesForModuleFromProvider(ctx, m.module, tag) + if err != OutputFilesProviderNotSet { if err != nil && tag != DefaultDistTag { ctx.PropertyErrorf("dist.tag", "%s", err.Error()) + } else { + distFiles = distFiles.addPathsForTag(tag, distFileForTagFromProvider...) + continue } - - distFiles = distFiles.addPathsForTag(tag, distFilesForTag...) - } else if tag != DefaultDistTag { - // If the tag was specified then it is an error if the module does not - // implement OutputFileProducer because there is no other way of accessing - // the paths for the specified tag. - ctx.PropertyErrorf("dist.tag", - "tag %s not supported because the module does not implement OutputFileProducer", tag) } } - return distFiles } @@ -1297,7 +1215,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. @@ -1347,7 +1265,11 @@ func (m *ModuleBase) HostCrossSupported() bool { // hostEnabled is true if the host_supported property is true or the HostOrDeviceSupported // value has the hostDefault bit set. hostEnabled := proptools.BoolDefault(m.hostAndDeviceProperties.Host_supported, hod&hostDefault != 0) - return hod&hostCrossSupported != 0 && hostEnabled + + // Default true for the Host_cross_supported property + hostCrossEnabled := proptools.BoolDefault(m.hostCrossProperties.Host_cross_supported, true) + + return hod&hostCrossSupported != 0 && hostEnabled && hostCrossEnabled } func (m *ModuleBase) Platform() bool { @@ -1411,13 +1333,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 } @@ -1487,12 +1417,13 @@ func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]*DepSet[InstallPat if isInstallDepNeeded(dep, ctx.OtherModuleDependencyTag(dep)) { // Installation is still handled by Make, so anything hidden from Make is not // installable. + info := OtherModuleProviderOrDefault(ctx, dep, InstallFilesProvider) if !dep.IsHideFromMake() && !dep.IsSkipInstall() { - installDeps = append(installDeps, dep.base().installFilesDepSet) + installDeps = append(installDeps, info.TransitiveInstallFiles) } // Add packaging deps even when the dependency is not installed so that uninstallable // modules can still be packaged. Often the package will be installed instead. - packagingSpecs = append(packagingSpecs, dep.base().packagingSpecsDepSet) + packagingSpecs = append(packagingSpecs, info.TransitivePackagingSpecs) } }) @@ -1510,18 +1441,6 @@ func isInstallDepNeeded(dep Module, tag blueprint.DependencyTag) bool { return IsInstallDepNeededTag(tag) } -func (m *ModuleBase) FilesToInstall() InstallPaths { - return m.installFiles -} - -func (m *ModuleBase) PackagingSpecs() []PackagingSpec { - return m.packagingSpecs -} - -func (m *ModuleBase) TransitivePackagingSpecs() []PackagingSpec { - return m.packagingSpecsDepSet.ToList() -} - func (m *ModuleBase) NoAddressSanitizer() bool { return m.noAddressSanitizer } @@ -1619,7 +1538,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) } @@ -1631,58 +1550,71 @@ func (m *ModuleBase) TargetRequiredModuleNames() []string { return m.base().commonProperties.Target_required } -func (m *ModuleBase) InitRc() Paths { - return append(Paths{}, m.initRcPaths...) +func (m *ModuleBase) VintfFragmentModuleNames(ctx ConfigurableEvaluatorContext) []string { + return m.base().commonProperties.Vintf_fragment_modules.GetOrDefault(m.ConfigurableEvaluator(ctx), nil) } -func (m *ModuleBase) VintfFragments() Paths { - return append(Paths{}, m.vintfFragmentsPaths...) -} - -func (m *ModuleBase) CompileMultilib() *string { - return m.base().commonProperties.Compile_multilib -} - -// SetLicenseInstallMap stores the set of dependency module:location mappings for files in an -// apex container for use when generation the license metadata file. -func (m *ModuleBase) SetLicenseInstallMap(installMap []string) { - m.licenseInstallMap = append(m.licenseInstallMap, installMap...) -} - -func (m *ModuleBase) generateModuleTarget(ctx ModuleContext) { - var allInstalledFiles InstallPaths - var allCheckbuildFiles Paths - ctx.VisitAllModuleVariants(func(module Module) { - a := module.base() - allInstalledFiles = append(allInstalledFiles, a.installFiles...) - // A module's -checkbuild phony targets should - // not be created if the module is not exported to make. - // Those could depend on the build target and fail to compile - // for the current build target. - if !ctx.Config().KatiEnabled() || !shouldSkipAndroidMkProcessing(ctx, a) { - allCheckbuildFiles = append(allCheckbuildFiles, a.checkbuildFiles...) - } - }) - - var deps Paths - +func (m *ModuleBase) generateVariantTarget(ctx *moduleContext) { namespacePrefix := ctx.Namespace().id if namespacePrefix != "" { namespacePrefix = namespacePrefix + "-" } + if !ctx.uncheckedModule { + name := namespacePrefix + ctx.ModuleName() + "-" + ctx.ModuleSubDir() + "-checkbuild" + ctx.Phony(name, ctx.checkbuildFiles...) + ctx.checkbuildTarget = PathForPhony(ctx, name) + } + +} + +func (m *ModuleBase) generateModuleTarget(ctx *moduleContext) { + var allInstalledFiles InstallPaths + var allCheckbuildTargets Paths + ctx.VisitAllModuleVariants(func(module Module) { + a := module.base() + var checkbuildTarget Path + var uncheckedModule bool + if a == m { + allInstalledFiles = append(allInstalledFiles, ctx.installFiles...) + checkbuildTarget = ctx.checkbuildTarget + uncheckedModule = ctx.uncheckedModule + } else { + info := OtherModuleProviderOrDefault(ctx, module, InstallFilesProvider) + allInstalledFiles = append(allInstalledFiles, info.InstallFiles...) + checkbuildTarget = info.CheckbuildTarget + uncheckedModule = info.UncheckedModule + } + // A module's -checkbuild phony targets should + // not be created if the module is not exported to make. + // Those could depend on the build target and fail to compile + // for the current build target. + if (!ctx.Config().KatiEnabled() || !shouldSkipAndroidMkProcessing(ctx, a)) && !uncheckedModule && checkbuildTarget != nil { + allCheckbuildTargets = append(allCheckbuildTargets, checkbuildTarget) + } + }) + + var deps Paths + + var namespacePrefix string + nameSpace := ctx.Namespace().Path + if nameSpace != "." { + namespacePrefix = strings.ReplaceAll(nameSpace, "/", ".") + "-" + } + + var info FinalModuleBuildTargetsInfo + if len(allInstalledFiles) > 0 { name := namespacePrefix + ctx.ModuleName() + "-install" ctx.Phony(name, allInstalledFiles.Paths()...) - m.installTarget = PathForPhony(ctx, name) - deps = append(deps, m.installTarget) + info.InstallTarget = PathForPhony(ctx, name) + deps = append(deps, info.InstallTarget) } - if len(allCheckbuildFiles) > 0 { + if len(allCheckbuildTargets) > 0 { name := namespacePrefix + ctx.ModuleName() + "-checkbuild" - ctx.Phony(name, allCheckbuildFiles...) - m.checkbuildTarget = PathForPhony(ctx, name) - deps = append(deps, m.checkbuildTarget) + ctx.Phony(name, allCheckbuildTargets...) + deps = append(deps, PathForPhony(ctx, name)) } if len(deps) > 0 { @@ -1693,7 +1625,8 @@ func (m *ModuleBase) generateModuleTarget(ctx ModuleContext) { ctx.Phony(namespacePrefix+ctx.ModuleName()+suffix, deps...) - m.blueprintDir = ctx.ModuleDir() + info.BlueprintDir = ctx.ModuleDir() + SetProvider(ctx, FinalModuleBuildTargetsProvider, info) } } @@ -1772,7 +1705,11 @@ func (m *ModuleBase) baseModuleContextFactory(ctx blueprint.BaseModuleContext) b } } -func (m *ModuleBase) archModuleContextFactory(ctx blueprint.IncomingTransitionContext) archModuleContext { +type archModuleContextFactoryContext interface { + Config() interface{} +} + +func (m *ModuleBase) archModuleContextFactory(ctx archModuleContextFactoryContext) archModuleContext { config := ctx.Config().(Config) target := m.Target() primaryArch := false @@ -1793,21 +1730,69 @@ func (m *ModuleBase) archModuleContextFactory(ctx blueprint.IncomingTransitionCo } +type InstallFilesInfo struct { + InstallFiles InstallPaths + CheckbuildFiles Paths + CheckbuildTarget Path + UncheckedModule bool + PackagingSpecs []PackagingSpec + // katiInstalls tracks the install rules that were created by Soong but are being exported + // to Make to convert to ninja rules so that Make can add additional dependencies. + KatiInstalls katiInstalls + KatiSymlinks katiInstalls + TestData []DataPath + TransitivePackagingSpecs *DepSet[PackagingSpec] + LicenseMetadataFile WritablePath + + // The following fields are private before, make it private again once we have + // better solution. + TransitiveInstallFiles *DepSet[InstallPath] + // katiInitRcInstalls and katiVintfInstalls track the install rules created by Soong that are + // allowed to have duplicates across modules and variants. + KatiInitRcInstalls katiInstalls + KatiVintfInstalls katiInstalls + InitRcPaths Paths + VintfFragmentsPaths Paths + InstalledInitRcPaths InstallPaths + InstalledVintfFragmentsPaths InstallPaths + + // The files to copy to the dist as explicitly specified in the .bp file. + DistFiles TaggedDistFiles +} + +var InstallFilesProvider = blueprint.NewProvider[InstallFilesInfo]() + +type FinalModuleBuildTargetsInfo struct { + // Used by buildTargetSingleton to create checkbuild and per-directory build targets + // Only set on the final variant of each module + InstallTarget WritablePath + CheckbuildTarget WritablePath + BlueprintDir string +} + +var FinalModuleBuildTargetsProvider = blueprint.NewProvider[FinalModuleBuildTargetsInfo]() + func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) { ctx := &moduleContext{ module: m.module, bp: blueprintCtx, baseModuleContext: m.baseModuleContextFactory(blueprintCtx), variables: make(map[string]string), + phonies: make(map[string]Paths), } - m.licenseMetadataFile = PathForModuleOut(ctx, "meta_lic") + setContainerInfo(ctx) + if ctx.Config().Getenv("DISABLE_CONTAINER_CHECK") != "true" { + checkContainerViolations(ctx) + } + + ctx.licenseMetadataFile = PathForModuleOut(ctx, "meta_lic") dependencyInstallFiles, dependencyPackagingSpecs := m.computeInstallDeps(ctx) - // set m.installFilesDepSet to only the transitive dependencies to be used as the dependencies + // set the TransitiveInstallFiles to only the transitive dependencies to be used as the dependencies // of installed files of this module. It will be replaced by a depset including the installed // files of this module at the end for use by modules that depend on this one. - m.installFilesDepSet = NewDepSet[InstallPath](TOPOLOGICAL, nil, dependencyInstallFiles) + ctx.TransitiveInstallFiles = NewDepSet[InstallPath](TOPOLOGICAL, nil, dependencyInstallFiles) // Temporarily continue to call blueprintCtx.GetMissingDependencies() to maintain the previous behavior of never // reporting missing dependency errors in Blueprint when AllowMissingDependencies == true. @@ -1851,6 +1836,8 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) checkDistProperties(ctx, fmt.Sprintf("dists[%d]", i), &m.distProperties.Dists[i]) } + var installFiles InstallFilesInfo + if m.Enabled(ctx) { // ensure all direct android.Module deps are enabled ctx.VisitDirectDepsBlueprint(func(bm blueprint.Module) { @@ -1868,30 +1855,36 @@ 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() { - m.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 m.initRcPaths { + for _, src := range ctx.initRcPaths { installedInitRc := rcDir.Join(ctx, src.Base()) - m.katiInitRcInstalls = append(m.katiInitRcInstalls, katiInstall{ + ctx.katiInitRcInstalls = append(ctx.katiInitRcInstalls, katiInstall{ from: src, to: installedInitRc, }) ctx.PackageFile(rcDir, src.Base(), src) - m.installedInitRcPaths = append(m.installedInitRcPaths, installedInitRc) + ctx.installedInitRcPaths = append(ctx.installedInitRcPaths, installedInitRc) } + installFiles.InitRcPaths = ctx.initRcPaths + installFiles.KatiInitRcInstalls = ctx.katiInitRcInstalls + installFiles.InstalledInitRcPaths = ctx.installedInitRcPaths } - m.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments.GetOrDefault(ctx, nil)) + ctx.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments.GetOrDefault(ctx, nil)) vintfDir := PathForModuleInstall(ctx, "etc", "vintf", "manifest") - for _, src := range m.vintfFragmentsPaths { + for _, src := range ctx.vintfFragmentsPaths { installedVintfFragment := vintfDir.Join(ctx, src.Base()) - m.katiVintfInstalls = append(m.katiVintfInstalls, katiInstall{ + ctx.katiVintfInstalls = append(ctx.katiVintfInstalls, katiInstall{ from: src, to: installedVintfFragment, }) ctx.PackageFile(vintfDir, src.Base(), src) - m.installedVintfFragmentsPaths = append(m.installedVintfFragmentsPaths, installedVintfFragment) + ctx.installedVintfFragmentsPaths = append(ctx.installedVintfFragmentsPaths, installedVintfFragment) } + installFiles.VintfFragmentsPaths = ctx.vintfFragmentsPaths + installFiles.KatiVintfInstalls = ctx.katiVintfInstalls + installFiles.InstalledVintfFragmentsPaths = ctx.installedVintfFragmentsPaths } licensesPropertyFlattener(ctx) @@ -1918,21 +1911,33 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) return } + if x, ok := m.module.(IDEInfo); ok { + var result IdeInfo + x.IDEInfo(ctx, &result) + result.BaseModuleName = x.BaseModuleName() + SetProvider(ctx, IdeInfoProviderKey, result) + } + // Create the set of tagged dist files after calling GenerateAndroidBuildActions // as GenerateTaggedDistFiles() calls OutputFiles(tag) and so relies on the // output paths being set which must be done before or during // GenerateAndroidBuildActions. - m.distFiles = m.GenerateTaggedDistFiles(ctx) + installFiles.DistFiles = m.GenerateTaggedDistFiles(ctx) if ctx.Failed() { return } - m.installFiles = append(m.installFiles, ctx.installFiles...) - m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...) - m.packagingSpecs = append(m.packagingSpecs, ctx.packagingSpecs...) - m.katiInstalls = append(m.katiInstalls, ctx.katiInstalls...) - m.katiSymlinks = append(m.katiSymlinks, ctx.katiSymlinks...) - m.testData = append(m.testData, ctx.testData...) + m.generateVariantTarget(ctx) + + installFiles.LicenseMetadataFile = ctx.licenseMetadataFile + installFiles.InstallFiles = ctx.installFiles + installFiles.CheckbuildFiles = ctx.checkbuildFiles + installFiles.CheckbuildTarget = ctx.checkbuildTarget + installFiles.UncheckedModule = ctx.uncheckedModule + installFiles.PackagingSpecs = ctx.packagingSpecs + installFiles.KatiInstalls = ctx.katiInstalls + installFiles.KatiSymlinks = ctx.katiSymlinks + installFiles.TestData = ctx.testData } else if ctx.Config().AllowMissingDependencies() { // If the module is not enabled it will not create any build rules, nothing will call // ctx.GetMissingDependencies(), and blueprint will consider the missing dependencies to be unhandled @@ -1948,17 +1953,19 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) } } - m.installFilesDepSet = NewDepSet[InstallPath](TOPOLOGICAL, m.installFiles, dependencyInstallFiles) - m.packagingSpecsDepSet = NewDepSet[PackagingSpec](TOPOLOGICAL, m.packagingSpecs, dependencyPackagingSpecs) + ctx.TransitiveInstallFiles = NewDepSet[InstallPath](TOPOLOGICAL, ctx.installFiles, dependencyInstallFiles) + installFiles.TransitiveInstallFiles = ctx.TransitiveInstallFiles + installFiles.TransitivePackagingSpecs = NewDepSet[PackagingSpec](TOPOLOGICAL, ctx.packagingSpecs, dependencyPackagingSpecs) - buildLicenseMetadata(ctx, m.licenseMetadataFile) + SetProvider(ctx, InstallFilesProvider, installFiles) + buildLicenseMetadata(ctx, ctx.licenseMetadataFile) - if m.moduleInfoJSON != nil { + if ctx.moduleInfoJSON != nil { var installed InstallPaths - installed = append(installed, m.katiInstalls.InstallPaths()...) - installed = append(installed, m.katiSymlinks.InstallPaths()...) - installed = append(installed, m.katiInitRcInstalls.InstallPaths()...) - installed = append(installed, m.katiVintfInstalls.InstallPaths()...) + installed = append(installed, ctx.katiInstalls.InstallPaths()...) + installed = append(installed, ctx.katiSymlinks.InstallPaths()...) + installed = append(installed, ctx.katiInitRcInstalls.InstallPaths()...) + installed = append(installed, ctx.katiVintfInstalls.InstallPaths()...) installedStrings := installed.Strings() var targetRequired, hostRequired []string @@ -1969,41 +1976,49 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) } var data []string - for _, d := range m.testData { + for _, d := range ctx.testData { data = append(data, d.ToRelativeInstallPath()) } - if m.moduleInfoJSON.Uninstallable { + if ctx.moduleInfoJSON.Uninstallable { installedStrings = nil - if len(m.moduleInfoJSON.CompatibilitySuites) == 1 && m.moduleInfoJSON.CompatibilitySuites[0] == "null-suite" { - m.moduleInfoJSON.CompatibilitySuites = nil - m.moduleInfoJSON.TestConfig = nil - m.moduleInfoJSON.AutoTestConfig = nil + if len(ctx.moduleInfoJSON.CompatibilitySuites) == 1 && ctx.moduleInfoJSON.CompatibilitySuites[0] == "null-suite" { + ctx.moduleInfoJSON.CompatibilitySuites = nil + ctx.moduleInfoJSON.TestConfig = nil + ctx.moduleInfoJSON.AutoTestConfig = nil data = nil } } - m.moduleInfoJSON.core = CoreModuleInfoJSON{ - RegisterName: m.moduleInfoRegisterName(ctx, m.moduleInfoJSON.SubName), + ctx.moduleInfoJSON.core = CoreModuleInfoJSON{ + RegisterName: m.moduleInfoRegisterName(ctx, ctx.moduleInfoJSON.SubName), Path: []string{ctx.ModuleDir()}, Installed: installedStrings, - ModuleName: m.BaseModuleName() + m.moduleInfoJSON.SubName, + ModuleName: m.BaseModuleName() + ctx.moduleInfoJSON.SubName, SupportedVariants: []string{m.moduleInfoVariant(ctx)}, TargetDependencies: targetRequired, HostDependencies: hostRequired, Data: data, - Required: m.RequiredModuleNames(ctx), + Required: append(m.RequiredModuleNames(ctx), m.VintfFragmentModuleNames(ctx)...), } - SetProvider(ctx, ModuleInfoJSONProvider, m.moduleInfoJSON) + SetProvider(ctx, ModuleInfoJSONProvider, ctx.moduleInfoJSON) } m.buildParams = ctx.buildParams m.ruleParams = ctx.ruleParams m.variables = ctx.variables - if m.outputFiles.DefaultOutputFiles != nil || m.outputFiles.TaggedOutputFiles != nil { - SetProvider(ctx, OutputFilesProvider, m.outputFiles) + outputFiles := ctx.GetOutputFiles() + if outputFiles.DefaultOutputFiles != nil || outputFiles.TaggedOutputFiles != nil { + SetProvider(ctx, OutputFilesProvider, outputFiles) } + + if len(ctx.phonies) > 0 { + SetProvider(ctx, ModulePhonyProvider, ModulePhonyInfo{ + Phonies: ctx.phonies, + }) + } + buildComplianceMetadataProvider(ctx, m) } func SetJarJarPrefixHandler(handler func(ModuleContext)) { @@ -2087,11 +2102,61 @@ type katiInstall struct { absFrom string } +func (p *katiInstall) GobEncode() ([]byte, error) { + w := new(bytes.Buffer) + encoder := gob.NewEncoder(w) + err := errors.Join(encoder.Encode(p.from), encoder.Encode(p.to), + encoder.Encode(p.implicitDeps), encoder.Encode(p.orderOnlyDeps), + encoder.Encode(p.executable), encoder.Encode(p.extraFiles), + encoder.Encode(p.absFrom)) + if err != nil { + return nil, err + } + + return w.Bytes(), nil +} + +func (p *katiInstall) GobDecode(data []byte) error { + r := bytes.NewBuffer(data) + decoder := gob.NewDecoder(r) + err := errors.Join(decoder.Decode(&p.from), decoder.Decode(&p.to), + decoder.Decode(&p.implicitDeps), decoder.Decode(&p.orderOnlyDeps), + decoder.Decode(&p.executable), decoder.Decode(&p.extraFiles), + decoder.Decode(&p.absFrom)) + if err != nil { + return err + } + + return nil +} + type extraFilesZip struct { zip Path dir InstallPath } +func (p *extraFilesZip) GobEncode() ([]byte, error) { + w := new(bytes.Buffer) + encoder := gob.NewEncoder(w) + err := errors.Join(encoder.Encode(p.zip), encoder.Encode(p.dir)) + if err != nil { + return nil, err + } + + return w.Bytes(), nil +} + +func (p *extraFilesZip) GobDecode(data []byte) error { + r := bytes.NewBuffer(data) + decoder := gob.NewDecoder(r) + err := errors.Join(decoder.Decode(&p.zip), decoder.Decode(&p.dir)) + if err != nil { + return err + } + + return nil +} + type katiInstalls []katiInstall // BuiltInstalled returns the katiInstalls in the form used by $(call copy-many-files) in Make, a @@ -2141,17 +2206,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, @@ -2165,6 +2236,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 { @@ -2361,7 +2438,7 @@ type sourceOrOutputDependencyTag struct { // The name of the module. moduleName string - // The tag that will be passed to the module's OutputFileProducer.OutputFiles(tag) method. + // The tag that will be used to get the specific output file(s). tag string } @@ -2415,14 +2492,7 @@ type SourceFileProducer interface { Srcs() Paths } -// A module that implements OutputFileProducer can be referenced from any property that is tagged with `android:"path"` -// using the ":module" syntax or ":module{.tag}" syntax and provides a list of output files to be used as if they were -// listed in the property. -type OutputFileProducer interface { - OutputFiles(tag string) (Paths, error) -} - -// OutputFilesForModule returns the paths from an OutputFileProducer with the given tag. On error, including if the +// OutputFilesForModule returns the output file paths with the given tag. On error, including if the // module produced zero paths, it reports errors to the ctx and returns nil. func OutputFilesForModule(ctx PathContext, module blueprint.Module, tag string) Paths { paths, err := outputFilesForModule(ctx, module, tag) @@ -2433,7 +2503,7 @@ func OutputFilesForModule(ctx PathContext, module blueprint.Module, tag string) return paths } -// OutputFileForModule returns the path from an OutputFileProducer with the given tag. On error, including if the +// OutputFileForModule returns the output file paths with the given tag. On error, including if the // module produced zero or multiple paths, it reports errors to the ctx and returns nil. func OutputFileForModule(ctx PathContext, module blueprint.Module, tag string) Path { paths, err := outputFilesForModule(ctx, module, tag) @@ -2470,24 +2540,17 @@ func OutputFileForModule(ctx PathContext, module blueprint.Module, tag string) P func outputFilesForModule(ctx PathContext, module blueprint.Module, tag string) (Paths, error) { outputFilesFromProvider, err := outputFilesForModuleFromProvider(ctx, module, tag) - if outputFilesFromProvider != nil || err != nil { + if outputFilesFromProvider != nil || err != OutputFilesProviderNotSet { return outputFilesFromProvider, err } - if outputFileProducer, ok := module.(OutputFileProducer); ok { - paths, err := outputFileProducer.OutputFiles(tag) - if err != nil { - return nil, fmt.Errorf("failed to get output file from module %q at tag %q: %s", - pathContextName(ctx, module), tag, err.Error()) - } - return paths, nil - } else if sourceFileProducer, ok := module.(SourceFileProducer); ok { + if sourceFileProducer, ok := module.(SourceFileProducer); ok { if tag != "" { - return nil, fmt.Errorf("module %q is a SourceFileProducer, not an OutputFileProducer, and so does not support tag %q", pathContextName(ctx, module), tag) + return nil, fmt.Errorf("module %q is a SourceFileProducer, which does not support tag %q", pathContextName(ctx, module), tag) } paths := sourceFileProducer.Srcs() return paths, nil } else { - return nil, fmt.Errorf("module %q is not an OutputFileProducer or SourceFileProducer", pathContextName(ctx, module)) + return nil, fmt.Errorf("module %q is not a SourceFileProducer or having valid output file for tag %q", pathContextName(ctx, module), tag) } } @@ -2495,34 +2558,51 @@ func outputFilesForModule(ctx PathContext, module blueprint.Module, tag string) // *inter-module-communication*. // If mctx module is the same as the param module the output files are obtained // from outputFiles property of module base, to avoid both setting and -// reading OutputFilesProvider before GenerateBuildActions is finished. Also -// only empty-string-tag is supported in this case. +// reading OutputFilesProvider before GenerateBuildActions is finished. // If a module doesn't have the OutputFilesProvider, nil is returned. func outputFilesForModuleFromProvider(ctx PathContext, module blueprint.Module, tag string) (Paths, error) { - // TODO: support OutputFilesProvider for singletons - mctx, ok := ctx.(ModuleContext) - if !ok { - return nil, nil + var outputFiles OutputFilesInfo + fromProperty := false + + type OutputFilesProviderModuleContext interface { + OtherModuleProviderContext + Module() Module + GetOutputFiles() OutputFilesInfo } - if mctx.Module() != module { - if outputFilesProvider, ok := OtherModuleProvider(mctx, module, OutputFilesProvider); ok { - if tag == "" { - return outputFilesProvider.DefaultOutputFiles, nil - } else if taggedOutputFiles, hasTag := outputFilesProvider.TaggedOutputFiles[tag]; hasTag { - return taggedOutputFiles, nil - } else { - return nil, fmt.Errorf("unsupported module reference tag %q", tag) - } - } - } else { - if tag == "" { - return mctx.Module().base().outputFiles.DefaultOutputFiles, nil + + if mctx, isMctx := ctx.(OutputFilesProviderModuleContext); isMctx { + if mctx.Module() != module { + outputFiles, _ = OtherModuleProvider(mctx, module, OutputFilesProvider) } else { + outputFiles = mctx.GetOutputFiles() + fromProperty = true + } + } else if cta, isCta := ctx.(*singletonContextAdaptor); isCta { + providerData, _ := cta.otherModuleProvider(module, OutputFilesProvider) + outputFiles, _ = providerData.(OutputFilesInfo) + } else { + return nil, fmt.Errorf("unsupported context %q in method outputFilesForModuleFromProvider", reflect.TypeOf(ctx)) + } + + if outputFiles.isEmpty() { + return nil, OutputFilesProviderNotSet + } + + if tag == "" { + return outputFiles.DefaultOutputFiles, nil + } else if taggedOutputFiles, hasTag := outputFiles.TaggedOutputFiles[tag]; hasTag { + return taggedOutputFiles, nil + } else { + if fromProperty { return nil, fmt.Errorf("unsupported tag %q for module getting its own output files", tag) + } else { + return nil, fmt.Errorf("unsupported module reference tag %q", tag) } } - // TODO: Add a check for param module not having OutputFilesProvider set - return nil, nil +} + +func (o OutputFilesInfo) isEmpty() bool { + return o.DefaultOutputFiles == nil && o.TaggedOutputFiles == nil } type OutputFilesInfo struct { @@ -2535,6 +2615,9 @@ type OutputFilesInfo struct { var OutputFilesProvider = blueprint.NewProvider[OutputFilesInfo]() +// This is used to mark the case where OutputFilesProvider is not set on some modules. +var OutputFilesProviderNotSet = fmt.Errorf("No output files from provider") + // Modules can implement HostToolProvider and return a valid OptionalPath from HostToolPath() to // specify that they can be used as a tool by a genrule module. type HostToolProvider interface { @@ -2546,8 +2629,6 @@ type HostToolProvider interface { func init() { RegisterParallelSingletonType("buildtarget", BuildTargetSingleton) - RegisterParallelSingletonType("soongconfigtrace", soongConfigTraceSingletonFunc) - FinalDepsMutators(registerSoongConfigTraceMutator) } func BuildTargetSingleton() Singleton { @@ -2599,17 +2680,15 @@ func (c *buildTargetSingleton) GenerateBuildActions(ctx SingletonContext) { modulesInDir := make(map[string]Paths) ctx.VisitAllModules(func(module Module) { - blueprintDir := module.base().blueprintDir - installTarget := module.base().installTarget - checkbuildTarget := module.base().checkbuildTarget + info := OtherModuleProviderOrDefault(ctx, module, FinalModuleBuildTargetsProvider) - if checkbuildTarget != nil { - checkbuildDeps = append(checkbuildDeps, checkbuildTarget) - modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], checkbuildTarget) + if info.CheckbuildTarget != nil { + checkbuildDeps = append(checkbuildDeps, info.CheckbuildTarget) + modulesInDir[info.BlueprintDir] = append(modulesInDir[info.BlueprintDir], info.CheckbuildTarget) } - if installTarget != nil { - modulesInDir[blueprintDir] = append(modulesInDir[blueprintDir], installTarget) + if info.InstallTarget != nil { + modulesInDir[info.BlueprintDir] = append(modulesInDir[info.BlueprintDir], info.InstallTarget) } }) @@ -2644,7 +2723,7 @@ func (c *buildTargetSingleton) GenerateBuildActions(ctx SingletonContext) { ctx.VisitAllModules(func(module Module) { if module.Enabled(ctx) { key := osAndCross{os: module.Target().Os, hostCross: module.Target().HostCross} - osDeps[key] = append(osDeps[key], module.base().checkbuildFiles...) + osDeps[key] = append(osDeps[key], OtherModuleProviderOrDefault(ctx, module, InstallFilesProvider).CheckbuildFiles...) } }) @@ -2679,7 +2758,7 @@ func (c *buildTargetSingleton) GenerateBuildActions(ctx SingletonContext) { // Collect information for opening IDE project files in java/jdeps.go. type IDEInfo interface { - IDEInfo(ideInfo *IdeInfo) + IDEInfo(ctx BaseModuleContext, ideInfo *IdeInfo) BaseModuleName() string } @@ -2691,7 +2770,9 @@ type IDECustomizedModuleName interface { IDECustomizedModuleName() string } +// Collect information for opening IDE project files in java/jdeps.go. type IdeInfo struct { + BaseModuleName string `json:"-"` Deps []string `json:"dependencies,omitempty"` Srcs []string `json:"srcs,omitempty"` Aidl_include_dirs []string `json:"aidl_include_dirs,omitempty"` @@ -2705,58 +2786,32 @@ type IdeInfo struct { Libs []string `json:"libs,omitempty"` } +// Merge merges two IdeInfos and produces a new one, leaving the origional unchanged +func (i IdeInfo) Merge(other IdeInfo) IdeInfo { + return IdeInfo{ + Deps: mergeStringLists(i.Deps, other.Deps), + Srcs: mergeStringLists(i.Srcs, other.Srcs), + Aidl_include_dirs: mergeStringLists(i.Aidl_include_dirs, other.Aidl_include_dirs), + Jarjar_rules: mergeStringLists(i.Jarjar_rules, other.Jarjar_rules), + Jars: mergeStringLists(i.Jars, other.Jars), + Classes: mergeStringLists(i.Classes, other.Classes), + Installed_paths: mergeStringLists(i.Installed_paths, other.Installed_paths), + SrcJars: mergeStringLists(i.SrcJars, other.SrcJars), + Paths: mergeStringLists(i.Paths, other.Paths), + Static_libs: mergeStringLists(i.Static_libs, other.Static_libs), + Libs: mergeStringLists(i.Libs, other.Libs), + } +} + +// mergeStringLists appends the two string lists together and returns a new string list, +// leaving the originals unchanged. Duplicate strings will be deduplicated. +func mergeStringLists(a, b []string) []string { + return FirstUniqueStrings(Concat(a, b)) +} + +var IdeInfoProviderKey = blueprint.NewProvider[IdeInfo]() + func CheckBlueprintSyntax(ctx BaseModuleContext, filename string, contents string) []error { bpctx := ctx.blueprintBaseModuleContext() return blueprint.CheckBlueprintSyntax(bpctx.ModuleFactories(), filename, contents) } - -func registerSoongConfigTraceMutator(ctx RegisterMutatorsContext) { - ctx.BottomUp("soongconfigtrace", soongConfigTraceMutator).Parallel() -} - -// soongConfigTraceMutator accumulates recorded soong_config trace from children. Also it normalizes -// SoongConfigTrace to make it consistent. -func soongConfigTraceMutator(ctx BottomUpMutatorContext) { - trace := &ctx.Module().base().commonProperties.SoongConfigTrace - ctx.VisitDirectDeps(func(m Module) { - childTrace := &m.base().commonProperties.SoongConfigTrace - trace.Bools = append(trace.Bools, childTrace.Bools...) - trace.Strings = append(trace.Strings, childTrace.Strings...) - trace.IsSets = append(trace.IsSets, childTrace.IsSets...) - }) - trace.Bools = SortedUniqueStrings(trace.Bools) - trace.Strings = SortedUniqueStrings(trace.Strings) - trace.IsSets = SortedUniqueStrings(trace.IsSets) - - ctx.Module().base().commonProperties.SoongConfigTraceHash = trace.hash() -} - -// soongConfigTraceSingleton writes a map from each module's config hash value to trace data. -func soongConfigTraceSingletonFunc() Singleton { - return &soongConfigTraceSingleton{} -} - -type soongConfigTraceSingleton struct { -} - -func (s *soongConfigTraceSingleton) GenerateBuildActions(ctx SingletonContext) { - outFile := PathForOutput(ctx, "soong_config_trace.json") - - traces := make(map[string]*soongConfigTrace) - ctx.VisitAllModules(func(module Module) { - trace := &module.base().commonProperties.SoongConfigTrace - if !trace.isEmpty() { - hash := module.base().commonProperties.SoongConfigTraceHash - traces[hash] = trace - } - }) - - j, err := json.Marshal(traces) - if err != nil { - ctx.Errorf("json marshal to %q failed: %#v", outFile, err) - return - } - - WriteFileRule(ctx, outFile, string(j)) - ctx.Phony("soong_config_trace", outFile) -} diff --git a/android/module_context.go b/android/module_context.go index e2677a4f6..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) @@ -107,68 +109,79 @@ type ModuleContext interface { // InstallExecutable creates a rule to copy srcPath to name in the installPath directory, // with the given additional dependencies. The file is marked executable after copying. // - // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. + // The installed file can be accessed by InstallFilesInfo.InstallFiles, and the PackagingSpec + // for the installed file can be accessed by InstallFilesInfo.PackagingSpecs on this module + // or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through + // dependency tags for which IsInstallDepNeeded returns true. InstallExecutable(installPath InstallPath, name string, srcPath Path, deps ...InstallPath) InstallPath // InstallFile creates a rule to copy srcPath to name in the installPath directory, // with the given additional dependencies. // + // The installed file can be accessed by InstallFilesInfo.InstallFiles, and the PackagingSpec + // for the installed file can be accessed by InstallFilesInfo.PackagingSpecs on this module + // or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through + // dependency tags for which IsInstallDepNeeded returns true. + InstallFile(installPath InstallPath, name string, srcPath Path, deps ...InstallPath) InstallPath + + // InstallFileWithoutCheckbuild creates a rule to copy srcPath to name in the installPath directory, + // with the given additional dependencies, but does not add the file to the list of files to build + // during `m checkbuild`. + // // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the // installed file will be returned by PackagingSpecs() on this module or by // TransitivePackagingSpecs() on modules that depend on this module through dependency tags // for which IsInstallDepNeeded returns true. - InstallFile(installPath InstallPath, name string, srcPath Path, deps ...InstallPath) InstallPath + InstallFileWithoutCheckbuild(installPath InstallPath, name string, srcPath Path, deps ...InstallPath) InstallPath // InstallFileWithExtraFilesZip creates a rule to copy srcPath to name in the installPath // directory, and also unzip a zip file containing extra files to install into the same // directory. // - // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. + // The installed file can be accessed by InstallFilesInfo.InstallFiles, and the PackagingSpec + // for the installed file can be accessed by InstallFilesInfo.PackagingSpecs on this module + // or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through + // dependency tags for which IsInstallDepNeeded returns true. InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path, extraZip Path, deps ...InstallPath) InstallPath // InstallSymlink creates a rule to create a symlink from src srcPath to name in the installPath // directory. // - // The installed symlink will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. + // The installed symlink can be accessed by InstallFilesInfo.InstallFiles, and the PackagingSpec + // for the installed file can be accessed by InstallFilesInfo.PackagingSpecs on this module + // or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through + // dependency tags for which IsInstallDepNeeded returns true. InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath // InstallAbsoluteSymlink creates a rule to create an absolute symlink from src srcPath to name // in the installPath directory. // - // The installed symlink will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. + // The installed symlink can be accessed by InstallFilesInfo.InstallFiles, and the PackagingSpec + // for the installed file can be accessed by InstallFilesInfo.PackagingSpecs on this module + // or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through + // dependency tags for which IsInstallDepNeeded returns true. InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath // InstallTestData creates rules to install test data (e.g. data files used during a test) into // the installPath directory. // - // The installed files will be returned by FilesToInstall(), and the PackagingSpec for the - // installed files will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. + // The installed files can be accessed by InstallFilesInfo.InstallFiles, and the PackagingSpec + // for the installed files can be accessed by InstallFilesInfo.PackagingSpecs on this module + // or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through + // dependency tags for which IsInstallDepNeeded returns true. InstallTestData(installPath InstallPath, data []DataPath) InstallPaths // PackageFile creates a PackagingSpec as if InstallFile was called, but without creating // the rule to copy the file. This is useful to define how a module would be packaged // without installing it into the global installation directories. // - // The created PackagingSpec for the will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. + // The created PackagingSpec can be accessed by InstallFilesInfo.PackagingSpecs on this module + // or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through + // dependency tags for which IsInstallDepNeeded returns true. PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec - CheckbuildFile(srcPath Path) + CheckbuildFile(srcPaths ...Path) + UncheckedModule() InstallInData() bool InstallInTestcases() bool @@ -183,12 +196,11 @@ type ModuleContext interface { InstallInVendor() bool InstallForceOS() (*OsType, *ArchType) - RequiredModuleNames(ctx ConfigAndErrorContext) []string + RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string HostRequiredModuleNames() []string TargetRequiredModuleNames() []string ModuleSubDir() string - SoongConfigTraceHash() string Variable(pctx PackageContext, name, value string) Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule @@ -216,19 +228,58 @@ type ModuleContext interface { // SetOutputFiles stores the outputFiles to outputFiles property, which is used // to set the OutputFilesProvider later. SetOutputFiles(outputFiles Paths, tag string) + + GetOutputFiles() OutputFilesInfo + + // SetLicenseInstallMap stores the set of dependency module:location mappings for files in an + // apex container for use when generation the license metadata file. + SetLicenseInstallMap(installMap []string) + + // ComplianceMetadataInfo returns a ComplianceMetadataInfo instance for different module types to dump metadata, + // which usually happens in GenerateAndroidBuildActions() of a module type. + // See android.ModuleBase.complianceMetadataInfo + ComplianceMetadataInfo() *ComplianceMetadataInfo + + // Get the information about the containers this module belongs to. + getContainersInfo() ContainersInfo + setContainersInfo(info ContainersInfo) + + setAconfigPaths(paths Paths) } type moduleContext struct { bp blueprint.ModuleContext baseModuleContext - packagingSpecs []PackagingSpec - installFiles InstallPaths - checkbuildFiles Paths - module Module - phonies map[string]Paths + packagingSpecs []PackagingSpec + installFiles InstallPaths + checkbuildFiles Paths + checkbuildTarget Path + uncheckedModule bool + module Module + phonies map[string]Paths + // outputFiles stores the output of a module by tag and is used to set + // the OutputFilesProvider in GenerateBuildActions + outputFiles OutputFilesInfo - katiInstalls []katiInstall - katiSymlinks []katiInstall + TransitiveInstallFiles *DepSet[InstallPath] + + // set of dependency module:location mappings used to populate the license metadata for + // apex containers. + licenseInstallMap []string + + // The path to the generated license metadata file for the module. + licenseMetadataFile WritablePath + + katiInstalls katiInstalls + katiSymlinks katiInstalls + // katiInitRcInstalls and katiVintfInstalls track the install rules created by Soong that are + // allowed to have duplicates across modules and variants. + katiInitRcInstalls katiInstalls + katiVintfInstalls katiInstalls + initRcPaths Paths + vintfFragmentsPaths Paths + installedInitRcPaths InstallPaths + installedVintfFragmentsPaths InstallPaths testData []DataPath @@ -236,6 +287,21 @@ type moduleContext struct { buildParams []BuildParams ruleParams map[blueprint.Rule]blueprint.RuleParams variables map[string]string + + // moduleInfoJSON can be filled out by GenerateAndroidBuildActions to write a JSON file that will + // be included in the final module-info.json produced by Make. + moduleInfoJSON *ModuleInfoJSON + + // containersInfo stores the information about the containers and the information of the + // apexes the module belongs to. + containersInfo ContainersInfo + + // Merged Aconfig files for all transitive deps. + aconfigFilePaths Paths + + // complianceMetadataInfo is for different module types to dump metadata. + // See android.ModuleContext interface. + complianceMetadataInfo *ComplianceMetadataInfo } var _ ModuleContext = &moduleContext{} @@ -357,7 +423,7 @@ func (m *moduleContext) Build(pctx PackageContext, params BuildParams) { } func (m *moduleContext) Phony(name string, deps ...Path) { - addPhony(m.config, name, deps...) + m.phonies[name] = append(m.phonies[name], deps...) } func (m *moduleContext) GetMissingDependencies() []string { @@ -377,10 +443,6 @@ func (m *moduleContext) ModuleSubDir() string { return m.bp.ModuleSubDir() } -func (m *moduleContext) SoongConfigTraceHash() string { - return m.module.base().commonProperties.SoongConfigTraceHash -} - func (m *moduleContext) InstallInData() bool { return m.module.InstallInData() } @@ -465,17 +527,22 @@ func (m *moduleContext) requiresFullInstall() bool { func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path, deps ...InstallPath) InstallPath { - return m.installFile(installPath, name, srcPath, deps, false, true, nil) + return m.installFile(installPath, name, srcPath, deps, false, true, true, nil) +} + +func (m *moduleContext) InstallFileWithoutCheckbuild(installPath InstallPath, name string, srcPath Path, + deps ...InstallPath) InstallPath { + return m.installFile(installPath, name, srcPath, deps, false, true, false, nil) } func (m *moduleContext) InstallExecutable(installPath InstallPath, name string, srcPath Path, deps ...InstallPath) InstallPath { - return m.installFile(installPath, name, srcPath, deps, true, true, nil) + return m.installFile(installPath, name, srcPath, deps, true, true, true, nil) } func (m *moduleContext) InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path, extraZip Path, deps ...InstallPath) InstallPath { - return m.installFile(installPath, name, srcPath, deps, false, true, &extraFilesZip{ + return m.installFile(installPath, name, srcPath, deps, false, true, true, &extraFilesZip{ zip: extraZip, dir: installPath, }) @@ -487,11 +554,16 @@ func (m *moduleContext) PackageFile(installPath InstallPath, name string, srcPat } func (m *moduleContext) getAconfigPaths() *Paths { - return &m.module.base().aconfigFilePaths + return &m.aconfigFilePaths +} + +func (m *moduleContext) setAconfigPaths(paths Paths) { + m.aconfigFilePaths = paths } func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, executable bool) PackagingSpec { licenseFiles := m.Module().EffectiveLicenseFiles() + overrides := CopyOf(m.Module().base().commonProperties.Overrides) spec := PackagingSpec{ relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()), srcPath: srcPath, @@ -502,13 +574,15 @@ func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, e skipInstall: m.skipInstall(), aconfigPaths: m.getAconfigPaths(), archType: m.target.Arch.ArchType, + overrides: &overrides, + owner: m.ModuleName(), } m.packagingSpecs = append(m.packagingSpecs, spec) return spec } func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path, deps []InstallPath, - executable bool, hooks bool, extraZip *extraFilesZip) InstallPath { + executable bool, hooks bool, checkbuild bool, extraZip *extraFilesZip) InstallPath { fullInstallPath := installPath.Join(m, name) if hooks { @@ -516,9 +590,9 @@ func (m *moduleContext) installFile(installPath InstallPath, name string, srcPat } if m.requiresFullInstall() { - deps = append(deps, InstallPaths(m.module.base().installFilesDepSet.ToList())...) - deps = append(deps, m.module.base().installedInitRcPaths...) - deps = append(deps, m.module.base().installedVintfFragmentsPaths...) + deps = append(deps, InstallPaths(m.TransitiveInstallFiles.ToList())...) + deps = append(deps, m.installedInitRcPaths...) + deps = append(deps, m.installedVintfFragmentsPaths...) var implicitDeps, orderOnlyDeps Paths @@ -575,7 +649,9 @@ func (m *moduleContext) installFile(installPath InstallPath, name string, srcPat m.packageFile(fullInstallPath, srcPath, executable) - m.checkbuildFiles = append(m.checkbuildFiles, srcPath) + if checkbuild { + m.checkbuildFiles = append(m.checkbuildFiles, srcPath) + } return fullInstallPath } @@ -616,9 +692,9 @@ func (m *moduleContext) InstallSymlink(installPath InstallPath, name string, src } m.installFiles = append(m.installFiles, fullInstallPath) - m.checkbuildFiles = append(m.checkbuildFiles, srcPath) } + overrides := CopyOf(m.Module().base().commonProperties.Overrides) m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{ relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()), srcPath: nil, @@ -628,6 +704,8 @@ func (m *moduleContext) InstallSymlink(installPath InstallPath, name string, src skipInstall: m.skipInstall(), aconfigPaths: m.getAconfigPaths(), archType: m.target.Arch.ArchType, + overrides: &overrides, + owner: m.ModuleName(), }) return fullInstallPath @@ -663,6 +741,7 @@ func (m *moduleContext) InstallAbsoluteSymlink(installPath InstallPath, name str m.installFiles = append(m.installFiles, fullInstallPath) } + overrides := CopyOf(m.Module().base().commonProperties.Overrides) m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{ relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()), srcPath: nil, @@ -672,6 +751,8 @@ func (m *moduleContext) InstallAbsoluteSymlink(installPath InstallPath, name str skipInstall: m.skipInstall(), aconfigPaths: m.getAconfigPaths(), archType: m.target.Arch.ArchType, + overrides: &overrides, + owner: m.ModuleName(), }) return fullInstallPath @@ -683,52 +764,73 @@ func (m *moduleContext) InstallTestData(installPath InstallPath, data []DataPath ret := make(InstallPaths, 0, len(data)) for _, d := range data { relPath := d.ToRelativeInstallPath() - installed := m.installFile(installPath, relPath, d.SrcPath, nil, false, false, nil) + installed := m.installFile(installPath, relPath, d.SrcPath, nil, false, false, true, nil) ret = append(ret, installed) } return ret } -func (m *moduleContext) CheckbuildFile(srcPath Path) { - m.checkbuildFiles = append(m.checkbuildFiles, srcPath) +// CheckbuildFile specifies the output files that should be built by checkbuild. +func (m *moduleContext) CheckbuildFile(srcPaths ...Path) { + m.checkbuildFiles = append(m.checkbuildFiles, srcPaths...) } -func (m *moduleContext) blueprintModuleContext() blueprint.ModuleContext { +// UncheckedModule marks the current module has having no files that should be built by checkbuild. +func (m *moduleContext) UncheckedModule() { + m.uncheckedModule = true +} + +func (m *moduleContext) BlueprintModuleContext() blueprint.ModuleContext { return m.bp } func (m *moduleContext) LicenseMetadataFile() Path { - return m.module.base().licenseMetadataFile + return m.licenseMetadataFile } func (m *moduleContext) ModuleInfoJSON() *ModuleInfoJSON { - if moduleInfoJSON := m.module.base().moduleInfoJSON; moduleInfoJSON != nil { + if moduleInfoJSON := m.moduleInfoJSON; moduleInfoJSON != nil { return moduleInfoJSON } moduleInfoJSON := &ModuleInfoJSON{} - m.module.base().moduleInfoJSON = moduleInfoJSON + m.moduleInfoJSON = moduleInfoJSON return moduleInfoJSON } func (m *moduleContext) SetOutputFiles(outputFiles Paths, tag string) { if tag == "" { - if len(m.module.base().outputFiles.DefaultOutputFiles) > 0 { + if len(m.outputFiles.DefaultOutputFiles) > 0 { m.ModuleErrorf("Module %s default OutputFiles cannot be overwritten", m.ModuleName()) } - m.module.base().outputFiles.DefaultOutputFiles = outputFiles + m.outputFiles.DefaultOutputFiles = outputFiles } else { - if m.module.base().outputFiles.TaggedOutputFiles == nil { - m.module.base().outputFiles.TaggedOutputFiles = make(map[string]Paths) + if m.outputFiles.TaggedOutputFiles == nil { + m.outputFiles.TaggedOutputFiles = make(map[string]Paths) } - if _, exists := m.module.base().outputFiles.TaggedOutputFiles[tag]; exists { + if _, exists := m.outputFiles.TaggedOutputFiles[tag]; exists { m.ModuleErrorf("Module %s OutputFiles at tag %s cannot be overwritten", m.ModuleName(), tag) } else { - m.module.base().outputFiles.TaggedOutputFiles[tag] = outputFiles + m.outputFiles.TaggedOutputFiles[tag] = outputFiles } } } +func (m *moduleContext) GetOutputFiles() OutputFilesInfo { + return m.outputFiles +} + +func (m *moduleContext) SetLicenseInstallMap(installMap []string) { + m.licenseInstallMap = append(m.licenseInstallMap, installMap...) +} + +func (m *moduleContext) ComplianceMetadataInfo() *ComplianceMetadataInfo { + if m.complianceMetadataInfo == nil { + m.complianceMetadataInfo = NewComplianceMetadataInfo() + } + return m.complianceMetadataInfo +} + // Returns a list of paths expanded from globs and modules referenced using ":module" syntax. The property must // be tagged with `android:"path" to support automatic source module dependency resolution. // @@ -755,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) } @@ -766,3 +868,11 @@ func (m *moduleContext) HostRequiredModuleNames() []string { func (m *moduleContext) TargetRequiredModuleNames() []string { return m.module.TargetRequiredModuleNames() } + +func (m *moduleContext) getContainersInfo() ContainersInfo { + return m.containersInfo +} + +func (m *moduleContext) setContainersInfo(info ContainersInfo) { + m.containersInfo = info +} diff --git a/android/module_test.go b/android/module_test.go index 6d8dae4c1..d64e3a591 100644 --- a/android/module_test.go +++ b/android/module_test.go @@ -722,6 +722,7 @@ test { propInfo{Name: "Arch.X86_64.A", Type: "string", Value: "x86_64 a"}, propInfo{Name: "B", Type: "bool", Value: "true"}, propInfo{Name: "C", Type: "string slice", Values: []string{"default_c", "c"}}, + propInfo{Name: "Defaults", Type: "string slice", Values: []string{"foo_defaults"}}, propInfo{Name: "Embedded_prop", Type: "string", Value: "a"}, propInfo{Name: "Name", Type: "string", Value: "foo"}, propInfo{Name: "Nested.E", Type: "string", Value: "nested e"}, @@ -933,31 +934,54 @@ func TestSetAndroidMkEntriesWithTestOptions(t *testing.T) { } } -type fakeBlueprintModule struct{} - -func (fakeBlueprintModule) Name() string { return "foo" } - -func (fakeBlueprintModule) GenerateBuildActions(blueprint.ModuleContext) {} - type sourceProducerTestModule struct { - fakeBlueprintModule - source Path + ModuleBase + props struct { + // A represents the source file + A string + } } -func (s sourceProducerTestModule) Srcs() Paths { return Paths{s.source} } - -type outputFileProducerTestModule struct { - fakeBlueprintModule - output map[string]Path - error map[string]error +func sourceProducerTestModuleFactory() Module { + module := &sourceProducerTestModule{} + module.AddProperties(&module.props) + InitAndroidModule(module) + return module } -func (o outputFileProducerTestModule) OutputFiles(tag string) (Paths, error) { - return PathsIfNonNil(o.output[tag]), o.error[tag] +func (s sourceProducerTestModule) GenerateAndroidBuildActions(ModuleContext) {} + +func (s sourceProducerTestModule) Srcs() Paths { return PathsForTesting(s.props.A) } + +type outputFilesTestModule struct { + ModuleBase + props struct { + // A represents the tag + A string + // B represents the output file for tag A + B string + } +} + +func outputFilesTestModuleFactory() Module { + module := &outputFilesTestModule{} + module.AddProperties(&module.props) + InitAndroidModule(module) + return module +} + +func (o outputFilesTestModule) GenerateAndroidBuildActions(ctx ModuleContext) { + if o.props.A != "" || o.props.B != "" { + ctx.SetOutputFiles(PathsForTesting(o.props.B), o.props.A) + } + // This is to simulate the case that some module uses an object to set its + // OutputFilesProvider, but the object itself is empty. + ctx.SetOutputFiles(Paths{}, "missing") } type pathContextAddMissingDependenciesWrapper struct { PathContext + OtherModuleProviderContext missingDeps []string } @@ -968,52 +992,91 @@ func (p *pathContextAddMissingDependenciesWrapper) OtherModuleName(module bluepr return module.Name() } +func (p *pathContextAddMissingDependenciesWrapper) Module() Module { return nil } + +func (p *pathContextAddMissingDependenciesWrapper) GetOutputFiles() OutputFilesInfo { + return OutputFilesInfo{} +} + func TestOutputFileForModule(t *testing.T) { testcases := []struct { name string - module blueprint.Module + bp string tag string - env map[string]string - config func(*config) expected string missingDeps []string + env map[string]string + config func(*config) }{ { - name: "SourceFileProducer", - module: &sourceProducerTestModule{source: PathForTesting("foo.txt")}, - expected: "foo.txt", + name: "SourceFileProducer", + bp: `spt_module { + name: "test_module", + a: "spt.txt", + } + `, + tag: "", + expected: "spt.txt", }, { - name: "OutputFileProducer", - module: &outputFileProducerTestModule{output: map[string]Path{"": PathForTesting("foo.txt")}}, - expected: "foo.txt", + name: "OutputFileProviderEmptyStringTag", + bp: `oft_module { + name: "test_module", + a: "", + b: "empty.txt", + } + `, + tag: "", + expected: "empty.txt", }, { - name: "OutputFileProducer_tag", - module: &outputFileProducerTestModule{output: map[string]Path{"foo": PathForTesting("foo.txt")}}, + name: "OutputFileProviderTag", + bp: `oft_module { + name: "test_module", + a: "foo", + b: "foo.txt", + } + `, tag: "foo", expected: "foo.txt", }, { - name: "OutputFileProducer_AllowMissingDependencies", + name: "OutputFileAllowMissingDependencies", + bp: `oft_module { + name: "test_module", + } + `, + tag: "missing", + expected: "missing_output_file/test_module", + missingDeps: []string{"test_module"}, config: func(config *config) { config.TestProductVariables.Allow_missing_dependencies = boolPtr(true) }, - module: &outputFileProducerTestModule{}, - missingDeps: []string{"foo"}, - expected: "missing_output_file/foo", }, } + for _, tt := range testcases { - config := TestConfig(buildDir, tt.env, "", nil) - if tt.config != nil { - tt.config(config.config) - } - ctx := &pathContextAddMissingDependenciesWrapper{ - PathContext: PathContextForTesting(config), - } - got := OutputFileForModule(ctx, tt.module, tt.tag) - AssertPathRelativeToTopEquals(t, "expected source path", tt.expected, got) - AssertArrayString(t, "expected missing deps", tt.missingDeps, ctx.missingDeps) + t.Run(tt.name, func(t *testing.T) { + result := GroupFixturePreparers( + PrepareForTestWithDefaults, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("spt_module", sourceProducerTestModuleFactory) + ctx.RegisterModuleType("oft_module", outputFilesTestModuleFactory) + }), + FixtureWithRootAndroidBp(tt.bp), + ).RunTest(t) + + config := TestConfig(buildDir, tt.env, tt.bp, nil) + if tt.config != nil { + tt.config(config.config) + } + ctx := &pathContextAddMissingDependenciesWrapper{ + PathContext: PathContextForTesting(config), + OtherModuleProviderContext: result.TestContext.OtherModuleProviderAdaptor(), + } + got := OutputFileForModule(ctx, result.ModuleForTests("test_module", "").Module(), tt.tag) + AssertPathRelativeToTopEquals(t, "expected output path", tt.expected, got) + AssertArrayString(t, "expected missing deps", tt.missingDeps, ctx.missingDeps) + }) } } diff --git a/android/mutator.go b/android/mutator.go index 440b9060f..940494506 100644 --- a/android/mutator.go +++ b/android/mutator.go @@ -148,9 +148,9 @@ var preArch = []RegisterMutatorFunc{ } func registerArchMutator(ctx RegisterMutatorsContext) { - ctx.BottomUpBlueprint("os", osMutator).Parallel() - ctx.BottomUp("image", imageMutator).Parallel() - ctx.BottomUpBlueprint("arch", archMutator).Parallel() + ctx.Transition("os", &osTransitionMutator{}) + ctx.Transition("image", &imageTransitionMutator{}) + 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 { @@ -400,6 +400,12 @@ type IncomingTransitionContext interface { Config() Config DeviceConfig() DeviceConfig + + // IsAddingDependency returns true if the transition is being called while adding a dependency + // after the transition mutator has already run, or false if it is being called when the transition + // mutator is running. This should be used sparingly, all uses will have to be removed in order + // to support creating variants on demand. + IsAddingDependency() bool } type OutgoingTransitionContext interface { @@ -510,6 +516,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) @@ -574,6 +583,10 @@ func (c *incomingTransitionContextImpl) DeviceConfig() DeviceConfig { return DeviceConfig{c.bp.Config().(Config).deviceConfig} } +func (c *incomingTransitionContextImpl) IsAddingDependency() bool { + return c.bp.IsAddingDependency() +} + func (c *incomingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) { return c.bp.Provider(provider) } @@ -729,6 +742,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") diff --git a/android/mutator_test.go b/android/mutator_test.go index 21eebd2c8..b3ef00f3d 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"} + }, }) }) @@ -243,27 +288,20 @@ func TestFinalDepsPhase(t *testing.T) { } 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") - } - } - 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 f89977200..d9791b6a6 100644 --- a/android/neverallow.go +++ b/android/neverallow.go @@ -60,6 +60,7 @@ func init() { AddNeverAllowRules(createCcStubsRule()) AddNeverAllowRules(createJavaExcludeStaticLibsRule()) AddNeverAllowRules(createProhibitHeaderOnlyRule()) + AddNeverAllowRules(createLimitNdkExportRule()...) AddNeverAllowRules(createKotlinPluginRule()...) } @@ -183,6 +184,7 @@ func createCcSdkVariantRules() []Rule { "packages/modules/SdkExtensions/derive_sdk", // These are for apps and shouldn't be used by non-SDK variant modules. "prebuilts/ndk", + "frameworks/native/libs/binder/ndk", "tools/test/graphicsbenchmark/apps/sample_app", "tools/test/graphicsbenchmark/functional_tests/java", "vendor/xts/gts-tests/hostsidetests/gamedevicecert/apps/javatests", @@ -213,7 +215,7 @@ func createCcSdkVariantRules() []Rule { func createCcStubsRule() Rule { ccStubsImplementationInstallableProjectsAllowedList := []string{ - "packages/modules/Virtualization/vm_payload", + "packages/modules/Virtualization/libs/libvm_payload", } return NeverAllow(). @@ -238,6 +240,7 @@ func createInitFirstStageRules() []Rule { Without("name", "init_first_stage"). Without("name", "init_first_stage.microdroid"). With("install_in_root", "true"). + NotModuleType("prebuilt_root"). Because("install_in_root is only for init_first_stage."), } } @@ -266,6 +269,22 @@ func createProhibitHeaderOnlyRule() Rule { Because("headers_only can only be used for generating framework-minus-apex headers for non-updatable modules") } +func createLimitNdkExportRule() []Rule { + reason := "If the headers you're trying to export are meant to be a part of the NDK, they should be exposed by an ndk_headers module. If the headers shouldn't be a part of the NDK, the headers should instead be exposed from a separate `cc_library_headers` which consumers depend on." + // DO NOT ADD HERE - please consult danalbert@ + // b/357711733 + return []Rule{ + NeverAllow(). + NotIn("frameworks/native/libs/binder/ndk"). + ModuleType("ndk_library"). + WithMatcher("export_header_libs", isSetMatcherInstance).Because(reason), + NeverAllow().ModuleType("ndk_library").WithMatcher("export_generated_headers", isSetMatcherInstance).Because(reason), + NeverAllow().ModuleType("ndk_library").WithMatcher("export_include_dirs", isSetMatcherInstance).Because(reason), + NeverAllow().ModuleType("ndk_library").WithMatcher("export_shared_lib_headers", isSetMatcherInstance).Because(reason), + NeverAllow().ModuleType("ndk_library").WithMatcher("export_static_lib_headers", isSetMatcherInstance).Because(reason), + } +} + func createKotlinPluginRule() []Rule { kotlinPluginProjectsAllowedList := []string{ // TODO: Migrate compose plugin to the bundled compiler plugin diff --git a/android/notices.go b/android/notices.go index b9c1682e3..3c41d924e 100644 --- a/android/notices.go +++ b/android/notices.go @@ -36,10 +36,22 @@ func modulesOutputDirs(ctx BuilderContext, modules ...Module) []string { return SortedUniqueStrings(dirs) } -func modulesLicenseMetadata(ctx BuilderContext, modules ...Module) Paths { +type BuilderAndOtherModuleProviderContext interface { + BuilderContext + OtherModuleProviderContext +} + +func modulesLicenseMetadata(ctx OtherModuleProviderContext, modules ...Module) Paths { result := make(Paths, 0, len(modules)) + mctx, isMctx := ctx.(ModuleContext) for _, module := range modules { - if mf := module.base().licenseMetadataFile; mf != nil { + var mf Path + if isMctx && mctx.Module() == module { + mf = mctx.LicenseMetadataFile() + } else { + mf = OtherModuleProviderOrDefault(ctx, module, InstallFilesProvider).LicenseMetadataFile + } + if mf != nil { result = append(result, mf) } } @@ -48,7 +60,7 @@ func modulesLicenseMetadata(ctx BuilderContext, modules ...Module) Paths { // buildNoticeOutputFromLicenseMetadata writes out a notice file. func buildNoticeOutputFromLicenseMetadata( - ctx BuilderContext, tool, ruleName string, outputFile WritablePath, + ctx BuilderAndOtherModuleProviderContext, tool, ruleName string, outputFile WritablePath, libraryName string, stripPrefix []string, modules ...Module) { depsFile := outputFile.ReplaceExtension(ctx, strings.TrimPrefix(outputFile.Ext()+".d", ".")) rule := NewRuleBuilder(pctx, ctx) @@ -84,7 +96,7 @@ func buildNoticeOutputFromLicenseMetadata( // on the license metadata files for the input `modules` defaulting to the // current context module if none given. func BuildNoticeTextOutputFromLicenseMetadata( - ctx BuilderContext, outputFile WritablePath, ruleName, libraryName string, + ctx BuilderAndOtherModuleProviderContext, outputFile WritablePath, ruleName, libraryName string, stripPrefix []string, modules ...Module) { buildNoticeOutputFromLicenseMetadata(ctx, "textnotice", "text_notice_"+ruleName, outputFile, libraryName, stripPrefix, modules...) @@ -94,7 +106,7 @@ func BuildNoticeTextOutputFromLicenseMetadata( // on the license metadata files for the input `modules` defaulting to the // current context module if none given. func BuildNoticeHtmlOutputFromLicenseMetadata( - ctx BuilderContext, outputFile WritablePath, ruleName, libraryName string, + ctx BuilderAndOtherModuleProviderContext, outputFile WritablePath, ruleName, libraryName string, stripPrefix []string, modules ...Module) { buildNoticeOutputFromLicenseMetadata(ctx, "htmlnotice", "html_notice_"+ruleName, outputFile, libraryName, stripPrefix, modules...) @@ -104,7 +116,7 @@ func BuildNoticeHtmlOutputFromLicenseMetadata( // on the license metadata files for the input `modules` defaulting to the // current context module if none given. func BuildNoticeXmlOutputFromLicenseMetadata( - ctx BuilderContext, outputFile WritablePath, ruleName, libraryName string, + ctx BuilderAndOtherModuleProviderContext, outputFile WritablePath, ruleName, libraryName string, stripPrefix []string, modules ...Module) { buildNoticeOutputFromLicenseMetadata(ctx, "xmlnotice", "xml_notice_"+ruleName, outputFile, libraryName, stripPrefix, modules...) diff --git a/android/packaging.go b/android/packaging.go index ae412e1bb..0909936c6 100644 --- a/android/packaging.go +++ b/android/packaging.go @@ -15,8 +15,12 @@ package android import ( + "bytes" + "encoding/gob" + "errors" "fmt" "path/filepath" + "sort" "strings" "github.com/google/blueprint" @@ -55,6 +59,42 @@ type PackagingSpec struct { // ArchType of the module which produced this packaging spec archType ArchType + + // List of module names that this packaging spec overrides + overrides *[]string + + // Name of the module where this packaging spec is output of + owner string +} + +func (p *PackagingSpec) GobEncode() ([]byte, error) { + w := new(bytes.Buffer) + encoder := gob.NewEncoder(w) + err := errors.Join(encoder.Encode(p.relPathInPackage), encoder.Encode(p.srcPath), + encoder.Encode(p.symlinkTarget), encoder.Encode(p.executable), + encoder.Encode(p.effectiveLicenseFiles), encoder.Encode(p.partition), + encoder.Encode(p.skipInstall), encoder.Encode(p.aconfigPaths), + encoder.Encode(p.archType)) + if err != nil { + return nil, err + } + + return w.Bytes(), nil +} + +func (p *PackagingSpec) GobDecode(data []byte) error { + r := bytes.NewBuffer(data) + decoder := gob.NewDecoder(r) + err := errors.Join(decoder.Decode(&p.relPathInPackage), decoder.Decode(&p.srcPath), + decoder.Decode(&p.symlinkTarget), decoder.Decode(&p.executable), + decoder.Decode(&p.effectiveLicenseFiles), decoder.Decode(&p.partition), + decoder.Decode(&p.skipInstall), decoder.Decode(&p.aconfigPaths), + decoder.Decode(&p.archType)) + if err != nil { + return err + } + + return nil } func (p *PackagingSpec) Equals(other *PackagingSpec) bool { @@ -324,7 +364,10 @@ func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.Dep } func (p *PackagingBase) GatherPackagingSpecsWithFilter(ctx ModuleContext, filter func(PackagingSpec) bool) map[string]PackagingSpec { - m := make(map[string]PackagingSpec) + // all packaging specs gathered from the dep. + var all []PackagingSpec + // list of module names overridden + var overridden []string var arches []ArchType for _, target := range getSupportedTargets(ctx) { @@ -345,7 +388,8 @@ func (p *PackagingBase) GatherPackagingSpecsWithFilter(ctx ModuleContext, filter if pi, ok := ctx.OtherModuleDependencyTag(child).(PackagingItem); !ok || !pi.IsPackagingItem() { return } - for _, ps := range child.TransitivePackagingSpecs() { + for _, ps := range OtherModuleProviderOrDefault( + ctx, child, InstallFilesProvider).TransitivePackagingSpecs.ToList() { if !filterArch(ps) { continue } @@ -355,17 +399,33 @@ func (p *PackagingBase) GatherPackagingSpecsWithFilter(ctx ModuleContext, filter continue } } - dstPath := ps.relPathInPackage - if existingPs, ok := m[dstPath]; ok { - if !existingPs.Equals(&ps) { - ctx.ModuleErrorf("packaging conflict at %v:\n%v\n%v", dstPath, existingPs, ps) - } - continue + all = append(all, ps) + if ps.overrides != nil { + overridden = append(overridden, *ps.overrides...) } - - m[dstPath] = ps } }) + + // all minus packaging specs that are overridden + var filtered []PackagingSpec + for _, ps := range all { + if ps.owner != "" && InList(ps.owner, overridden) { + continue + } + filtered = append(filtered, ps) + } + + m := make(map[string]PackagingSpec) + for _, ps := range filtered { + dstPath := ps.relPathInPackage + if existingPs, ok := m[dstPath]; ok { + if !existingPs.Equals(&ps) { + ctx.ModuleErrorf("packaging conflict at %v:\n%v\n%v", dstPath, existingPs, ps) + } + continue + } + m[dstPath] = ps + } return m } @@ -377,31 +437,59 @@ func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]Packa // CopySpecsToDir is a helper that will add commands to the rule builder to copy the PackagingSpec // entries into the specified directory. func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir WritablePath) (entries []string) { - if len(specs) == 0 { + dirsToSpecs := make(map[WritablePath]map[string]PackagingSpec) + dirsToSpecs[dir] = specs + return p.CopySpecsToDirs(ctx, builder, dirsToSpecs) +} + +// CopySpecsToDirs is a helper that will add commands to the rule builder to copy the PackagingSpec +// entries into corresponding directories. +func (p *PackagingBase) CopySpecsToDirs(ctx ModuleContext, builder *RuleBuilder, dirsToSpecs map[WritablePath]map[string]PackagingSpec) (entries []string) { + empty := true + for _, specs := range dirsToSpecs { + if len(specs) > 0 { + empty = false + break + } + } + if empty { return entries } + seenDir := make(map[string]bool) preparerPath := PathForModuleOut(ctx, "preparer.sh") cmd := builder.Command().Tool(preparerPath) var sb strings.Builder sb.WriteString("set -e\n") - for _, k := range SortedKeys(specs) { - ps := specs[k] - destPath := filepath.Join(dir.String(), ps.relPathInPackage) - destDir := filepath.Dir(destPath) - entries = append(entries, ps.relPathInPackage) - if _, ok := seenDir[destDir]; !ok { - seenDir[destDir] = true - sb.WriteString(fmt.Sprintf("mkdir -p %s\n", destDir)) - } - if ps.symlinkTarget == "" { - cmd.Implicit(ps.srcPath) - sb.WriteString(fmt.Sprintf("cp %s %s\n", ps.srcPath, destPath)) - } else { - sb.WriteString(fmt.Sprintf("ln -sf %s %s\n", ps.symlinkTarget, destPath)) - } - if ps.executable { - sb.WriteString(fmt.Sprintf("chmod a+x %s\n", destPath)) + + dirs := make([]WritablePath, 0, len(dirsToSpecs)) + for dir, _ := range dirsToSpecs { + dirs = append(dirs, dir) + } + sort.Slice(dirs, func(i, j int) bool { + return dirs[i].String() < dirs[j].String() + }) + + for _, dir := range dirs { + specs := dirsToSpecs[dir] + for _, k := range SortedKeys(specs) { + ps := specs[k] + destPath := filepath.Join(dir.String(), ps.relPathInPackage) + destDir := filepath.Dir(destPath) + entries = append(entries, ps.relPathInPackage) + if _, ok := seenDir[destDir]; !ok { + seenDir[destDir] = true + sb.WriteString(fmt.Sprintf("mkdir -p %s\n", destDir)) + } + if ps.symlinkTarget == "" { + cmd.Implicit(ps.srcPath) + sb.WriteString(fmt.Sprintf("cp %s %s\n", ps.srcPath, destPath)) + } else { + sb.WriteString(fmt.Sprintf("ln -sf %s %s\n", ps.symlinkTarget, destPath)) + } + if ps.executable { + sb.WriteString(fmt.Sprintf("chmod a+x %s\n", destPath)) + } } } diff --git a/android/packaging_test.go b/android/packaging_test.go index 19b46fefd..f5b1020fc 100644 --- a/android/packaging_test.go +++ b/android/packaging_test.go @@ -28,6 +28,7 @@ type componentTestModule struct { props struct { Deps []string Skip_install *bool + Overrides []string } } @@ -117,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) @@ -650,3 +652,64 @@ func TestPrefer32Deps(t *testing.T) { runPackagingTest(t, config, bp, tc.expected) } } + +func TestOverrides(t *testing.T) { + bpTemplate := ` + component { + name: "foo", + deps: ["bar"], + } + + component { + name: "bar", + } + + component { + name: "bar_override", + overrides: ["bar"], + } + + component { + name: "baz", + deps: ["bar_override"], + } + + package_module { + name: "package", + deps: %DEPS%, + } + ` + testcases := []struct { + deps []string + expected []string + }{ + { + deps: []string{"foo"}, + expected: []string{"lib64/foo", "lib64/bar"}, + }, + { + deps: []string{"foo", "bar_override"}, + expected: []string{"lib64/foo", "lib64/bar_override"}, + }, + { + deps: []string{"foo", "bar", "bar_override"}, + expected: []string{"lib64/foo", "lib64/bar_override"}, + }, + { + deps: []string{"bar", "bar_override"}, + expected: []string{"lib64/bar_override"}, + }, + { + deps: []string{"foo", "baz"}, + expected: []string{"lib64/foo", "lib64/baz", "lib64/bar_override"}, + }, + } + for _, tc := range testcases { + config := testConfig{ + multiTarget: true, + depsCollectFirstTargetOnly: false, + } + bp := strings.Replace(bpTemplate, "%DEPS%", `["`+strings.Join(tc.deps, `", "`)+`"]`, -1) + runPackagingTest(t, config, bp, tc.expected) + } +} diff --git a/android/paths.go b/android/paths.go index 714e89990..657501830 100644 --- a/android/paths.go +++ b/android/paths.go @@ -15,6 +15,9 @@ package android import ( + "bytes" + "encoding/gob" + "errors" "fmt" "os" "path/filepath" @@ -24,7 +27,6 @@ import ( "strings" "github.com/google/blueprint" - "github.com/google/blueprint/bootstrap" "github.com/google/blueprint/pathtools" ) @@ -89,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 + OtherModuleProviderContext VisitDirectDepsBlueprint(visit func(blueprint.Module)) OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag + HasMutatorFinished(mutatorName string) bool } // ModuleMissingDepsPathContext is a subset of *ModuleContext methods required by @@ -234,6 +238,9 @@ type Path interface { // directory, and OutputPath.Join("foo").Rel() would return "foo". Rel() string + // WithoutRel returns a new Path with no relative path, i.e. Rel() will return the same value as Base(). + WithoutRel() Path + // RelativeToTop returns a new path relative to the top, it is provided solely for use in tests. // // It is guaranteed to always return the same type as it is called on, e.g. if called on an @@ -242,13 +249,13 @@ type Path interface { // A standard build has the following structure: // ../top/ // out/ - make install files go here. - // out/soong - this is the soongOutDir passed to NewTestConfig() + // out/soong - this is the outDir passed to NewTestConfig() // ... - the source files // // This function converts a path so that it appears relative to the ../top/ directory, i.e. - // * Make install paths, which have the pattern "soongOutDir/../" are converted into the top + // * Make install paths, which have the pattern "outDir/../" are converted into the top // relative path "out/" - // * Soong install paths and other writable paths, which have the pattern "soongOutDir/" are + // * Soong install paths and other writable paths, which have the pattern "outDir/soong/" are // converted into the top relative path "out/soong/". // * Source paths are already relative to the top. // * Phony paths are not relative to anything. @@ -258,8 +265,9 @@ type Path interface { } const ( - OutDir = "out" - OutSoongDir = OutDir + "/soong" + testOutDir = "out" + testOutSoongSubDir = "/soong" + TestOutSoongDir = testOutDir + testOutSoongSubDir ) // WritablePath is a type of path that can be used as an output for build rules. @@ -460,8 +468,8 @@ func ExistentPathsForSources(ctx PathGlobContext, paths []string) Paths { // - glob, relative to the local module directory, resolves as filepath(s), relative to the local // source directory. // - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer -// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source -// filepath. +// or set the OutputFilesProvider. These resolve as a filepath to an output filepath or generated +// source filepath. // // Properties passed as the paths argument must have been annotated with struct tag // `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the @@ -488,8 +496,8 @@ type SourceInput struct { // - glob, relative to the local module directory, resolves as filepath(s), relative to the local // source directory. Not valid in excludes. // - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer -// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source -// filepath. +// or set the OutputFilesProvider. These resolve as a filepath to an output filepath or generated +// source filepath. // // excluding the items (similarly resolved // Properties passed as the paths argument must have been annotated with struct tag @@ -547,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. @@ -565,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 @@ -617,8 +614,8 @@ func GetModuleFromPathDep(ctx ModuleWithDepsPathContext, moduleName, tag string) // - glob, relative to the local module directory, resolves as filepath(s), relative to the local // source directory. Not valid in excludes. // - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer -// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source -// filepath. +// or set the OutputFilesProvider. These resolve as a filepath to an output filepath or generated +// source filepath. // // and a list of the module names of missing module dependencies are returned as the second return. // Properties passed as the paths argument must have been annotated with struct tag @@ -1068,6 +1065,28 @@ type basePath struct { rel string } +func (p basePath) GobEncode() ([]byte, error) { + w := new(bytes.Buffer) + encoder := gob.NewEncoder(w) + err := errors.Join(encoder.Encode(p.path), encoder.Encode(p.rel)) + if err != nil { + return nil, err + } + + return w.Bytes(), nil +} + +func (p *basePath) GobDecode(data []byte) error { + r := bytes.NewBuffer(data) + decoder := gob.NewDecoder(r) + err := errors.Join(decoder.Decode(&p.path), decoder.Decode(&p.rel)) + if err != nil { + return err + } + + return nil +} + func (p basePath) Ext() string { return filepath.Ext(p.path) } @@ -1093,8 +1112,8 @@ func (p basePath) withRel(rel string) basePath { return p } -func (p basePath) RelativeToTop() Path { - ensureTestOnly() +func (p basePath) withoutRel() basePath { + p.rel = filepath.Base(p.path) return p } @@ -1110,6 +1129,11 @@ func (p SourcePath) withRel(rel string) SourcePath { return p } +func (p SourcePath) RelativeToTop() Path { + ensureTestOnly() + return p +} + // safePathForSource is for paths that we expect are safe -- only for use by go // code that is embedding ninja variables in paths func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) { @@ -1243,11 +1267,13 @@ func PathForSourceRelaxed(ctx PathContext, pathComponents ...string) SourcePath // PathForArbitraryOutput creates a path for the given components. Unlike PathForOutput, // the path is relative to the root of the output folder, not the out/soong folder. func PathForArbitraryOutput(ctx PathContext, pathComponents ...string) Path { - p, err := validatePath(pathComponents...) + path, err := validatePath(pathComponents...) if err != nil { reportPathError(ctx, err) } - return basePath{path: filepath.Join(ctx.Config().OutDir(), p)} + fullPath := filepath.Join(ctx.Config().OutDir(), path) + path = fullPath[len(fullPath)-len(path):] + return OutputPath{basePath{path, ""}, ctx.Config().OutDir(), fullPath} } // MaybeExistentPathForSource joins the provided path components and validates that the result @@ -1300,6 +1326,11 @@ func (p SourcePath) String() string { return p.path } +func (p SourcePath) WithoutRel() Path { + p.basePath = p.basePath.withoutRel() + return p +} + // Join creates a new SourcePath with paths... joined with the current path. The // provided paths... may not use '..' to escape from the current path. func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath { @@ -1350,25 +1381,47 @@ func (p SourcePath) OverlayPath(ctx ModuleMissingDepsPathContext, path Path) Opt type OutputPath struct { basePath - // The soong build directory, i.e. Config.SoongOutDir() - soongOutDir string + // The base out directory for this path, either Config.SoongOutDir() or Config.OutDir() + outDir string fullPath string } +func (p OutputPath) GobEncode() ([]byte, error) { + w := new(bytes.Buffer) + encoder := gob.NewEncoder(w) + err := errors.Join(encoder.Encode(p.basePath), encoder.Encode(p.outDir), encoder.Encode(p.fullPath)) + if err != nil { + return nil, err + } + + return w.Bytes(), nil +} + +func (p *OutputPath) GobDecode(data []byte) error { + r := bytes.NewBuffer(data) + decoder := gob.NewDecoder(r) + err := errors.Join(decoder.Decode(&p.basePath), decoder.Decode(&p.outDir), decoder.Decode(&p.fullPath)) + if err != nil { + return err + } + + return nil +} + func (p OutputPath) withRel(rel string) OutputPath { p.basePath = p.basePath.withRel(rel) p.fullPath = filepath.Join(p.fullPath, rel) return p } -func (p OutputPath) WithoutRel() OutputPath { - p.basePath.rel = filepath.Base(p.basePath.path) +func (p OutputPath) WithoutRel() Path { + p.basePath = p.basePath.withoutRel() return p } func (p OutputPath) getSoongOutDir() string { - return p.soongOutDir + return p.outDir } func (p OutputPath) RelativeToTop() Path { @@ -1376,8 +1429,13 @@ func (p OutputPath) RelativeToTop() Path { } func (p OutputPath) outputPathRelativeToTop() OutputPath { - p.fullPath = StringPathRelativeToTop(p.soongOutDir, p.fullPath) - p.soongOutDir = OutSoongDir + p.fullPath = StringPathRelativeToTop(p.outDir, p.fullPath) + if strings.HasSuffix(p.outDir, testOutSoongSubDir) { + p.outDir = TestOutSoongDir + } else { + // Handle the PathForArbitraryOutput case + p.outDir = testOutDir + } return p } @@ -1394,6 +1452,11 @@ type toolDepPath struct { basePath } +func (t toolDepPath) WithoutRel() Path { + t.basePath = t.basePath.withoutRel() + return t +} + func (t toolDepPath) RelativeToTop() Path { ensureTestOnly() return t @@ -1423,7 +1486,7 @@ func PathForOutput(ctx PathContext, pathComponents ...string) OutputPath { return OutputPath{basePath{path, ""}, ctx.Config().soongOutDir, fullPath} } -// PathsForOutput returns Paths rooted from soongOutDir +// PathsForOutput returns Paths rooted from outDir func PathsForOutput(ctx PathContext, paths []string) WritablePaths { ret := make(WritablePaths, len(paths)) for i, path := range paths { @@ -1607,11 +1670,10 @@ type ModuleOutPathContext interface { ModuleName() string ModuleDir() string ModuleSubDir() string - SoongConfigTraceHash() string } func pathForModuleOut(ctx ModuleOutPathContext) OutputPath { - return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir(), ctx.SoongConfigTraceHash()) + return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir()) } // PathForModuleOut returns a Path representing the paths... under the module's @@ -1744,6 +1806,32 @@ type InstallPath struct { fullPath string } +func (p *InstallPath) GobEncode() ([]byte, error) { + w := new(bytes.Buffer) + encoder := gob.NewEncoder(w) + err := errors.Join(encoder.Encode(p.basePath), encoder.Encode(p.soongOutDir), + encoder.Encode(p.partitionDir), encoder.Encode(p.partition), + encoder.Encode(p.makePath), encoder.Encode(p.fullPath)) + if err != nil { + return nil, err + } + + return w.Bytes(), nil +} + +func (p *InstallPath) GobDecode(data []byte) error { + r := bytes.NewBuffer(data) + decoder := gob.NewDecoder(r) + err := errors.Join(decoder.Decode(&p.basePath), decoder.Decode(&p.soongOutDir), + decoder.Decode(&p.partitionDir), decoder.Decode(&p.partition), + decoder.Decode(&p.makePath), decoder.Decode(&p.fullPath)) + if err != nil { + return err + } + + return nil +} + // Will panic if called from outside a test environment. func ensureTestOnly() { if PrefixInList(os.Args, "-test.") { @@ -1755,14 +1843,19 @@ func ensureTestOnly() { func (p InstallPath) RelativeToTop() Path { ensureTestOnly() if p.makePath { - p.soongOutDir = OutDir + p.soongOutDir = testOutDir } else { - p.soongOutDir = OutSoongDir + p.soongOutDir = TestOutSoongDir } p.fullPath = filepath.Join(p.soongOutDir, p.path) return p } +func (p InstallPath) WithoutRel() Path { + p.basePath = p.basePath.withoutRel() + return p +} + func (p InstallPath) getSoongOutDir() string { return p.soongOutDir } @@ -2083,6 +2176,11 @@ func (p PhonyPath) RelativeToTop() Path { return p } +func (p PhonyPath) WithoutRel() Path { + p.basePath = p.basePath.withoutRel() + return p +} + func (p PhonyPath) ReplaceExtension(ctx PathContext, ext string) OutputPath { panic("Not implemented") } @@ -2099,6 +2197,11 @@ func (p testPath) RelativeToTop() Path { return p } +func (p testPath) WithoutRel() Path { + p.basePath = p.basePath.withoutRel() + return p +} + func (p testPath) String() string { return p.path } diff --git a/android/paths_test.go b/android/paths_test.go index 93b9b9a16..941f0ca78 100644 --- a/android/paths_test.go +++ b/android/paths_test.go @@ -1183,9 +1183,6 @@ type pathForModuleSrcOutputFileProviderModule struct { Outs []string Tagged []string } - - outs Paths - tagged Paths } func pathForModuleSrcOutputFileProviderModuleFactory() Module { @@ -1196,24 +1193,17 @@ func pathForModuleSrcOutputFileProviderModuleFactory() Module { } func (p *pathForModuleSrcOutputFileProviderModule) GenerateAndroidBuildActions(ctx ModuleContext) { + var outs, taggedOuts Paths for _, out := range p.props.Outs { - p.outs = append(p.outs, PathForModuleOut(ctx, out)) + outs = append(outs, PathForModuleOut(ctx, out)) } for _, tagged := range p.props.Tagged { - p.tagged = append(p.tagged, PathForModuleOut(ctx, tagged)) + taggedOuts = append(taggedOuts, PathForModuleOut(ctx, tagged)) } -} -func (p *pathForModuleSrcOutputFileProviderModule) OutputFiles(tag string) (Paths, error) { - switch tag { - case "": - return p.outs, nil - case ".tagged": - return p.tagged, nil - default: - return nil, fmt.Errorf("unsupported tag %q", tag) - } + ctx.SetOutputFiles(outs, "") + ctx.SetOutputFiles(taggedOuts, ".tagged") } type pathForModuleSrcTestCase struct { diff --git a/android/phony.go b/android/phony.go index 814a9e30a..f8db88d43 100644 --- a/android/phony.go +++ b/android/phony.go @@ -26,14 +26,20 @@ type phonyMap map[string]Paths var phonyMapLock sync.Mutex -func getPhonyMap(config Config) phonyMap { +type ModulePhonyInfo struct { + Phonies map[string]Paths +} + +var ModulePhonyProvider = blueprint.NewProvider[ModulePhonyInfo]() + +func getSingletonPhonyMap(config Config) phonyMap { return config.Once(phonyMapOnceKey, func() interface{} { return make(phonyMap) }).(phonyMap) } -func addPhony(config Config, name string, deps ...Path) { - phonyMap := getPhonyMap(config) +func addSingletonPhony(config Config, name string, deps ...Path) { + phonyMap := getSingletonPhonyMap(config) phonyMapLock.Lock() defer phonyMapLock.Unlock() phonyMap[name] = append(phonyMap[name], deps...) @@ -47,7 +53,15 @@ type phonySingleton struct { var _ SingletonMakeVarsProvider = (*phonySingleton)(nil) func (p *phonySingleton) GenerateBuildActions(ctx SingletonContext) { - p.phonyMap = getPhonyMap(ctx.Config()) + p.phonyMap = getSingletonPhonyMap(ctx.Config()) + ctx.VisitAllModules(func(m Module) { + if info, ok := OtherModuleProvider(ctx, m, ModulePhonyProvider); ok { + for k, v := range info.Phonies { + p.phonyMap[k] = append(p.phonyMap[k], v...) + } + } + }) + p.phonyList = SortedKeys(p.phonyMap) for _, phony := range p.phonyList { p.phonyMap[phony] = SortedUniquePaths(p.phonyMap[phony]) diff --git a/android/prebuilt.go b/android/prebuilt.go index 921fb3c11..fd5a6eaee 100644 --- a/android/prebuilt.go +++ b/android/prebuilt.go @@ -61,7 +61,7 @@ var _ ExcludeFromApexContentsTag = PrebuiltDepTag type UserSuppliedPrebuiltProperties struct { // When prefer is set to true the prebuilt will be used instead of any source module with // a matching name. - Prefer *bool `android:"arch_variant"` + Prefer proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"` // When specified this names a Soong config variable that controls the prefer property. // @@ -148,11 +148,7 @@ func PrebuiltNameFromSource(name string) string { } func (p *Prebuilt) ForcePrefer() { - p.properties.Prefer = proptools.BoolPtr(true) -} - -func (p *Prebuilt) Prefer() bool { - return proptools.Bool(p.properties.Prefer) + p.properties.Prefer = NewSimpleConfigurable(true) } // SingleSourcePathFromSupplier invokes the supplied supplier for the current module in the @@ -248,6 +244,8 @@ func InitPrebuiltModuleWithSrcSupplier(module PrebuiltInterface, srcsSupplier Pr p.srcsPropertyName = srcsPropertyName } +// InitPrebuiltModule is the same as InitPrebuiltModuleWithSrcSupplier, but uses the +// provided list of strings property as the source provider. func InitPrebuiltModule(module PrebuiltInterface, srcs *[]string) { if srcs == nil { panic(fmt.Errorf("srcs must not be nil")) @@ -260,6 +258,20 @@ func InitPrebuiltModule(module PrebuiltInterface, srcs *[]string) { InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, "srcs") } +// InitConfigurablePrebuiltModule is the same as InitPrebuiltModule, but uses a +// Configurable list of strings property instead of a regular list of strings. +func InitConfigurablePrebuiltModule(module PrebuiltInterface, srcs *proptools.Configurable[[]string]) { + if srcs == nil { + panic(fmt.Errorf("srcs must not be nil")) + } + + srcsSupplier := func(ctx BaseModuleContext, _ Module) []string { + return srcs.GetOrDefault(ctx, nil) + } + + InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, "srcs") +} + func InitSingleSourcePrebuiltModule(module PrebuiltInterface, srcProps interface{}, srcField string) { srcPropsValue := reflect.ValueOf(srcProps).Elem() srcStructField, _ := srcPropsValue.Type().FieldByName(srcField) @@ -738,7 +750,7 @@ func (p *Prebuilt) usePrebuilt(ctx BaseMutatorContext, source Module, prebuilt M } // TODO: use p.Properties.Name and ctx.ModuleDir to override preference - return Bool(p.properties.Prefer) + return p.properties.Prefer.GetOrDefault(ctx, false) } func (p *Prebuilt) SourceExists() bool { diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go index d775ac356..5e4af0ba5 100644 --- a/android/prebuilt_test.go +++ b/android/prebuilt_test.go @@ -15,7 +15,6 @@ package android import ( - "fmt" "testing" "github.com/google/blueprint" @@ -494,7 +493,6 @@ type prebuiltModule struct { properties struct { Srcs []string `android:"path,arch_variant"` } - src Path } func newPrebuiltModule() Module { @@ -510,24 +508,17 @@ func (p *prebuiltModule) Name() string { } func (p *prebuiltModule) GenerateAndroidBuildActions(ctx ModuleContext) { + var src Path if len(p.properties.Srcs) >= 1 { - p.src = p.prebuilt.SingleSourcePath(ctx) + src = p.prebuilt.SingleSourcePath(ctx) } + ctx.SetOutputFiles(Paths{src}, "") } func (p *prebuiltModule) Prebuilt() *Prebuilt { return &p.prebuilt } -func (p *prebuiltModule) OutputFiles(tag string) (Paths, error) { - switch tag { - case "": - return Paths{p.src}, nil - default: - return nil, fmt.Errorf("unsupported module reference tag %q", tag) - } -} - type sourceModuleProperties struct { Deps []string `android:"path,arch_variant"` } @@ -583,11 +574,7 @@ func newOverrideSourceModule() Module { func TestPrebuiltErrorCannotListBothSourceAndPrebuiltInContributions(t *testing.T) { selectMainlineModuleContritbutions := GroupFixturePreparers( - FixtureModifyProductVariables(func(variables FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_apex_contributions", - } - }), + PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "my_apex_contributions"), ) testPrebuiltErrorWithFixture(t, `Found duplicate variations of the same module in apex_contributions: foo and prebuilt_foo. Please remove one of these`, ` source { diff --git a/android/product_config.go b/android/product_config.go index 20b29a7c4..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 @@ -37,8 +39,10 @@ func (p *productConfigModule) GenerateAndroidBuildActions(ctx ModuleContext) { if targetProduct != "" { targetProduct += "." } - soongVariablesPath := PathForOutput(ctx, "soong."+targetProduct+"variables") - extraVariablesPath := PathForOutput(ctx, "soong."+targetProduct+"extra.variables") + + coverageSuffix := ctx.Config().CoverageSuffix() + soongVariablesPath := PathForOutput(ctx, "soong."+targetProduct+coverageSuffix+"variables") + extraVariablesPath := PathForOutput(ctx, "soong."+targetProduct+coverageSuffix+"extra.variables") rule := NewRuleBuilder(pctx, ctx) rule.Command().BuiltTool("merge_json"). 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/provider.go b/android/provider.go index 3b9c5d2ba..5ded4cc14 100644 --- a/android/provider.go +++ b/android/provider.go @@ -14,6 +14,8 @@ var _ OtherModuleProviderContext = BaseModuleContext(nil) var _ OtherModuleProviderContext = ModuleContext(nil) var _ OtherModuleProviderContext = BottomUpMutatorContext(nil) var _ OtherModuleProviderContext = TopDownMutatorContext(nil) +var _ OtherModuleProviderContext = SingletonContext(nil) +var _ OtherModuleProviderContext = (*TestContext)(nil) // OtherModuleProvider reads the provider for the given module. If the provider has been set the value is // returned and the boolean is true. If it has not been set the zero value of the provider's type is returned @@ -30,6 +32,11 @@ func OtherModuleProvider[K any](ctx OtherModuleProviderContext, module blueprint return value.(K), ok } +func OtherModuleProviderOrDefault[K any](ctx OtherModuleProviderContext, module blueprint.Module, provider blueprint.ProviderKey[K]) K { + value, _ := OtherModuleProvider(ctx, module, provider) + return value +} + // ModuleProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or // TopDownMutatorContext for use in ModuleProvider. type ModuleProviderContext interface { @@ -56,26 +63,6 @@ func ModuleProvider[K any](ctx ModuleProviderContext, provider blueprint.Provide return value.(K), ok } -type SingletonModuleProviderContext interface { - moduleProvider(blueprint.Module, blueprint.AnyProviderKey) (any, bool) -} - -var _ SingletonModuleProviderContext = SingletonContext(nil) -var _ SingletonModuleProviderContext = (*TestContext)(nil) - -// SingletonModuleProvider wraps blueprint.SingletonModuleProvider to provide a type-safe method to retrieve the value -// of the given provider from a module using a SingletonContext. If the provider has not been set the first return -// value will be the zero value of the provider's type, and the second return value will be false. If the provider has -// been set the second return value will be true. -func SingletonModuleProvider[K any](ctx SingletonModuleProviderContext, module blueprint.Module, provider blueprint.ProviderKey[K]) (K, bool) { - value, ok := ctx.moduleProvider(module, provider) - if !ok { - var k K - return k, false - } - return value.(K), ok -} - // SetProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or // TopDownMutatorContext for use in SetProvider. type SetProviderContext interface { diff --git a/android/rule_builder.go b/android/rule_builder.go index 85e29bd20..18bbcab5c 100644 --- a/android/rule_builder.go +++ b/android/rule_builder.go @@ -58,6 +58,7 @@ type RuleBuilder struct { sboxInputs bool sboxManifestPath WritablePath missingDeps []string + args map[string]string } // NewRuleBuilder returns a newly created RuleBuilder. @@ -78,6 +79,17 @@ func (rb *RuleBuilder) SetSboxOutDirDirAsEmpty() *RuleBuilder { return rb } +// Set the phony_output argument. +// This causes the output files to be ignored. +// If the output isn't created, it's not treated as an error. +// The build rule is run every time whether or not the output is created. +func (rb *RuleBuilder) SetPhonyOutput() { + if rb.args == nil { + rb.args = make(map[string]string) + } + rb.args["phony_output"] = "true" +} + // RuleBuilderInstall is a tuple of install from and to locations. type RuleBuilderInstall struct { From Path @@ -451,6 +463,8 @@ func (r *RuleBuilder) Build(name string, desc string) { r.build(name, desc, true) } +var sandboxEnvOnceKey = NewOnceKey("sandbox_environment_variables") + func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString bool) { name = ninjaNameEscape(name) @@ -542,6 +556,12 @@ func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString b To: proto.String(r.sboxPathForInputRel(input)), }) } + for _, input := range r.OrderOnlys() { + command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{ + From: proto.String(input.String()), + To: proto.String(r.sboxPathForInputRel(input)), + }) + } // If using rsp files copy them and their contents into the sbox directory with // the appropriate path mappings. @@ -562,6 +582,44 @@ func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString b }) } + // Only allow the build to access certain environment variables + command.DontInheritEnv = proto.Bool(true) + command.Env = r.ctx.Config().Once(sandboxEnvOnceKey, func() interface{} { + // The list of allowed variables was found by running builds of all + // genrules and seeing what failed + var result []*sbox_proto.EnvironmentVariable + inheritedVars := []string{ + "PATH", + "JAVA_HOME", + "TMPDIR", + // Allow RBE variables because the art tests invoke RBE manually + "RBE_log_dir", + "RBE_platform", + "RBE_server_address", + // TODO: RBE_exec_root is set to the absolute path to the root of the source + // tree, which we don't want sandboxed actions to find. Remap it to ".". + "RBE_exec_root", + } + for _, v := range inheritedVars { + result = append(result, &sbox_proto.EnvironmentVariable{ + Name: proto.String(v), + State: &sbox_proto.EnvironmentVariable_Inherit{ + Inherit: true, + }, + }) + } + // Set OUT_DIR to the relative path of the sandboxed out directory. + // Otherwise, OUT_DIR will be inherited from the rest of the build, + // which will allow scripts to escape the sandbox if OUT_DIR is an + // absolute path. + result = append(result, &sbox_proto.EnvironmentVariable{ + Name: proto.String("OUT_DIR"), + State: &sbox_proto.EnvironmentVariable_Value{ + Value: sboxOutSubDir, + }, + }) + return result + }).([]*sbox_proto.EnvironmentVariable) command.Chdir = proto.Bool(true) } @@ -726,6 +784,12 @@ func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString b commandString = proptools.NinjaEscape(commandString) } + args_vars := make([]string, len(r.args)) + i := 0 + for k, _ := range r.args { + args_vars[i] = k + i++ + } r.ctx.Build(r.pctx, BuildParams{ Rule: r.ctx.Rule(r.pctx, name, blueprint.RuleParams{ Command: commandString, @@ -734,7 +798,7 @@ func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString b Rspfile: proptools.NinjaEscape(rspFile), RspfileContent: rspFileContent, Pool: pool, - }), + }, args_vars...), Inputs: rspFileInputs, Implicits: inputs, OrderOnly: r.OrderOnlys(), @@ -744,6 +808,7 @@ func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString b Depfile: depFile, Deps: depFormat, Description: desc, + Args: r.args, }) } diff --git a/android/sbom.go b/android/sbom.go new file mode 100644 index 000000000..2a5499ed8 --- /dev/null +++ b/android/sbom.go @@ -0,0 +1,111 @@ +// 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 + +import ( + "io" + "path/filepath" + "strings" + + "github.com/google/blueprint" +) + +var ( + // Command line tool to generate SBOM in Soong + genSbom = pctx.HostBinToolVariable("genSbom", "gen_sbom") + + // Command to generate SBOM in Soong. + genSbomRule = pctx.AndroidStaticRule("genSbomRule", blueprint.RuleParams{ + Command: "rm -rf $out && ${genSbom} --output_file ${out} --metadata ${in} --product_out ${productOut} --soong_out ${soongOut} --build_version \"$$(cat ${buildFingerprintFile})\" --product_mfr \"${productManufacturer}\" --json", + CommandDeps: []string{"${genSbom}"}, + }, "productOut", "soongOut", "buildFingerprintFile", "productManufacturer") +) + +func init() { + RegisterSbomSingleton(InitRegistrationContext) +} + +func RegisterSbomSingleton(ctx RegistrationContext) { + ctx.RegisterParallelSingletonType("sbom_singleton", sbomSingletonFactory) +} + +// sbomSingleton is used to generate build actions of generating SBOM of products. +type sbomSingleton struct { + sbomFile OutputPath +} + +func sbomSingletonFactory() Singleton { + return &sbomSingleton{} +} + +// Generates SBOM of products +func (this *sbomSingleton) GenerateBuildActions(ctx SingletonContext) { + if !ctx.Config().HasDeviceProduct() { + return + } + // Get all METADATA files and add them as implicit input + metadataFileListFile := PathForArbitraryOutput(ctx, ".module_paths", "METADATA.list") + f, err := ctx.Config().fs.Open(metadataFileListFile.String()) + if err != nil { + panic(err) + } + b, err := io.ReadAll(f) + if err != nil { + panic(err) + } + allMetadataFiles := strings.Split(string(b), "\n") + implicits := []Path{metadataFileListFile} + for _, path := range allMetadataFiles { + implicits = append(implicits, PathForSource(ctx, path)) + } + prodVars := ctx.Config().productVariables + buildFingerprintFile := PathForArbitraryOutput(ctx, "target", "product", String(prodVars.DeviceName), "build_fingerprint.txt") + implicits = append(implicits, buildFingerprintFile) + + // Add installed_files.stamp as implicit input, which depends on all installed files of the product. + installedFilesStamp := PathForOutput(ctx, "compliance-metadata", ctx.Config().DeviceProduct(), "installed_files.stamp") + implicits = append(implicits, installedFilesStamp) + + metadataDb := PathForOutput(ctx, "compliance-metadata", ctx.Config().DeviceProduct(), "compliance-metadata.db") + this.sbomFile = PathForOutput(ctx, "sbom", ctx.Config().DeviceProduct(), "sbom.spdx.json") + ctx.Build(pctx, BuildParams{ + Rule: genSbomRule, + Input: metadataDb, + Implicits: implicits, + Output: this.sbomFile, + Args: map[string]string{ + "productOut": filepath.Join(ctx.Config().OutDir(), "target", "product", String(prodVars.DeviceName)), + "soongOut": ctx.Config().soongOutDir, + "buildFingerprintFile": buildFingerprintFile.String(), + "productManufacturer": ctx.Config().ProductVariables().ProductManufacturer, + }, + }) + + if !ctx.Config().UnbundledBuildApps() { + // When building SBOM of products, phony rule "sbom" is for generating product SBOM in Soong. + ctx.Build(pctx, BuildParams{ + Rule: blueprint.Phony, + Inputs: []Path{this.sbomFile}, + Output: PathForPhony(ctx, "sbom"), + }) + } +} + +func (this *sbomSingleton) MakeVars(ctx MakeVarsContext) { + // When building SBOM of products + if !ctx.Config().UnbundledBuildApps() { + ctx.DistForGoalWithFilename("droid", this.sbomFile, "sbom/sbom.spdx.json") + } +} diff --git a/android/sdk.go b/android/sdk.go index 632c4d92d..ab9a91ccb 100644 --- a/android/sdk.go +++ b/android/sdk.go @@ -513,6 +513,9 @@ type SdkMemberType interface { // SupportedLinkages returns the names of the linkage variants supported by this module. SupportedLinkages() []string + // DisablesStrip returns true if the stripping needs to be disabled for this module. + DisablesStrip() bool + // ArePrebuiltsRequired returns true if prebuilts are required in the sdk snapshot, false // otherwise. ArePrebuiltsRequired() bool @@ -618,6 +621,9 @@ type SdkMemberTypeBase struct { // The names of linkage variants supported by this module. SupportedLinkageNames []string + // StripDisabled returns true if the stripping needs to be disabled for this module. + StripDisabled bool + // When set to true BpPropertyNotRequired indicates that the member type does not require the // property to be specifiable in an Android.bp file. BpPropertyNotRequired bool @@ -689,6 +695,10 @@ func (b *SdkMemberTypeBase) SupportedLinkages() []string { return b.SupportedLinkageNames } +func (b *SdkMemberTypeBase) DisablesStrip() bool { + return b.StripDisabled +} + // registeredModuleExportsMemberTypes is the set of registered SdkMemberTypes for module_exports // modules. var registeredModuleExportsMemberTypes = &sdkRegistry{} @@ -803,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/sdk_version.go b/android/sdk_version.go index 01b55d0da..a9b88fbce 100644 --- a/android/sdk_version.go +++ b/android/sdk_version.go @@ -93,6 +93,15 @@ func (k SdkKind) String() string { } } +func ToSdkKind(s string) SdkKind { + for kind := SdkNone; kind <= SdkPrivate; kind++ { + if s == kind.String() { + return kind + } + } + return SdkInvalid +} + func (k SdkKind) DefaultJavaLibraryName() string { switch k { case SdkPublic: diff --git a/android/singleton.go b/android/singleton.go index d364384e2..913bf6a56 100644 --- a/android/singleton.go +++ b/android/singleton.go @@ -35,7 +35,7 @@ type SingletonContext interface { // Allows generating build actions for `referer` based on the metadata for `name` deferred until the singleton context. ModuleVariantsFromName(referer Module, name string) []Module - moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) + otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) ModuleErrorf(module blueprint.Module, format string, args ...interface{}) Errorf(format string, args ...interface{}) @@ -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 { @@ -177,7 +181,7 @@ func (s *singletonContextAdaptor) Build(pctx PackageContext, params BuildParams) } func (s *singletonContextAdaptor) Phony(name string, deps ...Path) { - addPhony(s.Config(), name, deps...) + addSingletonPhony(s.Config(), name, deps...) } func (s *singletonContextAdaptor) SetOutDir(pctx PackageContext, value string) { @@ -279,10 +283,14 @@ func (s *singletonContextAdaptor) ModuleVariantsFromName(referer Module, name st return result } -func (s *singletonContextAdaptor) moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) { +func (s *singletonContextAdaptor) otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) { return s.SingletonContext.ModuleProvider(module, provider) } 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.go b/android/singleton_module.go index 235173809..43028e857 100644 --- a/android/singleton_module.go +++ b/android/singleton_module.go @@ -68,7 +68,7 @@ type SingletonModuleBase struct { func (smb *SingletonModuleBase) GenerateBuildActions(ctx blueprint.ModuleContext) { smb.lock.Lock() if smb.variant != "" { - ctx.ModuleErrorf("GenerateAndroidBuildActions already called for variant %q, SingletonModules can only have one variant", smb.variant) + ctx.ModuleErrorf("GenerateAndroidBuildActions already called for variant %q, SingletonModules can only have one variant", smb.variant) } smb.variant = ctx.ModuleSubDir() smb.lock.Unlock() 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/soong_config_modules.go b/android/soong_config_modules.go index 38db92995..e0b1d7cbe 100644 --- a/android/soong_config_modules.go +++ b/android/soong_config_modules.go @@ -463,57 +463,6 @@ func loadSoongConfigModuleTypeDefinition(ctx LoadHookContext, from string) map[s }).(map[string]blueprint.ModuleFactory) } -// tracingConfig is a wrapper to soongconfig.SoongConfig which records all accesses to SoongConfig. -type tracingConfig struct { - config soongconfig.SoongConfig - boolSet map[string]bool - stringSet map[string]string - isSetSet map[string]bool -} - -func (c *tracingConfig) Bool(name string) bool { - c.boolSet[name] = c.config.Bool(name) - return c.boolSet[name] -} - -func (c *tracingConfig) String(name string) string { - c.stringSet[name] = c.config.String(name) - return c.stringSet[name] -} - -func (c *tracingConfig) IsSet(name string) bool { - c.isSetSet[name] = c.config.IsSet(name) - return c.isSetSet[name] -} - -func (c *tracingConfig) getTrace() soongConfigTrace { - ret := soongConfigTrace{} - - for k, v := range c.boolSet { - ret.Bools = append(ret.Bools, fmt.Sprintf("%q:%t", k, v)) - } - for k, v := range c.stringSet { - ret.Strings = append(ret.Strings, fmt.Sprintf("%q:%q", k, v)) - } - for k, v := range c.isSetSet { - ret.IsSets = append(ret.IsSets, fmt.Sprintf("%q:%t", k, v)) - } - - return ret -} - -func newTracingConfig(config soongconfig.SoongConfig) *tracingConfig { - c := tracingConfig{ - config: config, - boolSet: make(map[string]bool), - stringSet: make(map[string]string), - isSetSet: make(map[string]bool), - } - return &c -} - -var _ soongconfig.SoongConfig = (*tracingConfig)(nil) - // configModuleFactory takes an existing soongConfigModuleFactory and a // ModuleType to create a new ModuleFactory that uses a custom loadhook. func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfig.ModuleType) blueprint.ModuleFactory { @@ -561,8 +510,8 @@ func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfi // conditional on Soong config variables by reading the product // config variables from Make. AddLoadHook(module, func(ctx LoadHookContext) { - tracingConfig := newTracingConfig(ctx.Config().VendorConfig(moduleType.ConfigNamespace)) - newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, tracingConfig) + config := ctx.Config().VendorConfig(moduleType.ConfigNamespace) + newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, config) if err != nil { ctx.ModuleErrorf("%s", err) return @@ -570,8 +519,6 @@ func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfi for _, ps := range newProps { ctx.AppendProperties(ps) } - - module.(Module).base().commonProperties.SoongConfigTrace = tracingConfig.getTrace() }) return module, props } diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go index a6b2c51c6..04aafdeee 100644 --- a/android/soong_config_modules_test.go +++ b/android/soong_config_modules_test.go @@ -16,7 +16,6 @@ package android import ( "fmt" - "path/filepath" "testing" ) @@ -506,197 +505,3 @@ func TestSoongConfigModuleSingletonModule(t *testing.T) { }) } } - -func TestSoongConfigModuleTrace(t *testing.T) { - bp := ` - soong_config_module_type { - name: "acme_test", - module_type: "test", - config_namespace: "acme", - variables: ["board", "feature1", "FEATURE3", "unused_string_var"], - bool_variables: ["feature2", "unused_feature", "always_true"], - value_variables: ["size", "unused_size"], - properties: ["cflags", "srcs", "defaults"], - } - - soong_config_module_type { - name: "acme_test_defaults", - module_type: "test_defaults", - config_namespace: "acme", - variables: ["board", "feature1", "FEATURE3", "unused_string_var"], - bool_variables: ["feature2", "unused_feature", "always_true"], - value_variables: ["size", "unused_size"], - properties: ["cflags", "srcs", "defaults"], - } - - soong_config_string_variable { - name: "board", - values: ["soc_a", "soc_b", "soc_c"], - } - - soong_config_string_variable { - name: "unused_string_var", - values: ["a", "b"], - } - - soong_config_bool_variable { - name: "feature1", - } - - soong_config_bool_variable { - name: "FEATURE3", - } - - test_defaults { - name: "test_defaults", - cflags: ["DEFAULT"], - } - - test { - name: "normal", - defaults: ["test_defaults"], - } - - acme_test { - name: "board_1", - defaults: ["test_defaults"], - soong_config_variables: { - board: { - soc_a: { - cflags: ["-DSOC_A"], - }, - }, - }, - } - - acme_test { - name: "board_2", - defaults: ["test_defaults"], - soong_config_variables: { - board: { - soc_a: { - cflags: ["-DSOC_A"], - }, - }, - }, - } - - acme_test { - name: "size", - defaults: ["test_defaults"], - soong_config_variables: { - size: { - cflags: ["-DSIZE=%s"], - }, - }, - } - - acme_test { - name: "board_and_size", - defaults: ["test_defaults"], - soong_config_variables: { - board: { - soc_a: { - cflags: ["-DSOC_A"], - }, - }, - size: { - cflags: ["-DSIZE=%s"], - }, - }, - } - - acme_test_defaults { - name: "board_defaults", - soong_config_variables: { - board: { - soc_a: { - cflags: ["-DSOC_A"], - }, - }, - }, - } - - acme_test_defaults { - name: "size_defaults", - soong_config_variables: { - size: { - cflags: ["-DSIZE=%s"], - }, - }, - } - - test { - name: "board_and_size_with_defaults", - defaults: ["board_defaults", "size_defaults"], - } - ` - - fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer { - return FixtureModifyProductVariables(func(variables FixtureProductVariables) { - variables.VendorVars = vars - }) - } - - preparer := fixtureForVendorVars(map[string]map[string]string{ - "acme": { - "board": "soc_a", - "size": "42", - "feature1": "true", - "feature2": "false", - // FEATURE3 unset - "unused_feature": "true", // unused - "unused_size": "1", // unused - "unused_string_var": "a", // unused - "always_true": "true", - }, - }) - - t.Run("soong config trace hash", func(t *testing.T) { - result := GroupFixturePreparers( - preparer, - PrepareForTestWithDefaults, - PrepareForTestWithSoongConfigModuleBuildComponents, - prepareForSoongConfigTestModule, - FixtureRegisterWithContext(func(ctx RegistrationContext) { - ctx.FinalDepsMutators(registerSoongConfigTraceMutator) - }), - FixtureWithRootAndroidBp(bp), - ).RunTest(t) - - // Hashes of modules not using soong config should be empty - normal := result.ModuleForTests("normal", "").Module().(*soongConfigTestModule) - AssertDeepEquals(t, "normal hash", normal.base().commonProperties.SoongConfigTraceHash, "") - AssertDeepEquals(t, "normal hash out", normal.outputPath.RelativeToTop().String(), "out/soong/.intermediates/normal/test") - - board1 := result.ModuleForTests("board_1", "").Module().(*soongConfigTestModule) - board2 := result.ModuleForTests("board_2", "").Module().(*soongConfigTestModule) - size := result.ModuleForTests("size", "").Module().(*soongConfigTestModule) - - // Trace mutator sets soong config trace hash correctly - board1Hash := board1.base().commonProperties.SoongConfigTrace.hash() - board1Output := board1.outputPath.RelativeToTop().String() - AssertDeepEquals(t, "board hash calc", board1Hash, board1.base().commonProperties.SoongConfigTraceHash) - AssertDeepEquals(t, "board hash path", board1Output, filepath.Join("out/soong/.intermediates/board_1", board1Hash, "test")) - - sizeHash := size.base().commonProperties.SoongConfigTrace.hash() - sizeOutput := size.outputPath.RelativeToTop().String() - AssertDeepEquals(t, "size hash calc", sizeHash, size.base().commonProperties.SoongConfigTraceHash) - AssertDeepEquals(t, "size hash path", sizeOutput, filepath.Join("out/soong/.intermediates/size", sizeHash, "test")) - - // Trace should be identical for modules using the same set of variables - AssertDeepEquals(t, "board trace", board1.base().commonProperties.SoongConfigTrace, board2.base().commonProperties.SoongConfigTrace) - AssertDeepEquals(t, "board hash", board1.base().commonProperties.SoongConfigTraceHash, board2.base().commonProperties.SoongConfigTraceHash) - - // Trace hash should be different for different sets of soong variables - AssertBoolEquals(t, "board hash not equal to size hash", board1.base().commonProperties.SoongConfigTraceHash == size.commonProperties.SoongConfigTraceHash, false) - - boardSize := result.ModuleForTests("board_and_size", "").Module().(*soongConfigTestModule) - boardSizeDefaults := result.ModuleForTests("board_and_size_with_defaults", "").Module() - - // Trace should propagate - AssertDeepEquals(t, "board_size hash calc", boardSize.base().commonProperties.SoongConfigTrace.hash(), boardSize.base().commonProperties.SoongConfigTraceHash) - AssertDeepEquals(t, "board_size trace", boardSize.base().commonProperties.SoongConfigTrace, boardSizeDefaults.base().commonProperties.SoongConfigTrace) - AssertDeepEquals(t, "board_size hash", boardSize.base().commonProperties.SoongConfigTraceHash, boardSizeDefaults.base().commonProperties.SoongConfigTraceHash) - }) -} diff --git a/android/soongconfig/Android.bp b/android/soongconfig/Android.bp index 8fe1ff1eb..5a6df2684 100644 --- a/android/soongconfig/Android.bp +++ b/android/soongconfig/Android.bp @@ -9,7 +9,6 @@ bootstrap_go_package { "blueprint", "blueprint-parser", "blueprint-proptools", - "soong-bazel", "soong-starlark-format", ], srcs: [ 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/test_suites.go b/android/test_suites.go index ff75f26bb..936d2b651 100644 --- a/android/test_suites.go +++ b/android/test_suites.go @@ -47,7 +47,8 @@ func (t *testSuiteFiles) GenerateBuildActions(ctx SingletonContext) { files[testSuite] = make(map[string]InstallPaths) } name := ctx.ModuleName(m) - files[testSuite][name] = append(files[testSuite][name], tsm.FilesToInstall()...) + files[testSuite][name] = append(files[testSuite][name], + OtherModuleProviderOrDefault(ctx, tsm, InstallFilesProvider).InstallFiles...) } } }) diff --git a/android/testing.go b/android/testing.go index 8dd467dce..196b22e3e 100644 --- a/android/testing.go +++ b/android/testing.go @@ -126,6 +126,10 @@ var PrepareForTestWithMakevars = FixtureRegisterWithContext(func(ctx Registratio ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc) }) +var PrepareForTestVintfFragmentModules = FixtureRegisterWithContext(func(ctx RegistrationContext) { + registerVintfFragmentComponents(ctx) +}) + // Test fixture preparer that will register most java build components. // // Singletons and mutators should only be added here if they are needed for a majority of java @@ -149,6 +153,7 @@ var PrepareForTestWithAndroidBuildComponents = GroupFixturePreparers( PrepareForTestWithPackageModule, PrepareForTestWithPrebuilts, PrepareForTestWithVisibility, + PrepareForTestVintfFragmentModules, ) // Prepares an integration test with all build components from the android package. @@ -174,6 +179,16 @@ var PrepareForTestDisallowNonExistentPaths = FixtureModifyConfig(func(config Con config.TestAllowNonExistentPaths = false }) +// PrepareForTestWithBuildFlag returns a FixturePreparer that sets the given flag to the given value. +func PrepareForTestWithBuildFlag(flag, value string) FixturePreparer { + return FixtureModifyProductVariables(func(variables FixtureProductVariables) { + if variables.BuildFlags == nil { + variables.BuildFlags = make(map[string]string) + } + variables.BuildFlags[flag] = value + }) +} + func NewTestArchContext(config Config) *TestContext { ctx := NewTestContext(config) ctx.preDeps = append(ctx.preDeps, registerArchMutator) @@ -202,7 +217,7 @@ func (ctx *TestContext) HardCodedPreArchMutators(f RegisterMutatorFunc) { ctx.PreArchMutators(f) } -func (ctx *TestContext) moduleProvider(m blueprint.Module, p blueprint.AnyProviderKey) (any, bool) { +func (ctx *TestContext) otherModuleProvider(m blueprint.Module, p blueprint.AnyProviderKey) (any, bool) { return ctx.Context.ModuleProvider(m, p) } @@ -220,7 +235,7 @@ func (ctx *TestContext) FinalDepsMutators(f RegisterMutatorFunc) { func (ctx *TestContext) OtherModuleProviderAdaptor() OtherModuleProviderContext { return NewOtherModuleProviderAdaptor(func(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) { - return ctx.moduleProvider(module, provider) + return ctx.otherModuleProvider(module, provider) }) } @@ -822,15 +837,15 @@ func newBaseTestingComponent(config Config, provider testBuildProvider) baseTest // containing at most one instance of the temporary build directory at the start of the path while // this assumes that there can be any number at any position. func normalizeStringRelativeToTop(config Config, s string) string { - // The soongOutDir usually looks something like: /tmp/testFoo2345/001 + // The outDir usually looks something like: /tmp/testFoo2345/001 // - // Replace any usage of the soongOutDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with + // Replace any usage of the outDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with // "out/soong". outSoongDir := filepath.Clean(config.soongOutDir) re := regexp.MustCompile(`\Q` + outSoongDir + `\E\b`) s = re.ReplaceAllString(s, "out/soong") - // Replace any usage of the soongOutDir/.. with out, e.g. replace "/tmp/testFoo2345" with + // Replace any usage of the outDir/.. with out, e.g. replace "/tmp/testFoo2345" with // "out". This must come after the previous replacement otherwise this would replace // "/tmp/testFoo2345/001" with "out/001" instead of "out/soong". outDir := filepath.Dir(outSoongDir) @@ -1018,28 +1033,21 @@ func (m TestingModule) VariablesForTestsRelativeToTop() map[string]string { return normalizeStringMapRelativeToTop(m.config, m.module.VariablesForTests()) } -// OutputFiles first checks if module base outputFiles property has any output +// OutputFiles checks if module base outputFiles property has any output // files can be used to return. -// If not, it calls OutputFileProducer.OutputFiles on the -// encapsulated module, exits the test immediately if there is an error and +// Exits the test immediately if there is an error and // otherwise returns the result of calling Paths.RelativeToTop // on the returned Paths. -func (m TestingModule) OutputFiles(t *testing.T, tag string) Paths { - // TODO: add non-empty-string tag case and remove OutputFileProducer part - if tag == "" && m.module.base().outputFiles.DefaultOutputFiles != nil { - return m.module.base().outputFiles.DefaultOutputFiles.RelativeToTop() +func (m TestingModule) OutputFiles(ctx *TestContext, t *testing.T, tag string) Paths { + outputFiles := OtherModuleProviderOrDefault(ctx.OtherModuleProviderAdaptor(), m.Module(), OutputFilesProvider) + if tag == "" && outputFiles.DefaultOutputFiles != nil { + return outputFiles.DefaultOutputFiles.RelativeToTop() + } else if taggedOutputFiles, hasTag := outputFiles.TaggedOutputFiles[tag]; hasTag { + return taggedOutputFiles.RelativeToTop() } - producer, ok := m.module.(OutputFileProducer) - if !ok { - t.Fatalf("%q must implement OutputFileProducer\n", m.module.Name()) - } - paths, err := producer.OutputFiles(tag) - if err != nil { - t.Fatal(err) - } - - return paths.RelativeToTop() + t.Fatal(fmt.Errorf("No test output file has been set for tag %q", tag)) + return nil } // TestingSingleton is wrapper around an android.Singleton that provides methods to find information about individual @@ -1241,8 +1249,14 @@ func StringPathRelativeToTop(soongOutDir string, path string) string { } if isRel { - // The path is in the soong out dir so indicate that in the relative path. - return filepath.Join("out/soong", rel) + if strings.HasSuffix(soongOutDir, testOutSoongSubDir) { + // The path is in the soong out dir so indicate that in the relative path. + return filepath.Join(TestOutSoongDir, rel) + } else { + // Handle the PathForArbitraryOutput case + return filepath.Join(testOutDir, rel) + + } } // Check to see if the path is relative to the top level out dir. @@ -1312,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/util.go b/android/util.go index e21e66b88..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. @@ -201,6 +236,12 @@ func ListSetDifference[T comparable](l1, l2 []T) (bool, []T, []T) { return listsDiffer, diff1, diff2 } +// Returns true if the two lists have common elements. +func HasIntersection[T comparable](l1, l2 []T) bool { + _, a, b := ListSetDifference(l1, l2) + return len(a)+len(b) < len(setFromList(l1))+len(setFromList(l2)) +} + // Returns true if the given string s is prefixed with any string in the given prefix list. func HasAnyPrefix(s string, prefixList []string) bool { for _, prefix := range prefixList { diff --git a/android/util_test.go b/android/util_test.go index 8e73d835c..b76ffcfea 100644 --- a/android/util_test.go +++ b/android/util_test.go @@ -818,3 +818,100 @@ func TestReverseSlice(t *testing.T) { }) } } + +var hasIntersectionTestCases = []struct { + name string + l1 []string + l2 []string + expected bool +}{ + { + name: "empty", + l1: []string{"a", "b", "c"}, + l2: []string{}, + expected: false, + }, + { + name: "both empty", + l1: []string{}, + l2: []string{}, + expected: false, + }, + { + name: "identical", + l1: []string{"a", "b", "c"}, + l2: []string{"a", "b", "c"}, + expected: true, + }, + { + name: "duplicates", + l1: []string{"a", "a", "a"}, + l2: []string{"a", "b", "c"}, + expected: true, + }, + { + name: "duplicates with no intersection", + l1: []string{"d", "d", "d", "d"}, + l2: []string{"a", "b", "c"}, + expected: false, + }, +} + +func TestHasIntersection(t *testing.T) { + for _, testCase := range hasIntersectionTestCases { + t.Run(testCase.name, func(t *testing.T) { + hasIntersection := HasIntersection(testCase.l1, testCase.l2) + if !reflect.DeepEqual(hasIntersection, testCase.expected) { + t.Errorf("expected %#v, got %#v", testCase.expected, hasIntersection) + } + }) + } +} + +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 cd26405ab..c03d178af 100644 --- a/android/variable.go +++ b/android/variable.go @@ -131,6 +131,7 @@ type variableProperties struct { // are used for dogfooding and performance testing, and should be as similar to user builds // as possible. Debuggable struct { + Apk *string Cflags []string Cppflags []string Init_rc []string @@ -142,9 +143,11 @@ type variableProperties struct { Keep_symbols *bool Keep_symbols_and_debug_frame *bool } - Static_libs []string - Whole_static_libs []string - Shared_libs []string + Static_libs []string + Exclude_static_libs []string + Whole_static_libs []string + Shared_libs []string + Jni_libs []string Cmdline []string @@ -196,10 +199,10 @@ type variableProperties struct { // release_aidl_use_unfrozen is "true" when a device can // use the unfrozen versions of AIDL interfaces. Release_aidl_use_unfrozen struct { - Cflags []string - Cmd *string - Required []string - Vintf_fragments []string + Cflags []string + Cmd *string + Required []string + Vintf_fragment_modules []string } } `android:"arch_variant"` } @@ -248,8 +251,6 @@ type ProductVariables struct { VendorApiLevel *string `json:",omitempty"` - RecoverySnapshotVersion *string `json:",omitempty"` - DeviceSecondaryArch *string `json:",omitempty"` DeviceSecondaryArchVariant *string `json:",omitempty"` DeviceSecondaryCpuVariant *string `json:",omitempty"` @@ -285,8 +286,10 @@ type ProductVariables struct { AAPTPreferredConfig *string `json:",omitempty"` AAPTPrebuiltDPI []string `json:",omitempty"` - DefaultAppCertificate *string `json:",omitempty"` - MainlineSepolicyDevCertificates *string `json:",omitempty"` + DefaultAppCertificate *string `json:",omitempty"` + ExtraOtaKeys []string `json:",omitempty"` + ExtraOtaRecoveryKeys []string `json:",omitempty"` + MainlineSepolicyDevCertificates *string `json:",omitempty"` AppsDefaultVersionName *string `json:",omitempty"` @@ -304,6 +307,7 @@ type ProductVariables struct { HostStaticBinaries *bool `json:",omitempty"` Binder32bit *bool `json:",omitempty"` UseGoma *bool `json:",omitempty"` + UseABFS *bool `json:",omitempty"` UseRBE *bool `json:",omitempty"` UseRBEJAVAC *bool `json:",omitempty"` UseRBER8 *bool `json:",omitempty"` @@ -383,20 +387,6 @@ type ProductVariables struct { PgoAdditionalProfileDirs []string `json:",omitempty"` - VndkSnapshotBuildArtifacts *bool `json:",omitempty"` - - DirectedVendorSnapshot bool `json:",omitempty"` - VendorSnapshotModules map[string]bool `json:",omitempty"` - - DirectedRecoverySnapshot bool `json:",omitempty"` - RecoverySnapshotModules map[string]bool `json:",omitempty"` - - VendorSnapshotDirsIncluded []string `json:",omitempty"` - VendorSnapshotDirsExcluded []string `json:",omitempty"` - RecoverySnapshotDirsExcluded []string `json:",omitempty"` - RecoverySnapshotDirsIncluded []string `json:",omitempty"` - HostFakeSnapshotEnabled bool `json:",omitempty"` - MultitreeUpdateMeta bool `json:",omitempty"` BoardVendorSepolicyDirs []string `json:",omitempty"` @@ -405,6 +395,7 @@ type ProductVariables struct { SystemExtPrivateSepolicyDirs []string `json:",omitempty"` BoardSepolicyM4Defs []string `json:",omitempty"` + BoardPlatform *string `json:",omitempty"` BoardSepolicyVers *string `json:",omitempty"` PlatformSepolicyVersion *string `json:",omitempty"` @@ -446,6 +437,9 @@ type ProductVariables struct { TargetFSConfigGen []string `json:",omitempty"` + UseSoongSystemImage *bool `json:",omitempty"` + ProductSoongDefinedSystemImage *string `json:",omitempty"` + EnforceProductPartitionInterface *bool `json:",omitempty"` EnforceInterPartitionJavaSdkLibrary *bool `json:",omitempty"` @@ -469,7 +463,6 @@ type ProductVariables struct { GenruleSandboxing *bool `json:",omitempty"` BuildBrokenEnforceSyspropOwner bool `json:",omitempty"` BuildBrokenTrebleSyspropNeverallow bool `json:",omitempty"` - BuildBrokenUsesSoongPython2Modules bool `json:",omitempty"` BuildBrokenVendorPropertyNamespace bool `json:",omitempty"` BuildBrokenIncorrectPartitionImages bool `json:",omitempty"` BuildBrokenInputDirModules []string `json:",omitempty"` @@ -539,6 +532,11 @@ type ProductVariables struct { OdmPropFiles []string `json:",omitempty"` EnableUffdGc *string `json:",omitempty"` + + BoardAvbEnable *bool `json:",omitempty"` + BoardAvbSystemAddHashtreeFooterArgs []string `json:",omitempty"` + DeviceFrameworkCompatibilityMatrixFile []string `json:",omitempty"` + DeviceProductCompatibilityMatrixFile []string `json:",omitempty"` } type PartitionQualifiedVariablesType struct { diff --git a/android/variable_test.go b/android/variable_test.go index 928bca609..73dc052d5 100644 --- a/android/variable_test.go +++ b/android/variable_test.go @@ -199,9 +199,7 @@ func TestProductVariables(t *testing.T) { ctx.RegisterModuleType("module3", testProductVariableModuleFactoryFactory(&struct { Foo []string }{})) - ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) { - ctx.BottomUp("variable", VariableMutator).Parallel() - }) + registerVariableBuildComponents(ctx) }), FixtureWithRootAndroidBp(bp), ).RunTest(t) @@ -210,14 +208,14 @@ func TestProductVariables(t *testing.T) { var testProductVariableDefaultsProperties = struct { Product_variables struct { Eng struct { - Foo []string + Foo []string `android:"arch_variant"` Bar []string - } - } + } `android:"arch_variant"` + } `android:"arch_variant"` }{} type productVariablesDefaultsTestProperties struct { - Foo []string + Foo []string `android:"arch_variant"` } type productVariablesDefaultsTestProperties2 struct { @@ -242,7 +240,7 @@ func productVariablesDefaultsTestModuleFactory() Module { module := &productVariablesDefaultsTestModule{} module.AddProperties(&module.properties) module.variableProperties = testProductVariableDefaultsProperties - InitAndroidModule(module) + InitAndroidArchModule(module, DeviceSupported, MultilibBoth) InitDefaultableModule(module) return module } @@ -324,3 +322,46 @@ func BenchmarkSliceToTypeArray(b *testing.B) { }) } } + +// Test a defaults module that supports more product variable properties than the target module. +func TestProductVariablesArch(t *testing.T) { + bp := ` + test { + name: "foo", + arch: { + arm: { + product_variables: { + eng: { + foo: ["arm"], + }, + }, + }, + arm64: { + product_variables: { + eng: { + foo: ["arm64"], + }, + }, + }, + }, + foo: ["module"], + } + ` + + result := GroupFixturePreparers( + FixtureModifyProductVariables(func(variables FixtureProductVariables) { + variables.Eng = boolPtr(true) + }), + PrepareForTestWithArchMutator, + PrepareForTestWithVariables, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("test", productVariablesDefaultsTestModuleFactory) + }), + FixtureWithRootAndroidBp(bp), + ).RunTest(t) + + foo := result.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*productVariablesDefaultsTestModule) + + want := []string{"module", "arm64"} + AssertDeepEquals(t, "foo", want, foo.properties.Foo) +} diff --git a/android/vintf_fragment.go b/android/vintf_fragment.go new file mode 100644 index 000000000..329eac974 --- /dev/null +++ b/android/vintf_fragment.go @@ -0,0 +1,84 @@ +// 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 + +type vintfFragmentProperties struct { + // Vintf fragment XML file. + Src string `android:"path"` +} + +type vintfFragmentModule struct { + ModuleBase + + properties vintfFragmentProperties + + installDirPath InstallPath + outputFilePath OutputPath +} + +func init() { + registerVintfFragmentComponents(InitRegistrationContext) +} + +func registerVintfFragmentComponents(ctx RegistrationContext) { + ctx.RegisterModuleType("vintf_fragment", vintfLibraryFactory) +} + +// vintf_fragment module processes vintf fragment file and installs under etc/vintf/manifest. +// Vintf fragment files formerly listed in vintf_fragment property would be transformed into +// this module type. +func vintfLibraryFactory() Module { + m := &vintfFragmentModule{} + m.AddProperties( + &m.properties, + ) + InitAndroidArchModule(m, DeviceSupported, MultilibFirst) + + return m +} + +func (m *vintfFragmentModule) GenerateAndroidBuildActions(ctx ModuleContext) { + builder := NewRuleBuilder(pctx, ctx) + srcVintfFragment := PathForModuleSrc(ctx, m.properties.Src) + processedVintfFragment := PathForModuleOut(ctx, srcVintfFragment.Base()) + + // Process vintf fragment source file with assemble_vintf tool + builder.Command(). + Flag("VINTF_IGNORE_TARGET_FCM_VERSION=true"). + BuiltTool("assemble_vintf"). + FlagWithInput("-i ", srcVintfFragment). + FlagWithOutput("-o ", processedVintfFragment) + + builder.Build("assemble_vintf", "Process vintf fragment "+processedVintfFragment.String()) + + m.installDirPath = PathForModuleInstall(ctx, "etc", "vintf", "manifest") + m.outputFilePath = processedVintfFragment.OutputPath + + ctx.InstallFile(m.installDirPath, processedVintfFragment.Base(), processedVintfFragment) +} + +// Make this module visible to AndroidMK so it can be referenced from modules defined from Android.mk files +func (m *vintfFragmentModule) AndroidMkEntries() []AndroidMkEntries { + return []AndroidMkEntries{{ + Class: "ETC", + OutputFile: OptionalPathForPath(m.outputFilePath), + ExtraEntries: []AndroidMkExtraEntriesFunc{ + func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) { + entries.SetString("LOCAL_MODULE_PATH", m.installDirPath.String()) + entries.SetString("LOCAL_INSTALLED_MODULE_STEM", m.outputFilePath.Base()) + }, + }, + }} +} diff --git a/android/vintf_fragment_test.go b/android/vintf_fragment_test.go new file mode 100644 index 000000000..8be534cf4 --- /dev/null +++ b/android/vintf_fragment_test.go @@ -0,0 +1,36 @@ +// 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 + +import ( + "strings" + "testing" +) + +func TestVintfManifestBuildAction(t *testing.T) { + bp := ` + vintf_fragment { + name: "test_vintf_fragment", + src: "test_vintf_file", + } + ` + + testResult := PrepareForTestWithAndroidBuildComponents.RunTestWithBp(t, bp) + + vintfFragmentBuild := testResult.TestContext.ModuleForTests("test_vintf_fragment", "android_arm64_armv8-a").Rule("assemble_vintf") + if !strings.Contains(vintfFragmentBuild.RuleParams.Command, "assemble_vintf") { + t.Errorf("Vintf_manifest build command does not process with assemble_vintf : " + vintfFragmentBuild.RuleParams.Command) + } +} 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/android_sdk/sdk_repo_host.go b/android_sdk/sdk_repo_host.go index 373e88306..a2486fdf5 100644 --- a/android_sdk/sdk_repo_host.go +++ b/android_sdk/sdk_repo_host.go @@ -46,6 +46,9 @@ type sdkRepoHost struct { outputBaseName string outputFile android.OptionalPath + + // TODO(b/357908583): Temp field, remove this once we support Android Mk providers + installFile android.InstallPath } type remapProperties struct { @@ -234,14 +237,18 @@ func (s *sdkRepoHost) GenerateAndroidBuildActions(ctx android.ModuleContext) { s.outputBaseName = name s.outputFile = android.OptionalPathForPath(outputZipFile) - ctx.InstallFile(android.PathForModuleInstall(ctx, "sdk-repo"), name+".zip", outputZipFile) + installPath := android.PathForModuleInstall(ctx, "sdk-repo") + name = name + ".zip" + ctx.InstallFile(installPath, name, outputZipFile) + // TODO(b/357908583): Temp field, remove this once we support Android Mk providers + s.installFile = installPath.Join(ctx, name) } func (s *sdkRepoHost) AndroidMk() android.AndroidMkData { return android.AndroidMkData{ Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { fmt.Fprintln(w, ".PHONY:", name, "sdk_repo", "sdk-repo-"+name) - fmt.Fprintln(w, "sdk_repo", "sdk-repo-"+name+":", strings.Join(s.FilesToInstall().Strings(), " ")) + fmt.Fprintln(w, "sdk_repo", "sdk-repo-"+name+":", s.installFile.String()) fmt.Fprintf(w, "$(call dist-for-goals,sdk_repo sdk-repo-%s,%s:%s-FILE_NAME_TAG_PLACEHOLDER.zip)\n\n", s.BaseModuleName(), s.outputFile.String(), s.outputBaseName) }, diff --git a/androidmk/parser/ast.go b/androidmk/parser/ast.go index d5d135443..c3d198f94 100644 --- a/androidmk/parser/ast.go +++ b/androidmk/parser/ast.go @@ -84,6 +84,7 @@ type Rule struct { Prerequisites *MakeString RecipePos Pos Recipe string + RecipeEndPos Pos } func (x *Rule) Dump() string { @@ -95,7 +96,7 @@ func (x *Rule) Dump() string { } func (x *Rule) Pos() Pos { return x.Target.Pos() } -func (x *Rule) End() Pos { return Pos(int(x.RecipePos) + len(x.Recipe)) } +func (x *Rule) End() Pos { return x.RecipeEndPos } type Variable struct { Name *MakeString diff --git a/androidmk/parser/parser.go b/androidmk/parser/parser.go index 8a20bb052..f2477db3a 100644 --- a/androidmk/parser/parser.go +++ b/androidmk/parser/parser.go @@ -448,6 +448,7 @@ loop: Prerequisites: prerequisites, Recipe: recipe, RecipePos: recipePos, + RecipeEndPos: p.pos(), }) } } diff --git a/androidmk/parser/parser_test.go b/androidmk/parser/parser_test.go index db3313d27..e238f8b11 100644 --- a/androidmk/parser/parser_test.go +++ b/androidmk/parser/parser_test.go @@ -86,20 +86,19 @@ endif`, }, { name: "Blank line in rule's command", - in: `all: + in: `all: echo first line echo second line`, out: []Node{ &Rule{ - Target: SimpleMakeString("all", NoPos), - RecipePos: NoPos, - Recipe: "echo first line\necho second line", + Target: SimpleMakeString("all", NoPos), + RecipePos: NoPos, + Recipe: "echo first line\necho second line", Prerequisites: SimpleMakeString("", NoPos), }, }, }, - } func TestParse(t *testing.T) { @@ -125,3 +124,25 @@ func TestParse(t *testing.T) { }) } } + +func TestRuleEnd(t *testing.T) { + name := "ruleEndTest" + in := `all: +ifeq (A, A) + echo foo + echo foo + echo foo + echo foo +endif + echo bar +` + p := NewParser(name, bytes.NewBufferString(in)) + got, errs := p.Parse() + if len(errs) != 0 { + t.Fatalf("Unexpected errors while parsing: %v", errs) + } + + if got[0].End() < got[len(got) -1].Pos() { + t.Errorf("Rule's end (%d) is smaller than directive that inside of rule's start (%v)\n", got[0].End(), got[len(got) -1].Pos()) + } +} diff --git a/apex/Android.bp b/apex/Android.bp index abae9e261..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", @@ -37,9 +36,12 @@ bootstrap_go_package { "apex_test.go", "bootclasspath_fragment_test.go", "classpath_element_test.go", + "container_test.go", "dexpreopt_bootjars_test.go", "platform_bootclasspath_test.go", "systemserver_classpath_fragment_test.go", ], pluginFor: ["soong_build"], + // Used by plugins + visibility: ["//visibility:public"], } diff --git a/apex/aconfig_test.go b/apex/aconfig_test.go index 14c0b6394..bb811f58a 100644 --- a/apex/aconfig_test.go +++ b/apex/aconfig_test.go @@ -74,6 +74,8 @@ func TestValidationAcrossContainersExportedPass(t *testing.T) { apex_available: [ "myapex", ], + sdk_version: "none", + system_modules: "none", }`, }, { @@ -122,6 +124,8 @@ func TestValidationAcrossContainersExportedPass(t *testing.T) { apex_available: [ "myapex", ], + sdk_version: "none", + system_modules: "none", }`, }, { @@ -345,6 +349,8 @@ func TestValidationAcrossContainersNotExportedFail(t *testing.T) { apex_available: [ "myapex", ], + sdk_version: "none", + system_modules: "none", }`, expectedError: `.*my_java_library_foo/myapex depends on my_java_aconfig_library_foo/otherapex/production across containers`, }, @@ -392,6 +398,8 @@ func TestValidationAcrossContainersNotExportedFail(t *testing.T) { apex_available: [ "myapex", ], + sdk_version: "none", + system_modules: "none", }`, expectedError: `.*my_android_app_foo/myapex depends on my_java_aconfig_library_foo/otherapex/production across containers`, }, @@ -693,6 +701,8 @@ func TestValidationAcrossContainersNotExportedFail(t *testing.T) { apex_available: [ "myapex", ], + sdk_version: "none", + system_modules: "none", }`, expectedError: `.*my_android_app_foo/myapex depends on my_java_aconfig_library_foo/otherapex/production across containers`, }, @@ -769,6 +779,8 @@ func TestValidationNotPropagateAcrossShared(t *testing.T) { apex_available: [ "myapex", ], + sdk_version: "none", + system_modules: "none", }`, }, } diff --git a/apex/androidmk.go b/apex/androidmk.go index 4112108dd..933682ab1 100644 --- a/apex/androidmk.go +++ b/apex/androidmk.go @@ -136,6 +136,11 @@ func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, moduleDir st fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", filepath.Join(modulePath, fi.stem())) fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", fi.builtFile.String()+":"+filepath.Join(modulePath, fi.stem())) fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String()) + if fi.checkbuildTarget != nil { + fmt.Fprintln(w, "LOCAL_CHECKED_MODULE :=", fi.checkbuildTarget.String()) + } else { + fmt.Fprintln(w, "LOCAL_CHECKED_MODULE :=", fi.builtFile.String()) + } fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.nameInMake()) if fi.module != nil { // This apexFile's module comes from Soong diff --git a/apex/apex.go b/apex/apex.go index 14c36b71a..d3c1ed016 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() } @@ -158,10 +151,6 @@ type apexBundleProperties struct { // Default: true. Installable *bool - // If set true, VNDK libs are considered as stable libs and are not included in this APEX. - // Should be only used in non-system apexes (e.g. vendor: true). Default is false. - Use_vndk_as_stable *bool - // The type of filesystem to use. Either 'ext4', 'f2fs' or 'erofs'. Default 'ext4'. Payload_fs_type *string @@ -169,10 +158,6 @@ type apexBundleProperties struct { // Default is false. Ignore_system_library_special_case *bool - // Whenever apex_payload.img of the APEX should include dm-verity hashtree. - // Default value is true. - Generate_hashtree *bool - // Whenever apex_payload.img of the APEX should not be dm-verity signed. Should be only // used in tests. Test_only_unsigned_payload *bool @@ -213,6 +198,50 @@ type apexBundleProperties struct { } type ApexNativeDependencies struct { + // List of native libraries that are embedded inside this APEX. + Native_shared_libs proptools.Configurable[[]string] + + // List of JNI libraries that are embedded inside this APEX. + Jni_libs []string + + // List of rust dyn libraries that are embedded inside this APEX. + Rust_dyn_libs []string + + // List of native executables that are embedded inside this APEX. + Binaries proptools.Configurable[[]string] + + // List of native tests that are embedded inside this APEX. + Tests []string + + // List of filesystem images that are embedded inside this APEX bundle. + Filesystems []string + + // List of prebuilt_etcs that are embedded inside this APEX bundle. + Prebuilts proptools.Configurable[[]string] + + // List of native libraries to exclude from this APEX. + Exclude_native_shared_libs []string + + // List of JNI libraries to exclude from this APEX. + Exclude_jni_libs []string + + // List of rust dyn libraries to exclude from this APEX. + Exclude_rust_dyn_libs []string + + // List of native executables to exclude from this APEX. + Exclude_binaries []string + + // List of native tests to exclude from this APEX. + Exclude_tests []string + + // List of filesystem images to exclude from this APEX bundle. + Exclude_filesystems []string + + // List of prebuilt_etcs to exclude from this APEX bundle. + Exclude_prebuilts []string +} + +type ResolvedApexNativeDependencies struct { // List of native libraries that are embedded inside this APEX. Native_shared_libs []string @@ -223,8 +252,7 @@ type ApexNativeDependencies struct { Rust_dyn_libs []string // List of native executables that are embedded inside this APEX. - Binaries proptools.Configurable[[]string] - ResolvedBinaries []string `blueprint:"mutated"` + Binaries []string // List of native tests that are embedded inside this APEX. Tests []string @@ -233,8 +261,7 @@ type ApexNativeDependencies struct { Filesystems []string // List of prebuilt_etcs that are embedded inside this APEX bundle. - Prebuilts proptools.Configurable[[]string] - ResolvedPrebuilts []string `blueprint:"mutated"` + Prebuilts []string // List of native libraries to exclude from this APEX. Exclude_native_shared_libs []string @@ -259,14 +286,14 @@ type ApexNativeDependencies struct { } // Merge combines another ApexNativeDependencies into this one -func (a *ApexNativeDependencies) Merge(ctx android.BaseMutatorContext, b ApexNativeDependencies) { - a.Native_shared_libs = append(a.Native_shared_libs, b.Native_shared_libs...) +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.Rust_dyn_libs = append(a.Rust_dyn_libs, b.Rust_dyn_libs...) - a.ResolvedBinaries = append(a.ResolvedBinaries, b.Binaries.GetOrDefault(ctx, nil)...) + a.Binaries = append(a.Binaries, b.Binaries.GetOrDefault(ctx, nil)...) a.Tests = append(a.Tests, b.Tests...) a.Filesystems = append(a.Filesystems, b.Filesystems...) - a.ResolvedPrebuilts = append(a.ResolvedPrebuilts, b.Prebuilts.GetOrDefault(ctx, nil)...) + a.Prebuilts = append(a.Prebuilts, b.Prebuilts.GetOrDefault(ctx, nil)...) a.Exclude_native_shared_libs = append(a.Exclude_native_shared_libs, b.Exclude_native_shared_libs...) a.Exclude_jni_libs = append(a.Exclude_jni_libs, b.Exclude_jni_libs...) @@ -397,7 +424,6 @@ type apexBundle struct { android.ModuleBase android.DefaultableModuleBase android.OverridableModuleBase - multitree.ExportableModuleBase // Properties properties apexBundleProperties @@ -536,6 +562,8 @@ type apexFile struct { customStem string symlinks []string // additional symlinks + checkbuildTarget android.Path + // Info for Android.mk Module name of `module` in AndroidMk. Note the generated AndroidMk // module for apexFile is named something like .[] @@ -571,6 +599,9 @@ func newApexFile(ctx android.BaseModuleContext, builtFile android.Path, androidM module: module, } if module != nil { + if installFilesInfo, ok := android.OtherModuleProvider(ctx, module, android.InstallFilesProvider); ok { + ret.checkbuildTarget = installFilesInfo.CheckbuildTarget + } ret.moduleDir = ctx.OtherModuleDir(module) ret.partition = module.PartitionTag(ctx.DeviceConfig()) ret.requiredModuleNames = module.RequiredModuleNames(ctx) @@ -658,6 +689,8 @@ type dependencyTag struct { // If not-nil and an APEX is a member of an SDK then dependencies of that APEX with this tag will // also be added as exported members of that SDK. memberType android.SdkMemberType + + installable bool } func (d *dependencyTag) SdkMemberType(_ android.Module) android.SdkMemberType { @@ -676,18 +709,23 @@ func (d *dependencyTag) ReplaceSourceWithPrebuilt() bool { return !d.sourceOnly } +func (d *dependencyTag) InstallDepNeeded() bool { + return d.installable +} + var _ android.ReplaceSourceWithPrebuilt = &dependencyTag{} var _ android.SdkMemberDependencyTag = &dependencyTag{} var ( - androidAppTag = &dependencyTag{name: "androidApp", payload: true} - bpfTag = &dependencyTag{name: "bpf", payload: true} - certificateTag = &dependencyTag{name: "certificate"} - dclaTag = &dependencyTag{name: "dcla"} - executableTag = &dependencyTag{name: "executable", payload: true} - fsTag = &dependencyTag{name: "filesystem", payload: true} - bcpfTag = &dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true, memberType: java.BootclasspathFragmentSdkMemberType} - sscpfTag = &dependencyTag{name: "systemserverclasspathFragment", payload: true, sourceOnly: true, memberType: java.SystemServerClasspathFragmentSdkMemberType} + androidAppTag = &dependencyTag{name: "androidApp", payload: true} + bpfTag = &dependencyTag{name: "bpf", payload: true} + certificateTag = &dependencyTag{name: "certificate"} + dclaTag = &dependencyTag{name: "dcla"} + executableTag = &dependencyTag{name: "executable", payload: true} + fsTag = &dependencyTag{name: "filesystem", payload: true} + bcpfTag = &dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true, memberType: java.BootclasspathFragmentSdkMemberType} + // The dexpreopt artifacts of apex system server jars are installed onto system image. + sscpfTag = &dependencyTag{name: "systemserverclasspathFragment", payload: true, sourceOnly: true, memberType: java.SystemServerClasspathFragmentSdkMemberType, installable: true} compatConfigTag = &dependencyTag{name: "compatConfig", payload: true, sourceOnly: true, memberType: java.CompatConfigSdkMemberType} javaLibTag = &dependencyTag{name: "javaLib", payload: true} jniLibTag = &dependencyTag{name: "jniLib", payload: true} @@ -701,13 +739,12 @@ var ( ) // TODO(jiyong): shorten this function signature -func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeModules ApexNativeDependencies, target android.Target, imageVariation string) { +func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeModules ResolvedApexNativeDependencies, target android.Target, imageVariation string) { binVariations := target.Variations() libVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"}) rustLibVariations := append( target.Variations(), []blueprint.Variation{ {Mutator: "rust_libraries", Variation: "dylib"}, - {Mutator: "link", Variation: ""}, }..., ) @@ -720,7 +757,7 @@ func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeM // this module. This is required since arch variant of an APEX bundle is 'common' but it is // 'arm' or 'arm64' for native shared libs. ctx.AddFarVariationDependencies(binVariations, executableTag, - android.RemoveListFromList(nativeModules.ResolvedBinaries, nativeModules.Exclude_binaries)...) + android.RemoveListFromList(nativeModules.Binaries, nativeModules.Exclude_binaries)...) ctx.AddFarVariationDependencies(binVariations, testTag, android.RemoveListFromList(nativeModules.Tests, nativeModules.Exclude_tests)...) ctx.AddFarVariationDependencies(libVariations, jniLibTag, @@ -732,7 +769,7 @@ func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeM ctx.AddFarVariationDependencies(target.Variations(), fsTag, android.RemoveListFromList(nativeModules.Filesystems, nativeModules.Exclude_filesystems)...) ctx.AddFarVariationDependencies(target.Variations(), prebuiltTag, - android.RemoveListFromList(nativeModules.ResolvedPrebuilts, nativeModules.Exclude_prebuilts)...) + android.RemoveListFromList(nativeModules.Prebuilts, nativeModules.Exclude_prebuilts)...) } func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) { @@ -751,9 +788,9 @@ func (a *apexBundle) getImageVariationPair() (string, string) { prefix := android.CoreVariation if a.SocSpecific() || a.DeviceSpecific() { - prefix = cc.VendorVariation + prefix = android.VendorVariation } else if a.ProductSpecific() { - prefix = cc.ProductVariation + prefix = android.ProductVariation } return prefix, "" @@ -783,7 +820,7 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) { } } for i, target := range targets { - var deps ApexNativeDependencies + var deps ResolvedApexNativeDependencies // Add native modules targeting both ABIs. When multilib.* is omitted for // native_shared_libs/jni_libs/tests, it implies multilib.both @@ -800,7 +837,7 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) { if isPrimaryAbi { deps.Merge(ctx, a.properties.Multilib.First) deps.Merge(ctx, ApexNativeDependencies{ - Native_shared_libs: nil, + Native_shared_libs: proptools.NewConfigurable[[]string](nil, nil), Tests: nil, Jni_libs: nil, Binaries: a.properties.Binaries, @@ -954,24 +991,6 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) { return } - // Special casing for APEXes on non-system (e.g., vendor, odm, etc.) partitions. They are - // provided with a property named use_vndk_as_stable, which when set to true doesn't collect - // VNDK libraries as transitive dependencies. This option is useful for reducing the size of - // the non-system APEXes because the VNDK libraries won't be included (and duped) in the - // APEX, but shared across APEXes via the VNDK APEX. - useVndk := a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && mctx.Config().EnforceProductPartitionInterface()) - if proptools.Bool(a.properties.Use_vndk_as_stable) { - if !useVndk { - mctx.PropertyErrorf("use_vndk_as_stable", "not supported for system/system_ext APEXes") - } - if a.minSdkVersionValue(mctx) != "" { - mctx.PropertyErrorf("use_vndk_as_stable", "not supported when min_sdk_version is set") - } - if mctx.Failed() { - return - } - } - continueApexDepsWalk := func(child, parent android.Module) bool { am, ok := child.(android.ApexModule) if !ok || !am.CanHaveApexVariants() { @@ -989,10 +1008,6 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) { return false } - if useVndk && child.Name() == "libbinder" { - mctx.ModuleErrorf("Module %s in the vendor APEX %s should not use libbinder. Use libbinder_ndk instead.", parent.Name(), a.Name()) - } - // By default, all the transitive dependencies are collected, unless filtered out // above. return true @@ -1047,6 +1062,8 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) { InApexModules: []string{a.Name()}, // could be com.mycompany.android.foo ApexContents: []*android.ApexContents{apexContents}, TestApexes: testApexes, + BaseApexName: mctx.ModuleName(), + ApexAvailableName: proptools.String(a.properties.Apex_available_name), } mctx.WalkDeps(func(child, parent android.Module) bool { if !continueApexDepsWalk(child, parent) { @@ -1058,7 +1075,7 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) { if a.dynamic_common_lib_apex() { android.SetProvider(mctx, DCLAInfoProvider, DCLAInfo{ - ProvidedLibs: a.properties.Native_shared_libs, + ProvidedLibs: a.properties.Native_shared_libs.GetOrDefault(mctx, nil), }) } } @@ -1175,6 +1192,7 @@ var ( "test_com.android.os.statsd", "test_com.android.permission", "test_com.android.wifi", + "test_imgdiag_com.android.art", "test_jitzygote_com.android.art", // go/keep-sorted end } @@ -1373,27 +1391,6 @@ func (a *apexBundle) DepIsInSameApex(_ android.BaseModuleContext, _ android.Modu return true } -var _ android.OutputFileProducer = (*apexBundle)(nil) - -// Implements android.OutputFileProducer -func (a *apexBundle) OutputFiles(tag string) (android.Paths, error) { - switch tag { - case "", android.DefaultDistTag: - // This is the default dist path. - return android.Paths{a.outputFile}, nil - case imageApexSuffix: - // uncompressed one - if a.outputApexFile != nil { - return android.Paths{a.outputApexFile}, nil - } - fallthrough - default: - return nil, fmt.Errorf("unsupported module reference tag %q", tag) - } -} - -var _ multitree.Exportable = (*apexBundle)(nil) - func (a *apexBundle) Exportable() bool { return true } @@ -1407,7 +1404,7 @@ func (a *apexBundle) TaggedOutputs() map[string]android.Paths { var _ cc.Coverage = (*apexBundle)(nil) // Implements cc.Coverage -func (a *apexBundle) IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool { +func (a *apexBundle) IsNativeCoverageNeeded(ctx cc.IsNativeCoverageNeededContext) bool { return ctx.DeviceConfig().NativeCoverageEnabled() } @@ -1469,11 +1466,6 @@ func (a *apexBundle) installable() bool { return !a.properties.PreventInstall && (a.properties.Installable == nil || proptools.Bool(a.properties.Installable)) } -// See the generate_hashtree property -func (a *apexBundle) shouldGenerateHashtree() bool { - return proptools.BoolDefault(a.properties.Generate_hashtree, true) -} - // See the test_only_unsigned_payload property func (a *apexBundle) testOnlyShouldSkipPayloadSign() bool { return proptools.Bool(a.properties.Test_only_unsigned_payload) @@ -1533,7 +1525,7 @@ func (a *apexBundle) AddSanitizerDependencies(ctx android.BottomUpMutatorContext imageVariation := a.getImageVariation() for _, target := range ctx.MultiTargets() { if target.Arch.ArchType.Multilib == "lib64" { - addDependenciesForNativeModules(ctx, ApexNativeDependencies{ + addDependenciesForNativeModules(ctx, ResolvedApexNativeDependencies{ Native_shared_libs: []string{"libclang_rt.hwasan"}, Tests: nil, Jni_libs: nil, @@ -1685,12 +1677,10 @@ func apexFileForJavaModuleWithFile(ctx android.ModuleContext, module javaModule, if sdkLib, ok := module.(*java.SdkLibrary); ok { for _, install := range sdkLib.BuiltInstalledForApex() { af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName()) - install.PackageFile(ctx) } } else if dexpreopter, ok := module.(java.DexpreopterInterface); ok { for _, install := range dexpreopter.DexpreoptBuiltInstalledForApex() { af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName()) - install.PackageFile(ctx) } } return af @@ -1931,8 +1921,6 @@ type visitorContext struct { // visitor skips these from this list of module names unwantedTransitiveDeps []string - - aconfigFiles []android.Path } func (vctx *visitorContext) normalizeFileInfo(mctx android.ModuleContext) { @@ -1999,7 +1987,6 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, fi := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs) fi.isJniLib = isJniLib vctx.filesInfo = append(vctx.filesInfo, fi) - addAconfigFiles(vctx, ctx, child) // Collect the list of stub-providing libs except: // - VNDK libs are only for vendors // - bootstrap bionic libs are treated as provided by system @@ -2011,7 +1998,6 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, fi := apexFileForRustLibrary(ctx, ch) fi.isJniLib = isJniLib vctx.filesInfo = append(vctx.filesInfo, fi) - addAconfigFiles(vctx, ctx, child) return true // track transitive dependencies default: ctx.PropertyErrorf(propertyName, "%q is not a cc_library or cc_library_shared module", depName) @@ -2020,11 +2006,9 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, switch ch := child.(type) { case *cc.Module: vctx.filesInfo = append(vctx.filesInfo, apexFileForExecutable(ctx, ch)) - addAconfigFiles(vctx, ctx, child) return true // track transitive dependencies case *rust.Module: vctx.filesInfo = append(vctx.filesInfo, apexFileForRustExecutable(ctx, ch)) - addAconfigFiles(vctx, ctx, child) return true // track transitive dependencies default: ctx.PropertyErrorf("binaries", @@ -2064,7 +2048,6 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, return false } vctx.filesInfo = append(vctx.filesInfo, af) - addAconfigFiles(vctx, ctx, child) return true // track transitive dependencies default: ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child)) @@ -2073,14 +2056,11 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, switch ap := child.(type) { case *java.AndroidApp: vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...) - addAconfigFiles(vctx, ctx, child) return true // track transitive dependencies case *java.AndroidAppImport: vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...) - addAconfigFiles(vctx, ctx, child) case *java.AndroidTestHelperApp: vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...) - addAconfigFiles(vctx, ctx, child) case *java.AndroidAppSet: appDir := "app" if ap.Privileged() { @@ -2094,7 +2074,6 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, af := newApexFile(ctx, ap.OutputFile(), ap.BaseModuleName(), appDirName, appSet, ap) af.certificate = java.PresignedCertificate vctx.filesInfo = append(vctx.filesInfo, af) - addAconfigFiles(vctx, ctx, child) default: ctx.PropertyErrorf("apps", "%q is not an android_app module", depName) } @@ -2126,7 +2105,6 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, for _, etcFile := range filesToCopy { vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, etcFile)) } - addAconfigFiles(vctx, ctx, child) } else { ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName) } @@ -2138,20 +2116,9 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, } case testTag: if ccTest, ok := child.(*cc.Module); ok { - if ccTest.IsTestPerSrcAllTestsVariation() { - // Multiple-output test module (where `test_per_src: true`). - // - // `ccTest` is the "" ("all tests") variation of a `test_per_src` module. - // We do not add this variation to `filesInfo`, as it has no output; - // however, we do add the other variations of this module as indirect - // dependencies (see below). - } else { - // Single-output test module (where `test_per_src: false`). - af := apexFileForExecutable(ctx, ccTest) - af.class = nativeTest - vctx.filesInfo = append(vctx.filesInfo, af) - addAconfigFiles(vctx, ctx, child) - } + af := apexFileForExecutable(ctx, ccTest) + af.class = nativeTest + vctx.filesInfo = append(vctx.filesInfo, af) return true // track transitive dependencies } else { ctx.PropertyErrorf("tests", "%q is not a cc module", depName) @@ -2231,26 +2198,15 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, } vctx.filesInfo = append(vctx.filesInfo, af) - addAconfigFiles(vctx, ctx, child) return true // track transitive dependencies } else if rm, ok := child.(*rust.Module); ok { + if !android.IsDepInSameApex(ctx, am, am) { + return false + } + af := apexFileForRustLibrary(ctx, rm) af.transitiveDep = true vctx.filesInfo = append(vctx.filesInfo, af) - addAconfigFiles(vctx, ctx, child) - return true // track transitive dependencies - } - } else if cc.IsTestPerSrcDepTag(depTag) { - if ch, ok := child.(*cc.Module); ok { - af := apexFileForExecutable(ctx, ch) - // Handle modules created as `test_per_src` variations of a single test module: - // use the name of the generated test binary (`fileToCopy`) instead of the name - // of the original test module (`depName`, shared by all `test_per_src` - // variations of that module). - af.androidMkModuleName = filepath.Base(af.builtFile.String()) - // these are not considered transitive dep - af.transitiveDep = false - vctx.filesInfo = append(vctx.filesInfo, af) return true // track transitive dependencies } } else if cc.IsHeaderDepTag(depTag) { @@ -2266,10 +2222,13 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, } } else if rust.IsDylibDepTag(depTag) { if rustm, ok := child.(*rust.Module); ok && rustm.IsInstallableToApex() { + if !android.IsDepInSameApex(ctx, am, am) { + return false + } + af := apexFileForRustLibrary(ctx, rustm) af.transitiveDep = true vctx.filesInfo = append(vctx.filesInfo, af) - addAconfigFiles(vctx, ctx, child) return true // track transitive dependencies } } else if rust.IsRlibDepTag(depTag) { @@ -2288,7 +2247,6 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, return false } vctx.filesInfo = append(vctx.filesInfo, af) - addAconfigFiles(vctx, ctx, child) return true // track transitive dependencies default: ctx.PropertyErrorf("bootclasspath_fragments", @@ -2303,7 +2261,6 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, if profileAf := apexFileForJavaModuleProfile(ctx, child.(javaModule)); profileAf != nil { vctx.filesInfo = append(vctx.filesInfo, *profileAf) } - addAconfigFiles(vctx, ctx, child) return true // track transitive dependencies default: ctx.PropertyErrorf("systemserverclasspath_fragments", @@ -2321,19 +2278,6 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, return false } -func addAconfigFiles(vctx *visitorContext, ctx android.ModuleContext, module blueprint.Module) { - if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigPropagatingProviderKey); ok { - if len(dep.AconfigFiles) > 0 && dep.AconfigFiles[ctx.ModuleName()] != nil { - vctx.aconfigFiles = append(vctx.aconfigFiles, dep.AconfigFiles[ctx.ModuleName()]...) - } - } - - validationFlag := ctx.DeviceConfig().AconfigContainerValidation() - if validationFlag == "error" || validationFlag == "warning" { - android.VerifyAconfigBuildMode(ctx, ctx.ModuleName(), module, validationFlag == "error") - } -} - func (a *apexBundle) shouldCheckDuplicate(ctx android.ModuleContext) bool { // TODO(b/263308293) remove this if a.properties.IsCoverageVariant { @@ -2415,12 +2359,15 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { // 3) some fields in apexBundle struct are configured a.installDir = android.PathForModuleInstall(ctx, "apex") a.filesInfo = vctx.filesInfo - a.aconfigFiles = android.FirstUniquePaths(vctx.aconfigFiles) a.setPayloadFsType(ctx) a.setSystemLibLink(ctx) a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx) + //////////////////////////////////////////////////////////////////////////////////////////// + // 3.a) some artifacts are generated from the collected files + a.filesInfo = append(a.filesInfo, a.buildAconfigFiles(ctx)...) + //////////////////////////////////////////////////////////////////////////////////////////// // 4) generate the build rules to create the APEX. This is done in builder.go. a.buildManifest(ctx, vctx.provideNativeLibs, vctx.requireNativeLibs) @@ -2434,6 +2381,9 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.providePrebuiltInfo(ctx) a.required = a.RequiredModuleNames(ctx) + a.required = append(a.required, a.VintfFragmentModuleNames(ctx)...) + + a.setOutputFiles(ctx) } // Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file @@ -2462,6 +2412,18 @@ func (a *apexBundle) provideApexExportsInfo(ctx android.ModuleContext) { }) } +// Set output files to outputFiles property, which is later used to set the +// OutputFilesProvider +func (a *apexBundle) setOutputFiles(ctx android.ModuleContext) { + // default dist path + ctx.SetOutputFiles(android.Paths{a.outputFile}, "") + ctx.SetOutputFiles(android.Paths{a.outputFile}, android.DefaultDistTag) + // uncompressed one + if a.outputApexFile != nil { + ctx.SetOutputFiles(android.Paths{a.outputApexFile}, imageApexSuffix) + } +} + // apexBootclasspathFragmentFiles returns the list of apexFile structures defining the files that // the bootclasspath_fragment contributes to the apex. func apexBootclasspathFragmentFiles(ctx android.ModuleContext, module blueprint.Module) []apexFile { @@ -2556,7 +2518,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 } @@ -2721,12 +2682,12 @@ func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) { if a.minSdkVersionValue(ctx) == "" { ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well") } + if a.minSdkVersion(ctx).IsCurrent() { + ctx.PropertyErrorf("updatable", "updatable APEXes should not set min_sdk_version to current. Please use a finalized API level or a recognized in-development codename") + } if a.UsePlatformApis() { ctx.PropertyErrorf("updatable", "updatable APEXes can't use platform APIs") } - if proptools.Bool(a.properties.Use_vndk_as_stable) { - ctx.PropertyErrorf("use_vndk_as_stable", "updatable APEXes can't use external VNDK libs") - } if a.FutureUpdatable() { ctx.PropertyErrorf("future_updatable", "Already updatable. Remove `future_updatable: true:`") } @@ -2779,6 +2740,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. @@ -2813,13 +2780,24 @@ func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) { return false } - if to.AvailableFor(apexName) || baselineApexAvailable(apexName, toName) { + if to.AvailableFor(apexName) { return true } + + // Let's give some hint for apex_available + hint := fmt.Sprintf("%q", apexName) + + if strings.HasPrefix(apexName, "com.") && !strings.HasPrefix(apexName, "com.android.") && strings.Count(apexName, ".") >= 2 { + // In case of a partner APEX, prefix format might be an option. + components := strings.Split(apexName, ".") + components[len(components)-1] = "*" + hint += fmt.Sprintf(" or %q", strings.Join(components, ".")) + } + ctx.ModuleErrorf("%q requires %q that doesn't list the APEX under 'apex_available'."+ "\n\nDependency path:%s\n\n"+ - "Consider adding %q to 'apex_available' property of %q", - fromName, toName, ctx.GetPathString(true), apexName, toName) + "Consider adding %s to 'apex_available' property of %q", + fromName, toName, ctx.GetPathString(true), hint, toName) // Visit this module's dependencies to check and report any issues with their availability. return true }) @@ -2856,80 +2834,12 @@ func isStaticExecutableAllowed(apex string, exec string) bool { } // Collect information for opening IDE project files in java/jdeps.go. -func (a *apexBundle) IDEInfo(dpInfo *android.IdeInfo) { +func (a *apexBundle) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) { dpInfo.Deps = append(dpInfo.Deps, a.properties.Java_libs...) dpInfo.Deps = append(dpInfo.Deps, a.properties.Bootclasspath_fragments...) 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 e6ebff2c1..f405cb2fe 100644 --- a/apex/apex_singleton.go +++ b/apex/apex_singleton.go @@ -46,6 +46,9 @@ var ( Command: "cat $out.rsp | xargs cat" + // Only track non-external dependencies, i.e. those that end up in the binary " | grep -v '(external)'" + + // Allowlist androidx deps + " | grep -v '^androidx\\.'" + + " | grep -v '^prebuilt_androidx\\.'" + // Ignore comments in any of the files " | grep -v '^#'" + " | sort -u -f >$out", @@ -85,7 +88,7 @@ func (s *apexDepsInfoSingleton) GenerateBuildActions(ctx android.SingletonContex updatableFlatLists := android.Paths{} ctx.VisitAllModules(func(module android.Module) { if binaryInfo, ok := module.(android.ApexBundleDepsInfoIntf); ok { - apexInfo, _ := android.SingletonModuleProvider(ctx, module, android.ApexInfoProvider) + apexInfo, _ := android.OtherModuleProvider(ctx, module, android.ApexInfoProvider) if path := binaryInfo.FlatListPath(); path != nil { if binaryInfo.Updatable() || apexInfo.Updatable { updatableFlatLists = append(updatableFlatLists, path) @@ -152,7 +155,7 @@ func (a *apexPrebuiltInfo) GenerateBuildActions(ctx android.SingletonContext) { prebuiltInfos := []android.PrebuiltInfo{} ctx.VisitAllModules(func(m android.Module) { - prebuiltInfo, exists := android.SingletonModuleProvider(ctx, m, android.PrebuiltInfoProvider) + prebuiltInfo, exists := android.OtherModuleProvider(ctx, m, android.PrebuiltInfoProvider) // Use prebuiltInfoProvider to filter out non apex soong modules. // Use HideFromMake to filter out the unselected variants of a specific apex. if exists && !m.IsHideFromMake() { diff --git a/apex/apex_test.go b/apex/apex_test.go index 9a314472d..394f4ae23 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -267,6 +267,7 @@ func ensureNotContains(t *testing.T, result string, notExpected string) { } func ensureMatches(t *testing.T, result string, expectedRex string) { + t.Helper() ok, err := regexp.MatchString(expectedRex, result) if err != nil { t.Fatalf("regexp failure trying to match %s against `%s` expression: %s", result, expectedRex, err) @@ -277,6 +278,14 @@ func ensureMatches(t *testing.T, result string, expectedRex string) { } } +func ensureListContainsMatch(t *testing.T, result []string, expectedRex string) { + t.Helper() + p := regexp.MustCompile(expectedRex) + if android.IndexListPred(func(s string) bool { return p.MatchString(s) }, result) == -1 { + t.Errorf("%q is not found in %v", expectedRex, result) + } +} + func ensureListContains(t *testing.T, result []string, expected string) { t.Helper() if !android.InList(expected, result) { @@ -384,7 +393,7 @@ func TestBasicApex(t *testing.T) { symlink_preferred_arch: true, system_shared_libs: [], stl: "none", - apex_available: [ "myapex", "com.android.gki.*" ], + apex_available: [ "myapex" ], } rust_binary { @@ -432,14 +441,6 @@ func TestBasicApex(t *testing.T) { apex_available: ["myapex"], } - apex { - name: "com.android.gki.fake", - binaries: ["foo"], - key: "myapex.key", - file_contexts: ":myapex-file_contexts", - updatable: false, - } - cc_library_shared { name: "mylib2", srcs: ["mylib.cpp"], @@ -914,7 +915,7 @@ func TestApexWithStubs(t *testing.T) { cc_library { name: "mylib", srcs: ["mylib.cpp"], - shared_libs: ["mylib2", "mylib3"], + shared_libs: ["mylib2", "mylib3", "my_prebuilt_platform_lib", "my_prebuilt_platform_stub_only_lib"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], @@ -927,6 +928,7 @@ func TestApexWithStubs(t *testing.T) { system_shared_libs: [], stl: "none", stubs: { + symbol_file: "mylib2.map.txt", versions: ["1", "2", "3"], }, } @@ -938,6 +940,7 @@ func TestApexWithStubs(t *testing.T) { system_shared_libs: [], stl: "none", stubs: { + symbol_file: "mylib3.map.txt", versions: ["10", "11", "12"], }, apex_available: [ "myapex" ], @@ -951,6 +954,24 @@ func TestApexWithStubs(t *testing.T) { apex_available: [ "myapex" ], } + cc_prebuilt_library_shared { + name: "my_prebuilt_platform_lib", + stubs: { + symbol_file: "my_prebuilt_platform_lib.map.txt", + versions: ["1", "2", "3"], + }, + srcs: ["foo.so"], + } + + // Similar to my_prebuilt_platform_lib, but this library only provides stubs, i.e. srcs is empty + cc_prebuilt_library_shared { + name: "my_prebuilt_platform_stub_only_lib", + stubs: { + symbol_file: "my_prebuilt_platform_stub_only_lib.map.txt", + versions: ["1", "2", "3"], + } + } + rust_binary { name: "foo.rust", srcs: ["foo.rs"], @@ -1030,6 +1051,20 @@ func TestApexWithStubs(t *testing.T) { apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule") ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.shared_from_rust.so") + + // Ensure that mylib is linking with the latest version of stubs for my_prebuilt_platform_lib + ensureContains(t, mylibLdFlags, "my_prebuilt_platform_lib/android_arm64_armv8-a_shared_current/my_prebuilt_platform_lib.so") + // ... and not linking to the non-stub (impl) variant of my_prebuilt_platform_lib + ensureNotContains(t, mylibLdFlags, "my_prebuilt_platform_lib/android_arm64_armv8-a_shared/my_prebuilt_platform_lib.so") + // Ensure that genstub for platform-provided lib is invoked with --systemapi + ensureContains(t, ctx.ModuleForTests("my_prebuilt_platform_lib", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"], "--systemapi") + + // Ensure that mylib is linking with the latest version of stubs for my_prebuilt_platform_lib + ensureContains(t, mylibLdFlags, "my_prebuilt_platform_stub_only_lib/android_arm64_armv8-a_shared_current/my_prebuilt_platform_stub_only_lib.so") + // ... and not linking to the non-stub (impl) variant of my_prebuilt_platform_lib + ensureNotContains(t, mylibLdFlags, "my_prebuilt_platform_stub_only_lib/android_arm64_armv8-a_shared/my_prebuilt_platform_stub_only_lib.so") + // Ensure that genstub for platform-provided lib is invoked with --systemapi + ensureContains(t, ctx.ModuleForTests("my_prebuilt_platform_stub_only_lib", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"], "--systemapi") } func TestApexShouldNotEmbedStubVariant(t *testing.T) { @@ -1164,6 +1199,7 @@ func TestApexWithStubsWithMinSdkVersion(t *testing.T) { system_shared_libs: [], stl: "none", stubs: { + symbol_file: "mylib2.map.txt", versions: ["28", "29", "30", "current"], }, min_sdk_version: "28", @@ -1176,6 +1212,7 @@ func TestApexWithStubsWithMinSdkVersion(t *testing.T) { system_shared_libs: [], stl: "none", stubs: { + symbol_file: "mylib3.map.txt", versions: ["28", "29", "30", "current"], }, apex_available: [ "myapex" ], @@ -3648,7 +3685,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()) @@ -4848,200 +4885,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"], - } - - 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 := ` - 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"], - } - - java_library { - name: "libfoo", - } - - java_sdk_library_import { - name: "libbar", - 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") - }) - - 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", - } - - 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"), @@ -5064,23 +4907,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") @@ -5133,10 +4959,13 @@ 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"], } @@ -5152,8 +4981,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`) @@ -5169,18 +4996,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"], @@ -5201,13 +5020,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: { @@ -5230,8 +5053,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`) @@ -5292,12 +5113,14 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { name: "libfoo", jars: ["libfoo.jar"], apex_available: ["myapex"], + sdk_version: "core_current", } java_library { name: "libfoo", srcs: ["foo/bar/MyClass.java"], apex_available: ["myapex"], + sdk_version: "core_current", } java_sdk_library_import { @@ -5383,12 +5206,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"], + shared_library: false, + permitted_packages: ["libfoo"], } java_library { @@ -5396,6 +5222,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { srcs: ["foo/bar/MyClass.java"], apex_available: ["myapex"], installable: true, + sdk_version: "core_current", } java_sdk_library_import { @@ -5419,8 +5246,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`) @@ -5486,6 +5311,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { name: "libfoo", jars: ["libfoo.jar"], apex_available: ["myapex"], + sdk_version: "core_current", } java_library { @@ -5494,6 +5320,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { apex_available: ["myapex"], permitted_packages: ["foo"], installable: true, + sdk_version: "core_current", } java_sdk_library_import { @@ -5512,12 +5339,11 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { apex_available: ["myapex"], permitted_packages: ["bar"], compile_dex: true, + sdk_version: "core_current", } ` 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`) @@ -5629,8 +5455,6 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { ) 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`) @@ -5728,7 +5552,6 @@ func TestApexWithTests(t *testing.T) { updatable: false, tests: [ "mytest", - "mytests", ], } @@ -5771,25 +5594,6 @@ func TestApexWithTests(t *testing.T) { "testdata/baz" ], } - - cc_test { - name: "mytests", - gtest: false, - srcs: [ - "mytest1.cpp", - "mytest2.cpp", - "mytest3.cpp", - ], - test_per_src: true, - relative_install_path: "test", - system_shared_libs: [], - static_executable: true, - stl: "none", - data: [ - ":fg", - ":fg2", - ], - } `) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") @@ -5803,11 +5607,6 @@ func TestApexWithTests(t *testing.T) { ensureContains(t, copyCmds, "image.apex/bin/test/baz") ensureContains(t, copyCmds, "image.apex/bin/test/bar/baz") - // Ensure that test deps built with `test_per_src` are copied into apex. - ensureContains(t, copyCmds, "image.apex/bin/test/mytest1") - ensureContains(t, copyCmds, "image.apex/bin/test/mytest2") - ensureContains(t, copyCmds, "image.apex/bin/test/mytest3") - // Ensure the module is correctly translated. bundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, ctx, bundle) @@ -5817,9 +5616,6 @@ func TestApexWithTests(t *testing.T) { data.Custom(&builder, name, prefix, "", data) androidMk := builder.String() ensureContains(t, androidMk, "LOCAL_MODULE := mytest.myapex\n") - ensureContains(t, androidMk, "LOCAL_MODULE := mytest1.myapex\n") - ensureContains(t, androidMk, "LOCAL_MODULE := mytest2.myapex\n") - ensureContains(t, androidMk, "LOCAL_MODULE := mytest3.myapex\n") ensureContains(t, androidMk, "LOCAL_MODULE := myapex\n") } @@ -6134,6 +5930,7 @@ func TestApexWithTestHelperApp(t *testing.T) { name: "TesterHelpAppFoo", srcs: ["foo/bar/MyClass.java"], apex_available: [ "myapex" ], + sdk_version: "test_current", } `) @@ -6216,6 +6013,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) { @@ -6261,6 +6139,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) { @@ -6758,6 +6721,99 @@ func TestApexAvailable_CreatedForApex(t *testing.T) { } } +func TestApexAvailable_PrefixMatch(t *testing.T) { + + for _, tc := range []struct { + name string + apexAvailable string + expectedError string + }{ + { + name: "prefix matches correctly", + apexAvailable: "com.foo.*", + }, + { + name: "prefix doesn't match", + apexAvailable: "com.bar.*", + expectedError: `Consider .* "com.foo\.\*"`, + }, + { + name: "short prefix", + apexAvailable: "com.*", + expectedError: "requires two or more components", + }, + { + name: "wildcard not in the end", + apexAvailable: "com.*.foo", + expectedError: "should end with .*", + }, + { + name: "wildcard in the middle", + apexAvailable: "com.foo*.*", + expectedError: "not allowed in the middle", + }, + { + name: "hint with prefix pattern", + apexAvailable: "//apex_available:platform", + expectedError: "Consider adding \"com.foo.bar\" or \"com.foo.*\"", + }, + } { + t.Run(tc.name, func(t *testing.T) { + errorHandler := android.FixtureExpectsNoErrors + if tc.expectedError != "" { + errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(tc.expectedError) + } + context := android.GroupFixturePreparers( + prepareForApexTest, + android.FixtureMergeMockFs(android.MockFS{ + "system/sepolicy/apex/com.foo.bar-file_contexts": nil, + }), + ).ExtendWithErrorHandler(errorHandler) + + context.RunTestWithBp(t, ` + apex { + name: "com.foo.bar", + 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", + system_shared_libs: [], + apex_available: ["`+tc.apexAvailable+`"], + }`) + }) + } + testApexError(t, `Consider adding "com.foo" to`, ` + apex { + name: "com.foo", // too short for a partner apex + 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", + system_shared_libs: [], + } + `) +} + func TestOverrideApex(t *testing.T) { ctx := testApex(t, ` apex { @@ -7054,7 +7110,6 @@ func TestLegacyAndroid10Support(t *testing.T) { module := ctx.ModuleForTests("myapex", "android_common_myapex") args := module.Rule("apexRule").Args ensureContains(t, args["opt_flags"], "--manifest_json "+module.Output("apex_manifest.json").Output.String()) - ensureNotContains(t, args["opt_flags"], "--no_hashtree") // The copies of the libraries in the apex should have one more dependency than // the ones outside the apex, namely the unwinder. Ideally we should check @@ -7125,6 +7180,46 @@ func TestJavaSDKLibrary(t *testing.T) { ensureMatches(t, contents, "