diff options
123 files changed, 4082 insertions, 1780 deletions
diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go index 392e8193a..d29e3122d 100644 --- a/aconfig/aconfig_declarations.go +++ b/aconfig/aconfig_declarations.go @@ -137,18 +137,22 @@ func (module *DeclarationsModule) GenerateAndroidBuildActions(ctx android.Module 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), + } + 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: 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), - }, + Args: args, }) intermediateDumpFilePath := android.PathForModuleOut(ctx, "intermediate.txt") diff --git a/aconfig/aconfig_declarations_test.go b/aconfig/aconfig_declarations_test.go index 1fe3c862a..5201fedb1 100644 --- a/aconfig/aconfig_declarations_test.go +++ b/aconfig/aconfig_declarations_test.go @@ -69,3 +69,38 @@ func TestAconfigDeclarationsWithExportableUnset(t *testing.T) { depData, _ := android.SingletonModuleProvider(result, module, android.AconfigDeclarationsProviderKey) android.AssertBoolEquals(t, "exportable", depData.Exportable, false) } + +func TestAconfigDeclarationsWithContainer(t *testing.T) { + bp := ` + aconfig_declarations { + name: "module_name", + package: "com.example.package", + container: "com.android.foo", + srcs: [ + "foo.aconfig", + ], + } + ` + result := runTest(t, android.FixtureExpectsNoErrors, bp) + + module := result.ModuleForTests("module_name", "") + rule := module.Rule("aconfig") + android.AssertStringEquals(t, "rule must contain container", rule.Args["container"], "--container com.android.foo") +} + +func TestAconfigDeclarationsWithoutContainer(t *testing.T) { + bp := ` + aconfig_declarations { + name: "module_name", + package: "com.example.package", + srcs: [ + "foo.aconfig", + ], + } + ` + result := runTest(t, android.FixtureExpectsNoErrors, bp) + + module := result.ModuleForTests("module_name", "") + rule := module.Rule("aconfig") + android.AssertIntEquals(t, "rule must not contain container", len(rule.Args["container"]), 0) +} diff --git a/aconfig/codegen/aconfig_declarations_group.go b/aconfig/codegen/aconfig_declarations_group.go index 203b6be6f..1c91beeef 100644 --- a/aconfig/codegen/aconfig_declarations_group.go +++ b/aconfig/codegen/aconfig_declarations_group.go @@ -15,9 +15,10 @@ package codegen import ( - "android/soong/aconfig" - "android/soong/android" "fmt" + "maps" + + "android/soong/android" "github.com/google/blueprint" ) @@ -43,6 +44,7 @@ type AconfigDeclarationsGroup struct { aconfigDeclarationNames []string intermediateCacheOutputPaths android.Paths javaSrcjars android.Paths + modeInfos map[string]android.ModeInfo } type AconfigDeclarationsGroupProperties struct { @@ -76,9 +78,10 @@ func (adg *AconfigDeclarationsGroup) DepsMutator(ctx android.BottomUpMutatorCont } func (adg *AconfigDeclarationsGroup) VisitDeps(ctx android.ModuleContext) { + adg.modeInfos = make(map[string]android.ModeInfo) ctx.VisitDirectDeps(func(dep android.Module) { tag := ctx.OtherModuleDependencyTag(dep) - if provider, ok := android.OtherModuleProvider(ctx, dep, aconfig.CodegenInfoProvider); ok { + if provider, ok := android.OtherModuleProvider(ctx, dep, android.CodegenInfoProvider); ok { // aconfig declaration names and cache files are collected for all aconfig library dependencies adg.aconfigDeclarationNames = append(adg.aconfigDeclarationNames, provider.AconfigDeclarations...) @@ -88,8 +91,14 @@ func (adg *AconfigDeclarationsGroup) VisitDeps(ctx android.ModuleContext) { case aconfigDeclarationsGroupTag: // Will retrieve outputs from another language codegen modules when support is added adg.javaSrcjars = append(adg.javaSrcjars, provider.Srcjars...) + maps.Copy(adg.modeInfos, provider.ModeInfos) case javaAconfigLibraryTag: adg.javaSrcjars = append(adg.javaSrcjars, provider.Srcjars...) + maps.Copy(adg.modeInfos, provider.ModeInfos) + case ccAconfigLibraryTag: + maps.Copy(adg.modeInfos, provider.ModeInfos) + case rustAconfigLibraryTag: + maps.Copy(adg.modeInfos, provider.ModeInfos) } } }) @@ -100,10 +109,11 @@ func (adg *AconfigDeclarationsGroup) GenerateAndroidBuildActions(ctx android.Mod adg.aconfigDeclarationNames = android.FirstUniqueStrings(adg.aconfigDeclarationNames) adg.intermediateCacheOutputPaths = android.FirstUniquePaths(adg.intermediateCacheOutputPaths) - android.SetProvider(ctx, aconfig.CodegenInfoProvider, aconfig.CodegenInfo{ + android.SetProvider(ctx, android.CodegenInfoProvider, android.CodegenInfo{ AconfigDeclarations: adg.aconfigDeclarationNames, IntermediateCacheOutputPaths: adg.intermediateCacheOutputPaths, Srcjars: adg.javaSrcjars, + ModeInfos: adg.modeInfos, }) } diff --git a/aconfig/codegen/cc_aconfig_library.go b/aconfig/codegen/cc_aconfig_library.go index 50cd4de20..80e492620 100644 --- a/aconfig/codegen/cc_aconfig_library.go +++ b/aconfig/codegen/cc_aconfig_library.go @@ -146,4 +146,12 @@ func (this *CcAconfigLibraryCallbacks) GeneratorBuildActions(ctx cc.ModuleContex "mode": mode, }, }) + + android.SetProvider(ctx, android.CodegenInfoProvider, android.CodegenInfo{ + ModeInfos: map[string]android.ModeInfo{ + ctx.ModuleName(): { + Container: declarations.Container, + Mode: mode, + }}, + }) } diff --git a/aconfig/codegen/java_aconfig_library.go b/aconfig/codegen/java_aconfig_library.go index 7d7296e6a..1378dfece 100644 --- a/aconfig/codegen/java_aconfig_library.go +++ b/aconfig/codegen/java_aconfig_library.go @@ -17,7 +17,6 @@ package codegen import ( "fmt" - "android/soong/aconfig" "android/soong/android" "android/soong/java" @@ -92,12 +91,12 @@ func (callbacks *JavaAconfigDeclarationsLibraryCallbacks) GenerateSourceJarBuild if !isModeSupported(mode) { ctx.PropertyErrorf("mode", "%q is not a supported mode", mode) } - // TODO: uncomment this part after internal clean up - //if mode == "exported" && !declarations.Exportable { - // // if mode is exported, the corresponding aconfig_declaration must mark its - // // exportable property true - // ctx.PropertyErrorf("mode", "exported mode requires its aconfig_declaration has exportable prop true") - //} + + if mode == "exported" && !declarations.Exportable { + // if mode is exported, the corresponding aconfig_declaration must mark its + // exportable property true + ctx.PropertyErrorf("mode", "exported mode requires its aconfig_declaration has exportable prop true") + } ctx.Build(pctx, android.BuildParams{ Rule: javaRule, @@ -119,10 +118,15 @@ func (callbacks *JavaAconfigDeclarationsLibraryCallbacks) GenerateSourceJarBuild module.AddJarJarRenameRule(declarations.Package+".FakeFeatureFlagsImpl", "") } - android.SetProvider(ctx, aconfig.CodegenInfoProvider, aconfig.CodegenInfo{ + android.SetProvider(ctx, android.CodegenInfoProvider, android.CodegenInfo{ AconfigDeclarations: []string{declarationsModules[0].Name()}, IntermediateCacheOutputPaths: android.Paths{declarations.IntermediateCacheOutputPath}, Srcjars: android.Paths{srcJarPath}, + ModeInfos: map[string]android.ModeInfo{ + ctx.ModuleName(): { + Container: declarations.Container, + Mode: mode, + }}, }) return srcJarPath diff --git a/aconfig/codegen/rust_aconfig_library.go b/aconfig/codegen/rust_aconfig_library.go index 2ab54b6ab..3f7495be1 100644 --- a/aconfig/codegen/rust_aconfig_library.go +++ b/aconfig/codegen/rust_aconfig_library.go @@ -85,6 +85,15 @@ func (a *aconfigDecorator) GenerateSource(ctx rust.ModuleContext, deps rust.Path }, }) a.BaseSourceProvider.OutputFiles = android.Paths{generatedSource} + + android.SetProvider(ctx, android.CodegenInfoProvider, android.CodegenInfo{ + ModeInfos: map[string]android.ModeInfo{ + ctx.ModuleName(): { + Container: declarations.Container, + Mode: mode, + }}, + }) + return generatedSource } diff --git a/aconfig/init.go b/aconfig/init.go index e64429fb9..46554676b 100644 --- a/aconfig/init.go +++ b/aconfig/init.go @@ -20,20 +20,6 @@ import ( "github.com/google/blueprint" ) -type CodegenInfo struct { - // AconfigDeclarations is the name of the aconfig_declarations modules that - // the codegen module is associated with - AconfigDeclarations []string - - // Paths to the cache files of the associated aconfig_declaration modules - IntermediateCacheOutputPaths android.Paths - - // Paths to the srcjar files generated from the java_aconfig_library modules - Srcjars android.Paths -} - -var CodegenInfoProvider = blueprint.NewProvider[CodegenInfo]() - var ( pctx = android.NewPackageContext("android/soong/aconfig") @@ -42,6 +28,7 @@ var ( blueprint.RuleParams{ Command: `${aconfig} create-cache` + ` --package ${package}` + + ` ${container}` + ` ${declarations}` + ` ${values}` + ` ${default-permission}` + @@ -52,7 +39,7 @@ var ( "${aconfig}", }, Restat: true, - }, "release_version", "package", "declarations", "values", "default-permission") + }, "release_version", "package", "container", "declarations", "values", "default-permission") // For create-device-config-sysprops: Generate aconfig flag value map text file aconfigTextRule = pctx.AndroidStaticRule("aconfig_text", diff --git a/android/Android.bp b/android/Android.bp index e73f35574..03619f4b5 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -16,7 +16,6 @@ bootstrap_go_package { "soong-remoteexec", "soong-response", "soong-shared", - "soong-starlark", "soong-starlark-format", "soong-ui-metrics_proto", "soong-android-allowlists", @@ -135,6 +134,7 @@ bootstrap_go_package { "rule_builder_test.go", "sdk_version_test.go", "sdk_test.go", + "selects_test.go", "singleton_module_test.go", "soong_config_modules_test.go", "util_test.go", diff --git a/android/aconfig_providers.go b/android/aconfig_providers.go index 74c1a5ecc..fcc57e1a4 100644 --- a/android/aconfig_providers.go +++ b/android/aconfig_providers.go @@ -17,6 +17,7 @@ package android import ( "fmt" "io" + "maps" "reflect" "github.com/google/blueprint" @@ -50,6 +51,35 @@ type AconfigTransitiveDeclarationsInfo struct { var AconfigTransitiveDeclarationsInfoProvider = blueprint.NewProvider[AconfigTransitiveDeclarationsInfo]() +type ModeInfo struct { + Container string + Mode string +} +type CodegenInfo struct { + // AconfigDeclarations is the name of the aconfig_declarations modules that + // the codegen module is associated with + AconfigDeclarations []string + + // Paths to the cache files of the associated aconfig_declaration modules + IntermediateCacheOutputPaths Paths + + // Paths to the srcjar files generated from the java_aconfig_library modules + Srcjars Paths + + ModeInfos map[string]ModeInfo +} + +var CodegenInfoProvider = blueprint.NewProvider[CodegenInfo]() + +func propagateModeInfos(ctx ModuleContext, module Module, to, from map[string]ModeInfo) { + if len(from) > 0 { + depTag := ctx.OtherModuleDependencyTag(module) + if tag, ok := depTag.(PropagateAconfigValidationDependencyTag); ok && tag.PropagateAconfigValidation() { + maps.Copy(to, from) + } + } +} + // CollectDependencyAconfigFiles is used by some module types to provide finer dependency graphing than // we can do in ModuleBase. func CollectDependencyAconfigFiles(ctx ModuleContext, mergedAconfigFiles *map[string]Paths) { @@ -90,13 +120,40 @@ func SetAconfigFileMkEntries(m *ModuleBase, entries *AndroidMkEntries, aconfigFi type aconfigPropagatingDeclarationsInfo struct { AconfigFiles map[string]Paths + ModeInfos map[string]ModeInfo } var aconfigPropagatingProviderKey = blueprint.NewProvider[aconfigPropagatingDeclarationsInfo]() +func VerifyAconfigBuildMode(ctx ModuleContext, container string, module blueprint.Module, asError bool) { + if dep, ok := OtherModuleProvider(ctx, module, aconfigPropagatingProviderKey); ok { + for k, v := range dep.ModeInfos { + msg := fmt.Sprintf("%s/%s depends on %s/%s/%s across containers\n", + module.Name(), container, k, v.Container, v.Mode) + if v.Container != container && v.Mode != "exported" && v.Mode != "force-read-only" { + if asError { + ctx.ModuleErrorf(msg) + } else { + fmt.Printf("WARNING: " + msg) + } + } else { + if !asError { + fmt.Printf("PASSED: " + msg) + } + } + } + } +} + func aconfigUpdateAndroidBuildActions(ctx ModuleContext) { mergedAconfigFiles := make(map[string]Paths) + mergedModeInfos := make(map[string]ModeInfo) + ctx.VisitDirectDepsIgnoreBlueprint(func(module Module) { + if aconfig_dep, ok := OtherModuleProvider(ctx, module, CodegenInfoProvider); ok && len(aconfig_dep.ModeInfos) > 0 { + maps.Copy(mergedModeInfos, aconfig_dep.ModeInfos) + } + // If any of our dependencies have aconfig declarations (directly or propagated), then merge those and provide them. if dep, ok := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); ok { mergedAconfigFiles[dep.Container] = append(mergedAconfigFiles[dep.Container], dep.IntermediateCacheOutputPath) @@ -105,6 +162,7 @@ func aconfigUpdateAndroidBuildActions(ctx ModuleContext) { for container, v := range dep.AconfigFiles { mergedAconfigFiles[container] = append(mergedAconfigFiles[container], v...) } + propagateModeInfos(ctx, module, mergedModeInfos, dep.ModeInfos) } if dep, ok := OtherModuleProvider(ctx, module, AconfigTransitiveDeclarationsInfoProvider); ok { for container, v := range dep.AconfigFiles { @@ -120,6 +178,7 @@ func aconfigUpdateAndroidBuildActions(ctx ModuleContext) { SetProvider(ctx, aconfigPropagatingProviderKey, aconfigPropagatingDeclarationsInfo{ AconfigFiles: mergedAconfigFiles, + ModeInfos: mergedModeInfos, }) } } diff --git a/android/apex.go b/android/apex.go index 4d36a9396..87599051a 100644 --- a/android/apex.go +++ b/android/apex.go @@ -976,3 +976,18 @@ type ApexExportsInfo struct { // Map from the apex library name (without prebuilt_ prefix) to the dex file path on host LibraryNameToDexJarPathOnHost map[string]Path } + +var PrebuiltInfoProvider = blueprint.NewProvider[PrebuiltInfo]() + +// contents of prebuilt_info.json +type PrebuiltInfo struct { + // Name of the apex, without the prebuilt_ prefix + Name string + + Is_prebuilt bool + + // This is relative to root of the workspace. + // In case of mainline modules, this file contains the build_id that was used + // to generate the mainline module prebuilt. + Prebuilt_info_file_path string `json:",omitempty"` +} diff --git a/android/apex_contributions.go b/android/apex_contributions.go index c388afff9..c76d9c235 100644 --- a/android/apex_contributions.go +++ b/android/apex_contributions.go @@ -27,11 +27,13 @@ func init() { func RegisterApexContributionsBuildComponents(ctx RegistrationContext) { ctx.RegisterModuleType("apex_contributions", apexContributionsFactory) + ctx.RegisterModuleType("apex_contributions_defaults", apexContributionsDefaultsFactory) ctx.RegisterSingletonModuleType("all_apex_contributions", allApexContributionsFactory) } type apexContributions struct { ModuleBase + DefaultableModuleBase properties contributionProps } @@ -61,6 +63,7 @@ func apexContributionsFactory() Module { module := &apexContributions{} module.AddProperties(&module.properties) InitAndroidModule(module) + InitDefaultableModule(module) return module } @@ -70,6 +73,18 @@ func apexContributionsFactory() Module { func (m *apexContributions) GenerateAndroidBuildActions(ctx ModuleContext) { } +type apexContributionsDefaults struct { + ModuleBase + DefaultsModuleBase +} + +func apexContributionsDefaultsFactory() Module { + module := &apexContributionsDefaults{} + module.AddProperties(&contributionProps{}) + InitDefaultsModule(module) + return module +} + // A container for apex_contributions. // Based on product_config, it will create a dependency on the selected // apex_contributions per mainline module diff --git a/android/api_levels.go b/android/api_levels.go index 6fa4a0e41..fab5fc7bf 100644 --- a/android/api_levels.go +++ b/android/api_levels.go @@ -15,7 +15,6 @@ package android import ( - "android/soong/starlark_import" "encoding/json" "fmt" "strconv" @@ -290,6 +289,8 @@ var LastWithoutModuleLibCoreSystemModules = uncheckedFinalApiLevel(31) var ApiLevelR = uncheckedFinalApiLevel(30) +var ApiLevelUpsideDownCake = uncheckedFinalApiLevel(34) + // ReplaceFinalizedCodenames returns the API level number associated with that API level // if the `raw` input is the codename of an API level has been finalized. // If the input is *not* a finalized codename, the input is returned unmodified. @@ -440,7 +441,28 @@ func GetApiLevelsJson(ctx PathContext) WritablePath { } func getApiLevelsMapReleasedVersions() (map[string]int, error) { - return starlark_import.GetStarlarkValue[map[string]int]("api_levels_released_versions") + return map[string]int{ + "G": 9, + "I": 14, + "J": 16, + "J-MR1": 17, + "J-MR2": 18, + "K": 19, + "L": 21, + "L-MR1": 22, + "M": 23, + "N": 24, + "N-MR1": 25, + "O": 26, + "O-MR1": 27, + "P": 28, + "Q": 29, + "R": 30, + "S": 31, + "S-V2": 32, + "Tiramisu": 33, + "UpsideDownCake": 34, + }, nil } var finalCodenamesMapKey = NewOnceKey("FinalCodenamesMap") diff --git a/android/base_module_context.go b/android/base_module_context.go index b9c115349..dd38a4ec8 100644 --- a/android/base_module_context.go +++ b/android/base_module_context.go @@ -16,9 +16,11 @@ package android import ( "fmt" - "github.com/google/blueprint" "regexp" "strings" + + "github.com/google/blueprint" + "github.com/google/blueprint/parser" ) // BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns @@ -214,6 +216,10 @@ type BaseModuleContext interface { // getMissingDependencies returns the list of missing dependencies. // Calling this function prevents adding new dependencies. getMissingDependencies() []string + + // EvaluateConfiguration makes ModuleContext a valid proptools.ConfigurableEvaluator, so this context + // can be used to evaluate the final value of Configurable properties. + EvaluateConfiguration(parser.SelectType, string) (string, bool) } type baseModuleContext struct { @@ -564,3 +570,32 @@ func (b *baseModuleContext) GetPathString(skipFirst bool) string { } return sb.String() } + +func (m *baseModuleContext) EvaluateConfiguration(ty parser.SelectType, condition string) (string, bool) { + switch ty { + case parser.SelectTypeReleaseVariable: + if v, ok := m.Config().productVariables.BuildFlags[condition]; ok { + return v, true + } + return "", false + case parser.SelectTypeProductVariable: + // TODO(b/323382414): Might add these on a case-by-case basis + m.ModuleErrorf("TODO(b/323382414): Product variables are not yet supported in selects") + return "", false + case parser.SelectTypeSoongConfigVariable: + parts := strings.Split(condition, ":") + namespace := parts[0] + variable := parts[1] + if n, ok := m.Config().productVariables.VendorVars[namespace]; ok { + if v, ok := n[variable]; ok { + return v, true + } + } + return "", false + case parser.SelectTypeVariant: + m.ModuleErrorf("TODO(b/323382414): Variants are not yet supported in selects") + return "", false + default: + panic("Should be unreachable") + } +} diff --git a/android/config.go b/android/config.go index 936d1d3ae..10c30d4df 100644 --- a/android/config.go +++ b/android/config.go @@ -18,7 +18,6 @@ package android // product variables necessary for soong_build's operation. import ( - "android/soong/shared" "encoding/json" "fmt" "os" @@ -30,6 +29,8 @@ import ( "sync" "unicode" + "android/soong/shared" + "github.com/google/blueprint" "github.com/google/blueprint/bootstrap" "github.com/google/blueprint/pathtools" @@ -167,7 +168,10 @@ func (c Config) DisableHiddenApiChecks() bool { // Thus, verify_overlaps is disabled when RELEASE_DEFAULT_MODULE_BUILD_FROM_SOURCE is set to false. // TODO(b/308174018): Re-enable verify_overlaps for both builr from source/mainline prebuilts. func (c Config) DisableVerifyOverlaps() bool { - return c.IsEnvTrue("DISABLE_VERIFY_OVERLAPS") || !c.ReleaseDefaultModuleBuildFromSource() + if c.IsEnvFalse("DISABLE_VERIFY_OVERLAPS") && c.ReleaseDisableVerifyOverlaps() { + panic("The current release configuration does not support verify_overlaps. DISABLE_VERIFY_OVERLAPS cannot be set to false") + } + return c.IsEnvTrue("DISABLE_VERIFY_OVERLAPS") || c.ReleaseDisableVerifyOverlaps() || !c.ReleaseDefaultModuleBuildFromSource() } // MaxPageSizeSupported returns the max page size supported by the device. This @@ -208,6 +212,10 @@ func (c Config) ReleaseDefaultModuleBuildFromSource() bool { Bool(c.config.productVariables.ReleaseDefaultModuleBuildFromSource) } +func (c Config) ReleaseDisableVerifyOverlaps() bool { + return c.config.productVariables.GetBuildFlagBool("RELEASE_DISABLE_VERIFY_OVERLAPS_CHECK") +} + // Enables flagged apis annotated with READ_WRITE aconfig flags to be included in the stubs // and hiddenapi flags so that they are accessible at runtime func (c Config) ReleaseExportRuntimeApis() bool { @@ -638,9 +646,12 @@ func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) "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": {}, @@ -1342,13 +1353,16 @@ func (c *config) PrevVendorApiLevel() string { panic(fmt.Errorf("Cannot parse vendor API level %s to an integer: %s", c.VendorApiLevel(), err)) } - if vendorApiLevel < 202404 || vendorApiLevel%100 != 4 { - panic("Unknown vendor API level " + c.VendorApiLevel()) - } // The version before trunk stable is 34. if vendorApiLevel == 202404 { return "34" } + if vendorApiLevel >= 1 && vendorApiLevel <= 34 { + return strconv.Itoa(vendorApiLevel - 1) + } + if vendorApiLevel < 202404 || vendorApiLevel%100 != 4 { + panic("Unknown vendor API level " + c.VendorApiLevel()) + } return strconv.Itoa(vendorApiLevel - 100) } @@ -1932,6 +1946,10 @@ func (c *deviceConfig) GenerateAidlNdkPlatformBackend() bool { return c.config.productVariables.GenerateAidlNdkPlatformBackend } +func (c *deviceConfig) AconfigContainerValidation() string { + return c.config.productVariables.AconfigContainerValidation +} + func (c *config) IgnorePrefer32OnDevice() bool { return c.productVariables.IgnorePrefer32OnDevice } diff --git a/android/deptag.go b/android/deptag.go index a15443b4a..c7ba4d36f 100644 --- a/android/deptag.go +++ b/android/deptag.go @@ -43,3 +43,15 @@ func IsInstallDepNeededTag(tag blueprint.DependencyTag) bool { } return false } + +type PropagateAconfigValidationDependencyTag interface { + PropagateAconfigValidation() bool +} + +type AlwaysPropagateAconfigValidationDependencyTag struct{} + +func (p AlwaysPropagateAconfigValidationDependencyTag) PropagateAconfigValidation() bool { + return true +} + +var _ PropagateAconfigValidationDependencyTag = AlwaysPropagateAconfigValidationDependencyTag{} diff --git a/android/filegroup.go b/android/filegroup.go index bc881ed97..86d7b4bbd 100644 --- a/android/filegroup.go +++ b/android/filegroup.go @@ -15,6 +15,7 @@ package android import ( + "maps" "strings" "github.com/google/blueprint" @@ -97,6 +98,25 @@ func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) { } SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: fg.srcs.Strings()}) CollectDependencyAconfigFiles(ctx, &fg.mergedAconfigFiles) + + var aconfigDeclarations []string + var intermediateCacheOutputPaths Paths + var srcjars Paths + modeInfos := make(map[string]ModeInfo) + ctx.VisitDirectDeps(func(module Module) { + if dep, ok := OtherModuleProvider(ctx, module, CodegenInfoProvider); ok { + aconfigDeclarations = append(aconfigDeclarations, dep.AconfigDeclarations...) + intermediateCacheOutputPaths = append(intermediateCacheOutputPaths, dep.IntermediateCacheOutputPaths...) + srcjars = append(srcjars, dep.Srcjars...) + maps.Copy(modeInfos, dep.ModeInfos) + } + }) + SetProvider(ctx, CodegenInfoProvider, CodegenInfo{ + AconfigDeclarations: aconfigDeclarations, + IntermediateCacheOutputPaths: intermediateCacheOutputPaths, + Srcjars: srcjars, + ModeInfos: modeInfos, + }) } func (fg *fileGroup) Srcs() Paths { diff --git a/android/module.go b/android/module.go index b615ff5e8..000476cba 100644 --- a/android/module.go +++ b/android/module.go @@ -15,7 +15,6 @@ package android import ( - "android/soong/bazel" "crypto/md5" "encoding/hex" "encoding/json" @@ -27,6 +26,8 @@ import ( "sort" "strings" + "android/soong/bazel" + "github.com/google/blueprint" "github.com/google/blueprint/proptools" ) @@ -2087,6 +2088,7 @@ func isUnqualifiedModuleName(module string) bool { // caused by prebuilt_ prefix, or fully qualified module names. type sourceOrOutputDependencyTag struct { blueprint.BaseDependencyTag + AlwaysPropagateAconfigValidationDependencyTag // The name of the module. moduleName string diff --git a/android/ninja_deps.go b/android/ninja_deps.go index 1d50a47ec..bdf465e96 100644 --- a/android/ninja_deps.go +++ b/android/ninja_deps.go @@ -15,7 +15,6 @@ package android import ( - "android/soong/starlark_import" "sort" ) @@ -43,11 +42,4 @@ type ninjaDepsSingleton struct{} func (ninjaDepsSingleton) GenerateBuildActions(ctx SingletonContext) { ctx.AddNinjaFileDeps(ctx.Config().ninjaFileDeps()...) - - deps, err := starlark_import.GetNinjaDeps() - if err != nil { - ctx.Errorf("Error running starlark code: %s", err) - } else { - ctx.AddNinjaFileDeps(deps...) - } } diff --git a/android/path_properties.go b/android/path_properties.go index bbfaa8c46..ea925654e 100644 --- a/android/path_properties.go +++ b/android/path_properties.go @@ -47,7 +47,7 @@ func addPathDepsForProps(ctx BottomUpMutatorContext, props []interface{}) { // tagged with `android:"path"`. var pathProperties []string for _, ps := range props { - pathProperties = append(pathProperties, pathPropertiesForPropertyStruct(ps)...) + pathProperties = append(pathProperties, pathPropertiesForPropertyStruct(ctx, ps)...) } // Remove duplicates to avoid multiple dependencies. @@ -64,7 +64,7 @@ func addPathDepsForProps(ctx BottomUpMutatorContext, props []interface{}) { // pathPropertiesForPropertyStruct uses the indexes of properties that are tagged with // android:"path" to extract all their values from a property struct, returning them as a single // slice of strings. -func pathPropertiesForPropertyStruct(ps interface{}) []string { +func pathPropertiesForPropertyStruct(ctx BottomUpMutatorContext, ps interface{}) []string { v := reflect.ValueOf(ps) if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { panic(fmt.Errorf("type %s is not a pointer to a struct", v.Type())) @@ -106,6 +106,16 @@ func pathPropertiesForPropertyStruct(ps interface{}) []string { ret = append(ret, sv.String()) case reflect.Slice: ret = append(ret, sv.Interface().([]string)...) + case reflect.Struct: + intf := sv.Interface() + if configurable, ok := intf.(proptools.Configurable[string]); ok { + ret = append(ret, proptools.String(configurable.Evaluate(ctx))) + } else if configurable, ok := intf.(proptools.Configurable[[]string]); ok { + ret = append(ret, proptools.Slice(configurable.Evaluate(ctx))...) + } else { + panic(fmt.Errorf(`field %s in type %s has tag android:"path" but is not a string or slice of strings, it is a %s`, + v.Type().FieldByIndex(i).Name, v.Type(), sv.Type())) + } default: panic(fmt.Errorf(`field %s in type %s has tag android:"path" but is not a string or slice of strings, it is a %s`, v.Type().FieldByIndex(i).Name, v.Type(), sv.Type())) diff --git a/android/selects_test.go b/android/selects_test.go new file mode 100644 index 000000000..aa9c521a2 --- /dev/null +++ b/android/selects_test.go @@ -0,0 +1,316 @@ +// 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" + "testing" + + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" +) + +func TestSelects(t *testing.T) { + testCases := []struct { + name string + bp string + provider selectsTestProvider + vendorVars map[string]map[string]string + expectedError string + }{ + { + name: "basic string list", + bp: ` + my_module_type { + name: "foo", + my_string_list: select(soong_config_variable("my_namespace", "my_variable"), { + "a": ["a.cpp"], + "b": ["b.cpp"], + _: ["c.cpp"], + }), + } + `, + provider: selectsTestProvider{ + my_string_list: &[]string{"c.cpp"}, + }, + }, + { + name: "basic string", + bp: ` + my_module_type { + name: "foo", + my_string: select(soong_config_variable("my_namespace", "my_variable"), { + "a": "a.cpp", + "b": "b.cpp", + _: "c.cpp", + }), + } + `, + provider: selectsTestProvider{ + my_string: proptools.StringPtr("c.cpp"), + }, + }, + { + name: "basic bool", + bp: ` + my_module_type { + name: "foo", + my_bool: select(soong_config_variable("my_namespace", "my_variable"), { + "a": true, + "b": false, + _: true, + }), + } + `, + provider: selectsTestProvider{ + my_bool: proptools.BoolPtr(true), + }, + }, + { + name: "basic paths", + bp: ` + my_module_type { + name: "foo", + my_paths: select(soong_config_variable("my_namespace", "my_variable"), { + "a": ["foo.txt"], + "b": ["bar.txt"], + _: ["baz.txt"], + }), + } + `, + provider: selectsTestProvider{ + my_paths: &[]string{"baz.txt"}, + }, + }, + { + name: "paths with module references", + bp: ` + my_module_type { + name: "foo", + my_paths: select(soong_config_variable("my_namespace", "my_variable"), { + "a": [":a"], + "b": [":b"], + _: [":c"], + }), + } + `, + expectedError: `"foo" depends on undefined module "c"`, + }, + { + name: "Differing types", + bp: ` + my_module_type { + name: "foo", + my_string: select(soong_config_variable("my_namespace", "my_variable"), { + "a": "a.cpp", + "b": true, + _: "c.cpp", + }), + } + `, + expectedError: `can't assign bool value to string property "my_string\[1\]"`, + }, + { + name: "String list non-default", + bp: ` + my_module_type { + name: "foo", + my_string_list: select(soong_config_variable("my_namespace", "my_variable"), { + "a": ["a.cpp"], + "b": ["b.cpp"], + _: ["c.cpp"], + }), + } + `, + provider: selectsTestProvider{ + my_string_list: &[]string{"a.cpp"}, + }, + vendorVars: map[string]map[string]string{ + "my_namespace": { + "my_variable": "a", + }, + }, + }, + { + name: "String list append", + bp: ` + my_module_type { + name: "foo", + my_string_list: select(soong_config_variable("my_namespace", "my_variable"), { + "a": ["a.cpp"], + "b": ["b.cpp"], + _: ["c.cpp"], + }) + select(soong_config_variable("my_namespace", "my_variable_2"), { + "a2": ["a2.cpp"], + "b2": ["b2.cpp"], + _: ["c2.cpp"], + }), + } + `, + provider: selectsTestProvider{ + my_string_list: &[]string{"a.cpp", "c2.cpp"}, + }, + vendorVars: map[string]map[string]string{ + "my_namespace": { + "my_variable": "a", + }, + }, + }, + { + name: "String list prepend literal", + bp: ` + my_module_type { + name: "foo", + my_string_list: ["literal.cpp"] + select(soong_config_variable("my_namespace", "my_variable"), { + "a2": ["a2.cpp"], + "b2": ["b2.cpp"], + _: ["c2.cpp"], + }), + } + `, + provider: selectsTestProvider{ + my_string_list: &[]string{"literal.cpp", "c2.cpp"}, + }, + }, + { + name: "String list append literal", + bp: ` + my_module_type { + name: "foo", + my_string_list: select(soong_config_variable("my_namespace", "my_variable"), { + "a2": ["a2.cpp"], + "b2": ["b2.cpp"], + _: ["c2.cpp"], + }) + ["literal.cpp"], + } + `, + provider: selectsTestProvider{ + my_string_list: &[]string{"c2.cpp", "literal.cpp"}, + }, + }, + { + name: "Can't append bools", + bp: ` + my_module_type { + name: "foo", + my_bool: select(soong_config_variable("my_namespace", "my_variable"), { + "a": true, + "b": false, + _: true, + }) + false, + } + `, + expectedError: "my_bool: Cannot append bools", + }, + { + name: "Append string", + bp: ` + my_module_type { + name: "foo", + my_string: select(soong_config_variable("my_namespace", "my_variable"), { + "a": "a", + "b": "b", + _: "c", + }) + ".cpp", + } + `, + provider: selectsTestProvider{ + my_string: proptools.StringPtr("c.cpp"), + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + fixtures := GroupFixturePreparers( + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("my_module_type", newSelectsMockModule) + }), + FixtureModifyProductVariables(func(variables FixtureProductVariables) { + variables.VendorVars = tc.vendorVars + }), + ) + if tc.expectedError != "" { + fixtures = fixtures.ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(tc.expectedError)) + } + result := fixtures.RunTestWithBp(t, tc.bp) + + if tc.expectedError == "" { + m := result.ModuleForTests("foo", "") + p, _ := OtherModuleProvider[selectsTestProvider](result.testContext.OtherModuleProviderAdaptor(), m.Module(), selectsTestProviderKey) + if !reflect.DeepEqual(p, tc.provider) { + t.Errorf("Expected:\n %q\ngot:\n %q", tc.provider.String(), p.String()) + } + } + }) + } +} + +type selectsTestProvider struct { + my_bool *bool + my_string *string + my_string_list *[]string + my_paths *[]string +} + +func (p *selectsTestProvider) String() string { + myBoolStr := "nil" + if p.my_bool != nil { + myBoolStr = fmt.Sprintf("%t", *p.my_bool) + } + myStringStr := "nil" + if p.my_string != nil { + myStringStr = *p.my_string + } + return fmt.Sprintf(`selectsTestProvider { + my_bool: %v, + my_string: %s, + my_string_list: %s, + my_paths: %s, +}`, myBoolStr, myStringStr, p.my_string_list, p.my_paths) +} + +var selectsTestProviderKey = blueprint.NewProvider[selectsTestProvider]() + +type selectsMockModuleProperties struct { + My_bool proptools.Configurable[bool] + My_string proptools.Configurable[string] + My_string_list proptools.Configurable[[]string] + My_paths proptools.Configurable[[]string] `android:"path"` +} + +type selectsMockModule struct { + ModuleBase + DefaultableModuleBase + properties selectsMockModuleProperties +} + +func (p *selectsMockModule) GenerateAndroidBuildActions(ctx ModuleContext) { + SetProvider(ctx, selectsTestProviderKey, selectsTestProvider{ + my_bool: p.properties.My_bool.Evaluate(ctx), + my_string: p.properties.My_string.Evaluate(ctx), + my_string_list: p.properties.My_string_list.Evaluate(ctx), + my_paths: p.properties.My_paths.Evaluate(ctx), + }) +} + +func newSelectsMockModule() Module { + m := &selectsMockModule{} + m.AddProperties(&m.properties) + InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon) + InitDefaultableModule(m) + return m +} diff --git a/android/singleton.go b/android/singleton.go index ccddeafd2..5f939967c 100644 --- a/android/singleton.go +++ b/android/singleton.go @@ -251,7 +251,7 @@ func (s *singletonContextAdaptor) FinalModule(module Module) Module { func (s *singletonContextAdaptor) ModuleVariantsFromName(referer Module, name string) []Module { // get module reference for visibility enforcement - qualified := createVisibilityModuleReference(s.ModuleName(referer), s.ModuleDir(referer), s.ModuleType(referer)) + qualified := createVisibilityModuleReference(s.ModuleName(referer), s.ModuleDir(referer), referer) modules := s.SingletonContext.ModuleVariantsFromName(referer, name) result := make([]Module, 0, len(modules)) diff --git a/android/updatable_modules.go b/android/updatable_modules.go index 6d0eeb716..1548170f9 100644 --- a/android/updatable_modules.go +++ b/android/updatable_modules.go @@ -33,4 +33,4 @@ package android // * AOSP - xx9990000 // * x-mainline-prod - xx9990000 // * master - 990090000 -const DefaultUpdatableModuleVersion = "340090000" +const DefaultUpdatableModuleVersion = "990090000" diff --git a/android/variable.go b/android/variable.go index be3c80d8e..73f5bfd1f 100644 --- a/android/variable.go +++ b/android/variable.go @@ -500,6 +500,8 @@ type ProductVariables struct { HiddenapiExportableStubs *bool `json:",omitempty"` ExportRuntimeApis *bool `json:",omitempty"` + + AconfigContainerValidation string `json:",omitempty"` } type PartitionQualifiedVariablesType struct { diff --git a/android/visibility.go b/android/visibility.go index b3875620f..89c0adc15 100644 --- a/android/visibility.go +++ b/android/visibility.go @@ -58,19 +58,14 @@ const ( var visibilityRuleRegexp = regexp.MustCompile(visibilityRulePattern) type visibilityModuleReference struct { - name qualifiedModuleName - isPartitionModule bool + name qualifiedModuleName + module Module } -func createVisibilityModuleReference(name, dir, typ string) visibilityModuleReference { - isPartitionModule := false - switch typ { - case "android_filesystem", "android_system_image": - isPartitionModule = true - } +func createVisibilityModuleReference(name, dir string, module Module) visibilityModuleReference { return visibilityModuleReference{ - name: createQualifiedModuleName(name, dir), - isPartitionModule: isPartitionModule, + name: createQualifiedModuleName(name, dir), + module: module, } } @@ -214,21 +209,37 @@ func (r privateRule) String() string { return "//visibility:private" } +var anyPartitionRegex = regexp.MustCompile("^any_(system|system_ext|vendor|product|data|odm)_partition$") + // visibilityRule for //visibility:any_partition -type anyPartitionRule struct{} +type anyPartitionRule struct { + partitionType string +} var _ visibilityRule = anyPartitionRule{} +type PartitionTypeInterface interface { + PartitionType() string +} + func (r anyPartitionRule) matches(m visibilityModuleReference) bool { - return m.isPartitionModule + if m2, ok := m.module.(PartitionTypeInterface); ok { + return m2.PartitionType() == r.partitionType + } + return false } func (r anyPartitionRule) String() string { - return "//visibility:any_partition" + return "//visibility:any_" + r.partitionType + "_partition" } var visibilityRuleMap = NewOnceKey("visibilityRuleMap") +type visibilityRulesForModule struct { + rule compositeRule + implicitPartitionRules compositeRule +} + // The map from qualifiedModuleName to visibilityRule. func moduleToVisibilityRuleMap(config Config) *sync.Map { return config.Once(visibilityRuleMap, func() interface{} { @@ -303,7 +314,7 @@ func checkRules(ctx BaseModuleContext, currentPkg, property string, visibility [ if pkg == "visibility" { switch name { - case "private", "public", "any_partition": + case "private", "public": case "legacy_public": ctx.PropertyErrorf(property, "//visibility:legacy_public must not be used") continue @@ -311,6 +322,10 @@ func checkRules(ctx BaseModuleContext, currentPkg, property string, visibility [ // This keyword does not create a rule so pretend it does not exist. ruleCount -= 1 default: + if anyPartitionRegex.MatchString(name) { + // any_*_partition can be used with another visibility fields + continue + } ctx.PropertyErrorf(property, "unrecognized visibility rule %q", v) continue } @@ -349,15 +364,20 @@ func visibilityRuleGatherer(ctx BottomUpMutatorContext) { // Parse the visibility rules that control access to the module and store them by id // for use when enforcing the rules. + var rule compositeRule primaryProperty := m.base().primaryVisibilityProperty if primaryProperty != nil { if visibility := primaryProperty.getStrings(); visibility != nil { - rule := parseRules(ctx, currentPkg, primaryProperty.getName(), visibility) - if rule != nil { - moduleToVisibilityRuleMap(ctx.Config()).Store(qualifiedModuleId, rule) - } + rule = parseRules(ctx, currentPkg, primaryProperty.getName(), visibility) } } + ipr := implicitPartitionRules(ctx) + if rule != nil || ipr != nil { + moduleToVisibilityRuleMap(ctx.Config()).Store(qualifiedModuleId, visibilityRulesForModule{ + rule: rule, + implicitPartitionRules: ipr, + }) + } } func parseRules(ctx BaseModuleContext, currentPkg, property string, visibility []string) compositeRule { @@ -389,8 +409,13 @@ func parseRules(ctx BaseModuleContext, currentPkg, property string, visibility [ hasNonPrivateRule = false // This does not actually create a rule so continue onto the next rule. continue - case "any_partition": - r = anyPartitionRule{} + default: + match := anyPartitionRegex.FindStringSubmatch(name) + if match != nil { + r = anyPartitionRule{ + partitionType: match[1], + } + } } } else { switch name { @@ -429,6 +454,22 @@ func parseRules(ctx BaseModuleContext, currentPkg, property string, visibility [ return rules } +func implicitPartitionRules(ctx BaseModuleContext) compositeRule { + var result compositeRule + if ctx.SocSpecific() { + result = append(result, anyPartitionRule{partitionType: "vendor"}) + } else if ctx.ProductSpecific() { + result = append(result, anyPartitionRule{partitionType: "product"}) + } else if ctx.Module().InstallInData() { + result = append(result, anyPartitionRule{partitionType: "data"}) + } else if ctx.SystemExtSpecific() { + result = append(result, anyPartitionRule{partitionType: "system_ext"}) + } else if ctx.DeviceSpecific() { + result = append(result, anyPartitionRule{partitionType: "odm"}) + } + return result +} + func isAllowedFromOutsideVendor(pkg string, name string) bool { if pkg == "vendor" { return name == "__subpackages__" @@ -467,7 +508,7 @@ func splitRule(ctx BaseModuleContext, ruleExpression string, currentPkg, propert } func visibilityRuleEnforcer(ctx TopDownMutatorContext) { - qualified := createVisibilityModuleReference(ctx.ModuleName(), ctx.ModuleDir(), ctx.ModuleType()) + qualified := createVisibilityModuleReference(ctx.ModuleName(), ctx.ModuleDir(), ctx.Module()) // Visit all the dependencies making sure that this module has access to them all. ctx.VisitDirectDeps(func(dep Module) { @@ -502,10 +543,13 @@ var defaultVisibility = compositeRule{publicRule{}} // which is currently //visibility:public. func effectiveVisibilityRules(config Config, qualified qualifiedModuleName) compositeRule { moduleToVisibilityRule := moduleToVisibilityRuleMap(config) - value, ok := moduleToVisibilityRule.Load(qualified) + value := visibilityRulesForModule{} + if valueRaw, ok := moduleToVisibilityRule.Load(qualified); ok { + value = valueRaw.(visibilityRulesForModule) + } var rule compositeRule - if ok { - rule = value.(compositeRule) + if value.rule != nil { + rule = value.rule } else { rule = packageDefaultVisibility(moduleToVisibilityRule, qualified) } @@ -515,6 +559,20 @@ func effectiveVisibilityRules(config Config, qualified qualifiedModuleName) comp if rule == nil { rule = defaultVisibility } + + // If a partition rule wasn't specified, add implicit partition visibility + // rules based on the partition properties like vendor: true. + foundPartitionRule := false + for _, r := range rule { + if _, ok := r.(anyPartitionRule); ok { + foundPartitionRule = true + break + } + } + if !foundPartitionRule { + rule = append(rule, value.implicitPartitionRules...) + } + return rule } @@ -528,7 +586,7 @@ func packageDefaultVisibility(moduleToVisibilityRule *sync.Map, moduleId qualifi for { value, ok := moduleToVisibilityRule.Load(packageQualifiedId) if ok { - return value.(compositeRule) + return value.(visibilityRulesForModule).rule } if packageQualifiedId.isRootPackage() { @@ -602,7 +660,7 @@ func EffectiveVisibilityRules(ctx BaseModuleContext, module Module) VisibilityRu rule := effectiveVisibilityRules(ctx.Config(), qualified) - currentModule := createVisibilityModuleReference(moduleName, dir, ctx.OtherModuleType(module)) + currentModule := createVisibilityModuleReference(moduleName, dir, module) // Modules are implicitly visible to other modules in the same package, // without checking the visibility rules. Here we need to add that visibility diff --git a/android/visibility_test.go b/android/visibility_test.go index d4add7d32..1a2eecafb 100644 --- a/android/visibility_test.go +++ b/android/visibility_test.go @@ -1905,7 +1905,7 @@ var visibilityTests = []struct { }, }, { - name: "any_partition visibility works", + name: "any_system_partition visibility works", fs: MockFS{ "top/Android.bp": []byte(` android_filesystem { @@ -1916,26 +1916,112 @@ var visibilityTests = []struct { package(default_visibility=["//visibility:private"]) mock_library { name: "bar", - visibility: ["//visibility:any_partition"], + visibility: ["//visibility:any_system_partition"], }`), }, }, { - name: "any_partition visibility doesn't work for non-partitions", + name: "any_system_partition visibility works with the other visibility", fs: MockFS{ "top/Android.bp": []byte(` + android_filesystem { + name: "foo", + deps: ["bar"], + }`), + "top2/Android.bp": []byte(``), + "top/nested/Android.bp": []byte(` + package(default_visibility=["//visibility:private"]) + mock_library { + name: "bar", + visibility: [ + "//top2", + "//visibility:any_system_partition" + ], + }`), + }, + }, + { + name: "any_system_partition visibility doesn't work for non-partitions", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_library { + name: "foo", + deps: ["bar"], + }`), + "top/nested/Android.bp": []byte(` + mock_library { + name: "bar", + visibility: ["//visibility:any_system_partition"], + }`), + }, + expectedErrors: []string{`module "foo" variant "android_common": depends on //top/nested:bar which is not visible to this module`}, + }, + { + name: "any_system_partition visibility doesn't work for vendor partitions", + fs: MockFS{ + "top/Android.bp": []byte(` + android_filesystem { + name: "foo", + partition_type: "vendor", + deps: ["bar"], + }`), + "top/nested/Android.bp": []byte(` + package(default_visibility=["//visibility:private"]) + mock_library { + name: "bar", + visibility: ["//visibility:any_system_partition"], + }`), + }, + expectedErrors: []string{`module "foo" variant "android_common": depends on //top/nested:bar which is not visible to this module`}, + }, + { + name: "Vendor modules are visible to any vendor partition by default", + fs: MockFS{ + "top/Android.bp": []byte(` + android_filesystem { + name: "foo", + partition_type: "vendor", + deps: ["bar"], + }`), + "top/nested/Android.bp": []byte(` + package(default_visibility=["//visibility:private"]) mock_library { + name: "bar", + vendor: true, + }`), + }, + }, + { + name: "Not visible to vendor partitions when using any_system_partiton, even if vendor: true", + fs: MockFS{ + "top/Android.bp": []byte(` + android_filesystem { name: "foo", + partition_type: "vendor", deps: ["bar"], }`), "top/nested/Android.bp": []byte(` + package(default_visibility=["//visibility:private"]) mock_library { name: "bar", - visibility: ["//visibility:any_partition"], + vendor: true, + visibility: ["//visibility:any_system_partition"], }`), }, expectedErrors: []string{`module "foo" variant "android_common": depends on //top/nested:bar which is not visible to this module`}, }, + { + name: "unknown any_partition specs throw errors", + fs: MockFS{ + "top/nested/Android.bp": []byte(` + package(default_visibility=["//visibility:private"]) + mock_library { + name: "bar", + visibility: ["//visibility:any_unknown_partition"], + }`), + }, + expectedErrors: []string{`unrecognized visibility rule "//visibility:any_unknown_partition"`}, + }, } func TestVisibility(t *testing.T) { @@ -1957,8 +2043,7 @@ func TestVisibility(t *testing.T) { ctx.RegisterModuleType("mock_library", newMockLibraryModule) ctx.RegisterModuleType("mock_parent", newMockParentFactory) ctx.RegisterModuleType("mock_defaults", defaultsFactory) - // For testing //visibility:any_partition. The module type doesn't matter, just that it's registered under the name "android_filesystem" - ctx.RegisterModuleType("android_filesystem", newMockLibraryModule) + ctx.RegisterModuleType("android_filesystem", newMockFilesystemModule) }), prepareForTestWithFakePrebuiltModules, // Add additional files to the mock filesystem @@ -2012,6 +2097,37 @@ func (j *mockLibraryModule) DepsMutator(ctx BottomUpMutatorContext) { func (p *mockLibraryModule) GenerateAndroidBuildActions(ModuleContext) { } +type mockFilesystemModuleProperties struct { + Partition_type *string + Deps []string +} + +type mockFilesystemModule struct { + ModuleBase + properties mockFilesystemModuleProperties +} + +func (j *mockFilesystemModule) DepsMutator(ctx BottomUpMutatorContext) { + ctx.AddVariationDependencies(nil, dependencyTag{name: "mockdeps"}, j.properties.Deps...) +} + +func (p *mockFilesystemModule) GenerateAndroidBuildActions(ModuleContext) { +} + +func (p *mockFilesystemModule) PartitionType() string { + if p.properties.Partition_type == nil { + return "system" + } + return *p.properties.Partition_type +} + +func newMockFilesystemModule() Module { + m := &mockFilesystemModule{} + m.AddProperties(&m.properties) + InitAndroidArchModule(m, DeviceSupported, MultilibCommon) + return m +} + type mockDefaults struct { ModuleBase DefaultsModuleBase diff --git a/androidmk/parser/parser.go b/androidmk/parser/parser.go index fb6be38e9..8a20bb052 100644 --- a/androidmk/parser/parser.go +++ b/androidmk/parser/parser.go @@ -413,6 +413,9 @@ loop: p.accept('\t') newLine = false continue loop + } else if p.tok == '\n' { + p.accept('\n') + continue loop } else if p.parseDirective() { newLine = false continue diff --git a/androidmk/parser/parser_test.go b/androidmk/parser/parser_test.go index 9efebf8e1..db3313d27 100644 --- a/androidmk/parser/parser_test.go +++ b/androidmk/parser/parser_test.go @@ -84,6 +84,22 @@ endif`, }, }, }, + { + name: "Blank line in rule's command", + in: `all: + echo first line + + echo second line`, + out: []Node{ + &Rule{ + Target: SimpleMakeString("all", NoPos), + RecipePos: NoPos, + Recipe: "echo first line\necho second line", + Prerequisites: SimpleMakeString("", NoPos), + }, + }, + }, + } func TestParse(t *testing.T) { diff --git a/apex/aconfig_test.go b/apex/aconfig_test.go new file mode 100644 index 000000000..a179dbffd --- /dev/null +++ b/apex/aconfig_test.go @@ -0,0 +1,701 @@ +// 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 apex + +import ( + "testing" + + "android/soong/aconfig/codegen" + "android/soong/android" + "android/soong/cc" + "android/soong/genrule" + "android/soong/java" + "android/soong/rust" + "github.com/google/blueprint/proptools" +) + +var withAconfigValidationError = android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.AconfigContainerValidation = "error" + variables.BuildId = proptools.StringPtr("TEST.BUILD_ID") +}) + +func TestValidationAcrossContainersExportedPass(t *testing.T) { + testCases := []struct { + name string + bp string + }{ + { + name: "Java lib passes for exported containers cross", + bp: apex_default_bp + ` + apex { + name: "myapex", + manifest: ":myapex.manifest", + androidManifest: ":myapex.androidmanifest", + key: "myapex.key", + java_libs: [ + "my_java_library_foo", + ], + updatable: false, + } + java_library { + name: "my_java_library_foo", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + system_modules: "none", + static_libs: ["my_java_aconfig_library_foo"], + apex_available: [ + "myapex", + ], + } + aconfig_declarations { + name: "my_aconfig_declarations_foo", + package: "com.example.package", + container: "otherapex", + srcs: ["foo.aconfig"], + exportable: true, + } + java_aconfig_library { + name: "my_java_aconfig_library_foo", + aconfig_declarations: "my_aconfig_declarations_foo", + mode: "exported", + apex_available: [ + "myapex", + ], + }`, + }, + { + name: "Android app passes for exported containers cross", + bp: apex_default_bp + ` + apex { + name: "myapex", + manifest: ":myapex.manifest", + androidManifest: ":myapex.androidmanifest", + key: "myapex.key", + apps: [ + "my_android_app_foo", + ], + updatable: false, + } + android_app { + name: "my_android_app_foo", + srcs: ["foo/MyClass.java"], + sdk_version: "none", + system_modules: "none", + stl: "none", + static_libs: ["my_java_library_bar"], + apex_available: [ "myapex" ], + } + java_library { + name: "my_java_library_bar", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + system_modules: "none", + static_libs: ["my_java_aconfig_library_bar"], + apex_available: [ + "myapex", + ], + } + aconfig_declarations { + name: "my_aconfig_declarations_bar", + package: "com.example.package", + container: "otherapex", + srcs: ["bar.aconfig"], + exportable: true, + } + java_aconfig_library { + name: "my_java_aconfig_library_bar", + aconfig_declarations: "my_aconfig_declarations_bar", + mode: "exported", + apex_available: [ + "myapex", + ], + }`, + }, + { + name: "Cc lib passes for exported containers cross", + bp: apex_default_bp + ` + apex { + name: "myapex", + manifest: ":myapex.manifest", + androidManifest: ":myapex.androidmanifest", + key: "myapex.key", + native_shared_libs: [ + "my_cc_library_bar", + ], + binaries: [ + "my_cc_binary_baz", + ], + updatable: false, + } + cc_library { + name: "my_cc_library_bar", + srcs: ["foo/bar/MyClass.cc"], + static_libs: [ + "my_cc_aconfig_library_bar", + "my_cc_aconfig_library_baz", + ], + apex_available: [ + "myapex", + ], + } + cc_binary { + name: "my_cc_binary_baz", + srcs: ["foo/bar/MyClass.cc"], + static_libs: ["my_cc_aconfig_library_baz"], + apex_available: [ + "myapex", + ], + } + cc_library { + name: "server_configurable_flags", + srcs: ["server_configurable_flags.cc"], + } + aconfig_declarations { + name: "my_aconfig_declarations_bar", + package: "com.example.package", + container: "otherapex", + srcs: ["bar.aconfig"], + exportable: true, + } + cc_aconfig_library { + name: "my_cc_aconfig_library_bar", + aconfig_declarations: "my_aconfig_declarations_bar", + apex_available: [ + "myapex", + ], + mode: "exported", + } + aconfig_declarations { + name: "my_aconfig_declarations_baz", + package: "com.example.package", + container: "otherapex", + srcs: ["baz.aconfig"], + exportable: true, + } + cc_aconfig_library { + name: "my_cc_aconfig_library_baz", + aconfig_declarations: "my_aconfig_declarations_baz", + apex_available: [ + "myapex", + ], + mode: "exported", + }`, + }, + { + name: "Rust lib passes for exported containers cross", + bp: apex_default_bp + ` + apex { + name: "myapex", + manifest: ":myapex.manifest", + androidManifest: ":myapex.androidmanifest", + key: "myapex.key", + native_shared_libs: ["libmy_rust_library"], + binaries: ["my_rust_binary"], + updatable: false, + } + rust_library { + name: "libflags_rust", // test mock + crate_name: "flags_rust", + srcs: ["lib.rs"], + apex_available: ["myapex"], + } + rust_library { + name: "liblazy_static", // test mock + crate_name: "lazy_static", + srcs: ["src/lib.rs"], + apex_available: ["myapex"], + } + rust_ffi_shared { + name: "libmy_rust_library", + srcs: ["src/lib.rs"], + rustlibs: ["libmy_rust_aconfig_library_foo"], + crate_name: "my_rust_library", + apex_available: ["myapex"], + } + rust_binary { + name: "my_rust_binary", + srcs: ["foo/bar/MyClass.rs"], + rustlibs: ["libmy_rust_aconfig_library_bar"], + apex_available: ["myapex"], + } + aconfig_declarations { + name: "my_aconfig_declarations_foo", + package: "com.example.package", + container: "otherapex", + srcs: ["foo.aconfig"], + } + aconfig_declarations { + name: "my_aconfig_declarations_bar", + package: "com.example.package", + container: "otherapex", + srcs: ["bar.aconfig"], + } + rust_aconfig_library { + name: "libmy_rust_aconfig_library_foo", + aconfig_declarations: "my_aconfig_declarations_foo", + crate_name: "my_rust_aconfig_library_foo", + apex_available: ["myapex"], + mode: "exported", + } + rust_aconfig_library { + name: "libmy_rust_aconfig_library_bar", + aconfig_declarations: "my_aconfig_declarations_bar", + crate_name: "my_rust_aconfig_library_bar", + apex_available: ["myapex"], + mode: "exported", + }`, + }, + } + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + android.GroupFixturePreparers( + java.PrepareForTestWithJavaDefaultModules, + cc.PrepareForTestWithCcBuildComponents, + rust.PrepareForTestWithRustDefaultModules, + codegen.PrepareForTestWithAconfigBuildComponents, + PrepareForTestWithApexBuildComponents, + prepareForTestWithMyapex, + withAconfigValidationError, + ). + RunTestWithBp(t, test.bp) + }) + } +} + +func TestValidationAcrossContainersNotExportedFail(t *testing.T) { + testCases := []struct { + name string + expectedError string + bp string + }{ + { + name: "Java lib fails for non-exported containers cross", + bp: apex_default_bp + ` + apex { + name: "myapex", + manifest: ":myapex.manifest", + androidManifest: ":myapex.androidmanifest", + key: "myapex.key", + java_libs: [ + "my_java_library_foo", + ], + updatable: false, + } + java_library { + name: "my_java_library_foo", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + system_modules: "none", + static_libs: ["my_java_aconfig_library_foo"], + apex_available: [ + "myapex", + ], + } + aconfig_declarations { + name: "my_aconfig_declarations_foo", + package: "com.example.package", + container: "otherapex", + srcs: ["foo.aconfig"], + } + java_aconfig_library { + name: "my_java_aconfig_library_foo", + aconfig_declarations: "my_aconfig_declarations_foo", + apex_available: [ + "myapex", + ], + }`, + expectedError: `.*my_java_library_foo/myapex depends on my_java_aconfig_library_foo/otherapex/production across containers`, + }, + { + name: "Android app fails for non-exported containers cross", + bp: apex_default_bp + ` + apex { + name: "myapex", + manifest: ":myapex.manifest", + androidManifest: ":myapex.androidmanifest", + key: "myapex.key", + apps: [ + "my_android_app_foo", + ], + updatable: false, + } + android_app { + name: "my_android_app_foo", + srcs: ["foo/MyClass.java"], + sdk_version: "none", + system_modules: "none", + stl: "none", + static_libs: ["my_java_library_foo"], + apex_available: [ "myapex" ], + } + java_library { + name: "my_java_library_foo", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + system_modules: "none", + static_libs: ["my_java_aconfig_library_foo"], + apex_available: [ + "myapex", + ], + } + aconfig_declarations { + name: "my_aconfig_declarations_foo", + package: "com.example.package", + container: "otherapex", + srcs: ["bar.aconfig"], + } + java_aconfig_library { + name: "my_java_aconfig_library_foo", + aconfig_declarations: "my_aconfig_declarations_foo", + apex_available: [ + "myapex", + ], + }`, + expectedError: `.*my_android_app_foo/myapex depends on my_java_aconfig_library_foo/otherapex/production across containers`, + }, + { + name: "Cc lib fails for non-exported containers cross", + bp: apex_default_bp + ` + apex { + name: "myapex", + manifest: ":myapex.manifest", + androidManifest: ":myapex.androidmanifest", + key: "myapex.key", + native_shared_libs: [ + "my_cc_library_foo", + ], + updatable: false, + } + cc_library { + name: "my_cc_library_foo", + srcs: ["foo/bar/MyClass.cc"], + shared_libs: [ + "my_cc_aconfig_library_foo", + ], + apex_available: [ + "myapex", + ], + } + cc_library { + name: "server_configurable_flags", + srcs: ["server_configurable_flags.cc"], + } + aconfig_declarations { + name: "my_aconfig_declarations_foo", + package: "com.example.package", + container: "otherapex", + srcs: ["foo.aconfig"], + } + cc_aconfig_library { + name: "my_cc_aconfig_library_foo", + aconfig_declarations: "my_aconfig_declarations_foo", + apex_available: [ + "myapex", + ], + }`, + expectedError: `.*my_cc_library_foo/myapex depends on my_cc_aconfig_library_foo/otherapex/production across containers`, + }, + { + name: "Cc binary fails for non-exported containers cross", + bp: apex_default_bp + ` + apex { + name: "myapex", + manifest: ":myapex.manifest", + androidManifest: ":myapex.androidmanifest", + key: "myapex.key", + binaries: [ + "my_cc_binary_foo", + ], + updatable: false, + } + cc_library { + name: "my_cc_library_foo", + srcs: ["foo/bar/MyClass.cc"], + static_libs: [ + "my_cc_aconfig_library_foo", + ], + apex_available: [ + "myapex", + ], + } + cc_binary { + name: "my_cc_binary_foo", + srcs: ["foo/bar/MyClass.cc"], + static_libs: ["my_cc_library_foo"], + apex_available: [ + "myapex", + ], + } + cc_library { + name: "server_configurable_flags", + srcs: ["server_configurable_flags.cc"], + } + aconfig_declarations { + name: "my_aconfig_declarations_foo", + package: "com.example.package", + container: "otherapex", + srcs: ["foo.aconfig"], + } + cc_aconfig_library { + name: "my_cc_aconfig_library_foo", + aconfig_declarations: "my_aconfig_declarations_foo", + apex_available: [ + "myapex", + ], + }`, + expectedError: `.*my_cc_binary_foo/myapex depends on my_cc_aconfig_library_foo/otherapex/production across containers`, + }, + { + name: "Rust lib fails for non-exported containers cross", + bp: apex_default_bp + ` + apex { + name: "myapex", + manifest: ":myapex.manifest", + androidManifest: ":myapex.androidmanifest", + key: "myapex.key", + native_shared_libs: ["libmy_rust_library"], + updatable: false, + } + rust_library { + name: "libflags_rust", // test mock + crate_name: "flags_rust", + srcs: ["lib.rs"], + apex_available: ["myapex"], + } + rust_library { + name: "liblazy_static", // test mock + crate_name: "lazy_static", + srcs: ["src/lib.rs"], + apex_available: ["myapex"], + } + rust_ffi_shared { + name: "libmy_rust_library", + srcs: ["src/lib.rs"], + rustlibs: ["libmy_rust_aconfig_library_foo"], + crate_name: "my_rust_library", + apex_available: ["myapex"], + } + aconfig_declarations { + name: "my_aconfig_declarations_foo", + package: "com.example.package", + container: "otherapex", + srcs: ["foo.aconfig"], + } + rust_aconfig_library { + name: "libmy_rust_aconfig_library_foo", + aconfig_declarations: "my_aconfig_declarations_foo", + crate_name: "my_rust_aconfig_library_foo", + apex_available: ["myapex"], + }`, + expectedError: `.*libmy_rust_aconfig_library_foo/myapex depends on libmy_rust_aconfig_library_foo/otherapex/production across containers`, + }, + { + name: "Rust binary fails for non-exported containers cross", + bp: apex_default_bp + ` + apex { + name: "myapex", + manifest: ":myapex.manifest", + androidManifest: ":myapex.androidmanifest", + key: "myapex.key", + binaries: ["my_rust_binary"], + updatable: false, + } + rust_library { + name: "libflags_rust", // test mock + crate_name: "flags_rust", + srcs: ["lib.rs"], + apex_available: ["myapex"], + } + rust_library { + name: "liblazy_static", // test mock + crate_name: "lazy_static", + srcs: ["src/lib.rs"], + apex_available: ["myapex"], + } + rust_binary { + name: "my_rust_binary", + srcs: ["foo/bar/MyClass.rs"], + rustlibs: ["libmy_rust_aconfig_library_bar"], + apex_available: ["myapex"], + } + aconfig_declarations { + name: "my_aconfig_declarations_bar", + package: "com.example.package", + container: "otherapex", + srcs: ["bar.aconfig"], + } + rust_aconfig_library { + name: "libmy_rust_aconfig_library_bar", + aconfig_declarations: "my_aconfig_declarations_bar", + crate_name: "my_rust_aconfig_library_bar", + apex_available: ["myapex"], + }`, + expectedError: `.*libmy_rust_aconfig_library_bar/myapex depends on libmy_rust_aconfig_library_bar/otherapex/production across containers`, + }, + { + name: "Aconfig validation propagate along sourceOrOutputDependencyTag", + bp: apex_default_bp + ` + apex { + name: "myapex", + manifest: ":myapex.manifest", + androidManifest: ":myapex.androidmanifest", + key: "myapex.key", + apps: [ + "my_android_app_foo", + ], + updatable: false, + } + android_app { + name: "my_android_app_foo", + srcs: ["foo/MyClass.java"], + sdk_version: "none", + system_modules: "none", + stl: "none", + static_libs: ["my_java_library_foo"], + apex_available: [ "myapex" ], + } + java_library { + name: "my_java_library_foo", + srcs: [":my_genrule_foo"], + sdk_version: "none", + system_modules: "none", + apex_available: [ + "myapex", + ], + } + aconfig_declarations_group { + name: "my_aconfig_declarations_group_foo", + java_aconfig_libraries: [ + "my_java_aconfig_library_foo", + ], + } + filegroup { + name: "my_filegroup_foo_srcjars", + srcs: [ + ":my_aconfig_declarations_group_foo{.srcjars}", + ], + } + genrule { + name: "my_genrule_foo", + srcs: [":my_filegroup_foo_srcjars"], + cmd: "cp $(in) $(out)", + out: ["my_genrule_foo.srcjar"], + } + aconfig_declarations { + name: "my_aconfig_declarations_foo", + package: "com.example.package", + container: "otherapex", + srcs: ["bar.aconfig"], + } + java_aconfig_library { + name: "my_java_aconfig_library_foo", + aconfig_declarations: "my_aconfig_declarations_foo", + apex_available: [ + "myapex", + ], + }`, + expectedError: `.*my_android_app_foo/myapex depends on my_java_aconfig_library_foo/otherapex/production across containers`, + }, + } + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + errorHandler := android.FixtureExpectsNoErrors + if test.expectedError != "" { + errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(test.expectedError) + } + android.GroupFixturePreparers( + java.PrepareForTestWithJavaDefaultModules, + cc.PrepareForTestWithCcBuildComponents, + rust.PrepareForTestWithRustDefaultModules, + codegen.PrepareForTestWithAconfigBuildComponents, + genrule.PrepareForIntegrationTestWithGenrule, + PrepareForTestWithApexBuildComponents, + prepareForTestWithMyapex, + withAconfigValidationError, + ). + ExtendWithErrorHandler(errorHandler). + RunTestWithBp(t, test.bp) + }) + } +} + +func TestValidationNotPropagateAcrossShared(t *testing.T) { + testCases := []struct { + name string + bp string + }{ + { + name: "Java shared lib not propagate aconfig validation", + bp: apex_default_bp + ` + apex { + name: "myapex", + manifest: ":myapex.manifest", + androidManifest: ":myapex.androidmanifest", + key: "myapex.key", + java_libs: [ + "my_java_library_bar", + ], + updatable: false, + } + java_library { + name: "my_java_library_bar", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + system_modules: "none", + libs: ["my_java_library_foo"], + apex_available: [ + "myapex", + ], + } + java_library { + name: "my_java_library_foo", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "none", + system_modules: "none", + static_libs: ["my_java_aconfig_library_foo"], + apex_available: [ + "myapex", + ], + } + aconfig_declarations { + name: "my_aconfig_declarations_foo", + package: "com.example.package", + container: "otherapex", + srcs: ["foo.aconfig"], + } + java_aconfig_library { + name: "my_java_aconfig_library_foo", + aconfig_declarations: "my_aconfig_declarations_foo", + apex_available: [ + "myapex", + ], + }`, + }, + } + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + android.GroupFixturePreparers( + java.PrepareForTestWithJavaDefaultModules, + cc.PrepareForTestWithCcBuildComponents, + rust.PrepareForTestWithRustDefaultModules, + codegen.PrepareForTestWithAconfigBuildComponents, + PrepareForTestWithApexBuildComponents, + prepareForTestWithMyapex, + withAconfigValidationError, + ). + RunTestWithBp(t, test.bp) + }) + } +} diff --git a/apex/apex.go b/apex/apex.go index 9d7af189b..ed2ca984e 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -1648,10 +1648,9 @@ func apexFileForShBinary(ctx android.BaseModuleContext, sh *sh.ShBinary) apexFil return af } -func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, prebuilt prebuilt_etc.PrebuiltEtcModule, depName string) apexFile { +func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, prebuilt prebuilt_etc.PrebuiltEtcModule, outputFile android.Path) apexFile { dirInApex := filepath.Join(prebuilt.BaseDir(), prebuilt.SubDir()) - fileToCopy := prebuilt.OutputFile() - return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, prebuilt) + return newApexFile(ctx, outputFile, outputFile.Base(), dirInApex, etc, prebuilt) } func apexFileForCompatConfig(ctx android.BaseModuleContext, config java.PlatformCompatConfigIntf, depName string) apexFile { @@ -2120,7 +2119,10 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, } case prebuiltTag: if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok { - vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName)) + filesToCopy, _ := prebuilt.OutputFiles("") + 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) @@ -2263,7 +2265,10 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, // Because APK-in-APEX embeds jni_libs transitively, we don't need to track transitive deps } else if java.IsXmlPermissionsFileDepTag(depTag) { if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok { - vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName)) + filesToCopy, _ := prebuilt.OutputFiles("") + for _, etcFile := range filesToCopy { + vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, etcFile)) + } } } else if rust.IsDylibDepTag(depTag) { if rustm, ok := child.(*rust.Module); ok && rustm.IsInstallableToApex() { @@ -2321,9 +2326,15 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, } func addAconfigFiles(vctx *visitorContext, ctx android.ModuleContext, module blueprint.Module) { - dep, _ := android.OtherModuleProvider(ctx, module, android.AconfigTransitiveDeclarationsInfoProvider) - if len(dep.AconfigFiles) > 0 && dep.AconfigFiles[ctx.ModuleName()] != nil { - vctx.aconfigFiles = append(vctx.aconfigFiles, dep.AconfigFiles[ctx.ModuleName()]...) + if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigTransitiveDeclarationsInfoProvider); 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") } } @@ -2424,6 +2435,18 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { // Set a provider for dexpreopt of bootjars a.provideApexExportsInfo(ctx) + + a.providePrebuiltInfo(ctx) +} + +// Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file +// with information about whether source or prebuilt of an apex was used during the build. +func (a *apexBundle) providePrebuiltInfo(ctx android.ModuleContext) { + info := android.PrebuiltInfo{ + Name: a.Name(), + Is_prebuilt: false, + } + android.SetProvider(ctx, android.PrebuiltInfoProvider, info) } // Set a provider containing information about the jars and .prof provided by the apex diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go index 25c0cc444..e6ebff2c1 100644 --- a/apex/apex_singleton.go +++ b/apex/apex_singleton.go @@ -17,6 +17,8 @@ package apex import ( + "encoding/json" + "github.com/google/blueprint" "android/soong/android" @@ -129,3 +131,43 @@ func (s *apexDepsInfoSingleton) MakeVars(ctx android.MakeVarsContext) { // Export check result to Make. The path is added to droidcore. ctx.Strict("APEX_ALLOWED_DEPS_CHECK", s.allowedApexDepsInfoCheckResult.String()) } + +func init() { + registerApexPrebuiltInfoComponents(android.InitRegistrationContext) +} + +func registerApexPrebuiltInfoComponents(ctx android.RegistrationContext) { + ctx.RegisterParallelSingletonType("apex_prebuiltinfo_singleton", apexPrebuiltInfoFactory) +} + +func apexPrebuiltInfoFactory() android.Singleton { + return &apexPrebuiltInfo{} +} + +type apexPrebuiltInfo struct { + out android.WritablePath +} + +func (a *apexPrebuiltInfo) GenerateBuildActions(ctx android.SingletonContext) { + prebuiltInfos := []android.PrebuiltInfo{} + + ctx.VisitAllModules(func(m android.Module) { + prebuiltInfo, exists := android.SingletonModuleProvider(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() { + prebuiltInfos = append(prebuiltInfos, prebuiltInfo) + } + }) + + j, err := json.Marshal(prebuiltInfos) + if err != nil { + ctx.Errorf("Could not convert prebuilt info of apexes to json due to error: %v", err) + } + a.out = android.PathForOutput(ctx, "prebuilt_info.json") + android.WriteFileRule(ctx, a.out, string(j)) +} + +func (a *apexPrebuiltInfo) MakeVars(ctx android.MakeVarsContext) { + ctx.DistForGoal("droidcore", a.out) +} diff --git a/apex/apex_test.go b/apex/apex_test.go index 54d2d08ff..add6083c9 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -2068,7 +2068,7 @@ func TestApexMinSdkVersion_InVendorApex(t *testing.T) { // Ensure that mylib links with "current" LLNDK libFlags := names(mylib.Rule("ld").Args["libFlags"]) - ensureListContains(t, libFlags, "out/soong/.intermediates/libbar/"+vendorVariant+"_shared_current/libbar.so") + ensureListContains(t, libFlags, "out/soong/.intermediates/libbar/"+vendorVariant+"_shared/libbar.so") // Ensure that mylib is targeting 29 ccRule := ctx.ModuleForTests("mylib", vendorVariant+"_static_apex29").Output("obj/mylib.o") @@ -11022,7 +11022,7 @@ func TestFileSystemShouldSkipApexLibraries(t *testing.T) { } `) - inputs := result.ModuleForTests("myfilesystem", "android_common").Output("deps.zip").Implicits + inputs := result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img").Implicits android.AssertStringListDoesNotContain(t, "filesystem should not have libbar", inputs.Strings(), "out/soong/.intermediates/libbar/android_arm64_armv8-a_shared/libbar.so") diff --git a/apex/prebuilt.go b/apex/prebuilt.go index cebbae9a6..ea847e15c 100644 --- a/apex/prebuilt.go +++ b/apex/prebuilt.go @@ -121,6 +121,11 @@ type PrebuiltCommonProperties struct { // List of systemserverclasspath fragments inside this prebuilt APEX bundle and for which this // APEX bundle will create an APEX variant. Exported_systemserverclasspath_fragments []string + + // Path to the .prebuilt_info file of the prebuilt apex. + // In case of mainline modules, the .prebuilt_info file contains the build_id that was used to + // generate the prebuilt. + Prebuilt_info *string `android:"path"` } // initPrebuiltCommon initializes the prebuiltCommon structure and performs initialization of the @@ -819,6 +824,20 @@ func (p *prebuiltCommon) provideApexExportsInfo(ctx android.ModuleContext) { } } +// Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file +// with information about whether source or prebuilt of an apex was used during the build. +func (p *prebuiltCommon) providePrebuiltInfo(ctx android.ModuleContext) { + info := android.PrebuiltInfo{ + Name: p.BaseModuleName(), + Is_prebuilt: true, + } + // If Prebuilt_info information is available in the soong module definition, add it to prebuilt_info.json. + if p.prebuiltCommonProperties.Prebuilt_info != nil { + info.Prebuilt_info_file_path = android.PathForModuleSrc(ctx, *p.prebuiltCommonProperties.Prebuilt_info).String() + } + android.SetProvider(ctx, android.PrebuiltInfoProvider, info) +} + func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) { p.apexKeysPath = writeApexKeys(ctx, p) // TODO(jungjw): Check the key validity. @@ -846,6 +865,8 @@ func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) { // provide info used for generating the boot image p.provideApexExportsInfo(ctx) + p.providePrebuiltInfo(ctx) + // Save the files that need to be made available to Make. p.initApexFilesForAndroidMk(ctx) @@ -1068,6 +1089,8 @@ func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) { // provide info used for generating the boot image a.provideApexExportsInfo(ctx) + a.providePrebuiltInfo(ctx) + // Save the files that need to be made available to Make. a.initApexFilesForAndroidMk(ctx) @@ -766,6 +766,12 @@ func (d libraryDependencyTag) InstallDepNeeded() bool { var _ android.InstallNeededDependencyTag = libraryDependencyTag{} +func (d libraryDependencyTag) PropagateAconfigValidation() bool { + return d.static() +} + +var _ android.PropagateAconfigValidationDependencyTag = libraryDependencyTag{} + // dependencyTag is used for tagging miscellaneous dependency types that don't fit into // libraryDependencyTag. Each tag object is created globally and reused for multiple // dependencies (although since the object contains no references, assigning a tag to a diff --git a/cc/cc_test.go b/cc/cc_test.go index 6cc500b5f..d1b728ea2 100644 --- a/cc/cc_test.go +++ b/cc/cc_test.go @@ -2680,15 +2680,13 @@ func TestLlndkLibrary(t *testing.T) { } } expected := []string{ - "android_vendor.29_arm64_armv8-a_shared_current", "android_vendor.29_arm64_armv8-a_shared", - "android_vendor.29_arm_armv7-a-neon_shared_current", "android_vendor.29_arm_armv7-a-neon_shared", } android.AssertArrayString(t, "variants for llndk stubs", expected, actual) params := result.ModuleForTests("libllndk", "android_vendor.29_arm_armv7-a-neon_shared").Description("generate stub") - android.AssertSame(t, "use VNDK version for default stubs", "current", params.Args["apiLevel"]) + android.AssertSame(t, "use Vendor API level for default stubs", "202404", params.Args["apiLevel"]) checkExportedIncludeDirs := func(module, variant string, expectedDirs ...string) { t.Helper() diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go index 82beb293e..10342a3ba 100644 --- a/cc/config/arm64_device.go +++ b/cc/config/arm64_device.go @@ -49,8 +49,8 @@ var ( } arm64Ldflags = []string{ - "-Wl,--hash-style=gnu", "-Wl,-z,separate-code", + "-Wl,-z,separate-loadable-segments", } arm64Lldflags = arm64Ldflags diff --git a/cc/config/arm64_linux_host.go b/cc/config/arm64_linux_host.go index 335ad5672..f7d190b0b 100644 --- a/cc/config/arm64_linux_host.go +++ b/cc/config/arm64_linux_host.go @@ -42,7 +42,6 @@ var ( "-Wl,-z,now", "-Wl,--build-id=md5", "-Wl,--fatal-warnings", - "-Wl,--hash-style=gnu", "-Wl,--no-undefined-version", } diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go index 603bc6de7..3284e4b0a 100644 --- a/cc/config/arm_device.go +++ b/cc/config/arm_device.go @@ -38,7 +38,6 @@ var ( } armLdflags = []string{ - "-Wl,--hash-style=gnu", "-Wl,-m,armelf", // Revert this after b/322359235 is fixed "-Wl,-mllvm", "-Wl,-enable-shrink-wrap=false", diff --git a/cc/config/riscv64_device.go b/cc/config/riscv64_device.go index 6a84fee9f..47f0de1c8 100644 --- a/cc/config/riscv64_device.go +++ b/cc/config/riscv64_device.go @@ -23,24 +23,29 @@ import ( var ( riscv64Cflags = []string{ - // Help catch common 32/64-bit errors. + // Help catch common 32/64-bit errors. (This is duplicated in all 64-bit + // architectures' cflags.) "-Werror=implicit-function-declaration", + // This is already the driver's Android default, but duplicated here (and + // below) for ease of experimentation with additional extensions. "-march=rv64gcv_zba_zbb_zbs", - "-munaligned-access", - // Until https://gitlab.com/qemu-project/qemu/-/issues/1976 is fixed... + // TODO: move to driver (https://github.com/google/android-riscv64/issues/111) + "-mno-strict-align", + // TODO: remove when qemu V works (https://gitlab.com/qemu-project/qemu/-/issues/1976) + // (Note that we'll probably want to wait for berberis to be good enough + // that most people don't care about qemu's V performance either!) "-mno-implicit-float", - // (https://github.com/google/android-riscv64/issues/124) + // TODO: remove when clang default changed (https://github.com/google/android-riscv64/issues/124) "-mllvm -jump-is-expensive=false", } riscv64ArchVariantCflags = map[string][]string{} riscv64Ldflags = []string{ - "-Wl,--hash-style=gnu", + // This is already the driver's Android default, but duplicated here (and + // above) for ease of experimentation with additional extensions. "-march=rv64gcv_zba_zbb_zbs", - "-munaligned-access", - // We should change the default for this in clang, but for now... - // (https://github.com/google/android-riscv64/issues/124) + // TODO: remove when clang default changed (https://github.com/google/android-riscv64/issues/124) "-Wl,-mllvm -Wl,-jump-is-expensive=false", } diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go index b97d511b7..ca2c2b7b6 100644 --- a/cc/config/x86_64_device.go +++ b/cc/config/x86_64_device.go @@ -30,7 +30,7 @@ var ( x86_64Cppflags = []string{} x86_64Ldflags = []string{ - "-Wl,--hash-style=gnu", + "-Wl,-z,separate-loadable-segments", } X86_64Lldflags = x86_64Ldflags diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go index 2faa6709f..60b833906 100644 --- a/cc/config/x86_device.go +++ b/cc/config/x86_device.go @@ -33,9 +33,7 @@ var ( x86Cppflags = []string{} - x86Ldflags = []string{ - "-Wl,--hash-style=gnu", - } + x86Ldflags = []string{} x86ArchVariantCflags = map[string][]string{ "": []string{ diff --git a/cc/config/x86_linux_bionic_host.go b/cc/config/x86_linux_bionic_host.go index f80be9915..99d4ebb7f 100644 --- a/cc/config/x86_linux_bionic_host.go +++ b/cc/config/x86_linux_bionic_host.go @@ -46,7 +46,6 @@ var ( "-Wl,-z,now", "-Wl,--build-id=md5", "-Wl,--fatal-warnings", - "-Wl,--hash-style=gnu", "-Wl,--no-undefined-version", // Use the device gcc toolchain diff --git a/cc/genrule_test.go b/cc/genrule_test.go index 05c644f1e..08962065a 100644 --- a/cc/genrule_test.go +++ b/cc/genrule_test.go @@ -210,3 +210,47 @@ func TestVendorProductVariantGenrule(t *testing.T) { t.Errorf(`expected product variant, but does not exist in %v`, variants) } } + +// cc_genrule is initialized to android.InitAndroidArchModule +// that is an architecture-specific Android module. +// So testing properties tagged with `android:"arch_variant"` +// for cc_genrule. +func TestMultilibGenruleOut(t *testing.T) { + bp := ` + cc_genrule { + name: "gen", + cmd: "cp $(in) $(out)", + srcs: ["foo"], + multilib: { + lib32: { + out: [ + "subdir32/external-module32", + ], + }, + lib64: { + out: [ + "subdir64/external-module64", + ], + }, + }, + } + ` + result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, bp) + gen_32bit := result.ModuleForTests("gen", "android_arm_armv7-a-neon").OutputFiles(t, "") + android.AssertPathsEndWith(t, + "genrule_out", + []string{ + "subdir32/external-module32", + }, + gen_32bit, + ) + + gen_64bit := result.ModuleForTests("gen", "android_arm64_armv8-a").OutputFiles(t, "") + android.AssertPathsEndWith(t, + "genrule_out", + []string{ + "subdir64/external-module64", + }, + gen_64bit, + ) +} diff --git a/cc/library.go b/cc/library.go index e2b4d4f4b..560763243 100644 --- a/cc/library.go +++ b/cc/library.go @@ -677,18 +677,16 @@ func (library *libraryDecorator) getHeaderAbiCheckerProperties(ctx android.BaseM func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects { if ctx.IsLlndk() { - // This is the vendor variant of an LLNDK library, build the LLNDK stubs. - vndkVer := ctx.Module().(*Module).VndkVersion() - if !inList(vndkVer, ctx.Config().PlatformVersionActiveCodenames()) || vndkVer == "" { - // For non-enforcing devices, vndkVer is empty. Use "current" in that case, too. - vndkVer = "current" - } - if library.stubsVersion() != "" { - vndkVer = library.stubsVersion() + vendorApiLevel := ctx.Config().VendorApiLevel() + if vendorApiLevel == "" { + // TODO(b/321892570): Some tests relying on old fixtures which + // doesn't set vendorApiLevel. Needs to fix them. + vendorApiLevel = ctx.Config().PlatformSdkVersion().String() } + // This is the vendor variant of an LLNDK library, build the LLNDK stubs. nativeAbiResult := parseNativeAbiDefinition(ctx, String(library.Properties.Llndk.Symbol_file), - android.ApiLevelOrPanic(ctx, vndkVer), "--llndk") + android.ApiLevelOrPanic(ctx, vendorApiLevel), "--llndk") objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc) if !Bool(library.Properties.Llndk.Unversioned) { library.versionScriptPath = android.OptionalPathForPath( @@ -1893,6 +1891,10 @@ func (library *libraryDecorator) symbolFileForAbiCheck(ctx ModuleContext) *strin if library.hasStubsVariants() && library.Properties.Stubs.Symbol_file != nil { return library.Properties.Stubs.Symbol_file } + // TODO(b/309880485): Distinguish platform, NDK, LLNDK, and APEX version scripts. + if library.baseLinker.Properties.Version_script != nil { + return library.baseLinker.Properties.Version_script + } return nil } @@ -1913,12 +1915,15 @@ func (library *libraryDecorator) stubsVersions(ctx android.BaseMutatorContext) [ } if library.hasLLNDKStubs() && ctx.Module().(*Module).InVendorOrProduct() { - // LLNDK libraries only need a single stubs variant. - return []string{android.FutureApiLevel.String()} + // LLNDK libraries only need a single stubs variant (""), which is + // added automatically in createVersionVariations(). + return nil } // Future API level is implicitly added if there isn't - return addCurrentVersionIfNotPresent(library.Properties.Stubs.Versions) + versions := addCurrentVersionIfNotPresent(library.Properties.Stubs.Versions) + normalizeVersions(ctx, versions) + return versions } func addCurrentVersionIfNotPresent(vers []string) []string { @@ -2290,10 +2295,6 @@ func setStubsVersions(mctx android.BottomUpMutatorContext, library libraryInterf return } versions := library.stubsVersions(mctx) - if len(versions) <= 0 { - return - } - normalizeVersions(mctx, versions) if mctx.Failed() { return } diff --git a/cc/linker.go b/cc/linker.go index 2c50db2d6..9686697c8 100644 --- a/cc/linker.go +++ b/cc/linker.go @@ -530,13 +530,6 @@ func (linker *baseLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags { flags.Global.LdFlags = append(flags.Global.LdFlags, RpathFlags(ctx)...) } - if ctx.useSdk() { - // The bionic linker now has support gnu style hashes (which are much faster!), but shipping - // to older devices requires the old style hash. Fortunately, we can build with both and - // it'll work anywhere. - flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--hash-style=both") - } - flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.ToolchainLdflags()) // Version_script is not needed when linking stubs lib where the version diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py index 1e0bdf3ff..22f31d9f1 100755 --- a/cc/ndkstubgen/test_ndkstubgen.py +++ b/cc/ndkstubgen/test_ndkstubgen.py @@ -463,6 +463,98 @@ class IntegrationTest(unittest.TestCase): """) self.assertEqual(expected_version, version_file.getvalue()) + def test_integration_with_llndk(self) -> None: + input_file = io.StringIO(textwrap.dedent("""\ + VERSION_34 { # introduced=34 + global: + foo; + bar; # llndk + }; + VERSION_35 { # introduced=35 + global: + wiggle; + waggle; + waggle; # llndk=202404 + bubble; # llndk=202404 + duddle; + duddle; # llndk=202504 + } VERSION_34; + """)) + f = copy(self.filter) + f.llndk = True + f.api = 202404 + parser = symbolfile.SymbolFileParser(input_file, {}, f) + versions = parser.parse() + + src_file = io.StringIO() + version_file = io.StringIO() + symbol_list_file = io.StringIO() + + generator = ndkstubgen.Generator(src_file, + version_file, symbol_list_file, f) + generator.write(versions) + + expected_src = textwrap.dedent("""\ + void foo() {} + void bar() {} + void waggle() {} + void bubble() {} + """) + self.assertEqual(expected_src, src_file.getvalue()) + + expected_version = textwrap.dedent("""\ + VERSION_34 { + global: + foo; + bar; + }; + VERSION_35 { + global: + waggle; + bubble; + } VERSION_34; + """) + self.assertEqual(expected_version, version_file.getvalue()) + + def test_integration_with_llndk_with_single_version_block(self) -> None: + input_file = io.StringIO(textwrap.dedent("""\ + LIBANDROID { + global: + foo; # introduced=34 + bar; # introduced=35 + bar; # llndk=202404 + baz; # introduced=35 + }; + """)) + f = copy(self.filter) + f.llndk = True + f.api = 202404 + parser = symbolfile.SymbolFileParser(input_file, {}, f) + versions = parser.parse() + + src_file = io.StringIO() + version_file = io.StringIO() + symbol_list_file = io.StringIO() + + generator = ndkstubgen.Generator(src_file, + version_file, symbol_list_file, f) + generator.write(versions) + + expected_src = textwrap.dedent("""\ + void foo() {} + void bar() {} + """) + self.assertEqual(expected_src, src_file.getvalue()) + + expected_version = textwrap.dedent("""\ + LIBANDROID { + global: + foo; + bar; + }; + """) + self.assertEqual(expected_version, version_file.getvalue()) + def test_empty_stub(self) -> None: """Tests that empty stubs can be generated. diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py index 345e9f983..4553616ac 100644 --- a/cc/symbolfile/__init__.py +++ b/cc/symbolfile/__init__.py @@ -103,13 +103,24 @@ class Tags: @property def has_llndk_tags(self) -> bool: """Returns True if any LL-NDK tags are set.""" - return 'llndk' in self.tags + for tag in self.tags: + if tag == 'llndk' or tag.startswith('llndk='): + return True + return False @property def has_platform_only_tags(self) -> bool: """Returns True if any platform-only tags are set.""" return 'platform-only' in self.tags + def copy_introduced_from(self, tags: Tags) -> None: + """Copies introduced= or introduced-*= tags.""" + for tag in tags: + if tag.startswith('introduced=') or tag.startswith('introduced-'): + name, _ = split_tag(tag) + if not any(self_tag.startswith(name + '=') for self_tag in self.tags): + self.tags += (tag,) + @dataclass class Symbol: @@ -147,6 +158,8 @@ def is_api_level_tag(tag: Tag) -> bool: """Returns true if this tag has an API level that may need decoding.""" if tag.startswith('llndk-deprecated='): return True + if tag.startswith('llndk='): + return True if tag.startswith('introduced='): return True if tag.startswith('introduced-'): @@ -237,15 +250,22 @@ class Filter: This defines the rules shared between version tagging and symbol tagging. """ - # The apex and llndk tags will only exclude APIs from other modes. If in + # LLNDK mode/tags follow the similar filtering except that API level checking + # is based llndk= instead of introduced=. + if self.llndk: + if tags.has_mode_tags and not tags.has_llndk_tags: + return True + if not symbol_in_arch(tags, self.arch): + return True + if not symbol_in_llndk_api(tags, self.arch, self.api): + return True + return False # APEX or LLNDK mode and neither tag is provided, we fall back to the # default behavior because all NDK symbols are implicitly available to # APEX and LLNDK. if tags.has_mode_tags: if self.apex and tags.has_apex_tags: return False - if self.llndk and tags.has_llndk_tags: - return False if self.systemapi and tags.has_systemapi_tags: return False return True @@ -266,6 +286,10 @@ class Filter: return True if version.tags.has_platform_only_tags: return True + # Include all versions when targeting LLNDK because LLNDK symbols are self-versioned. + # Empty version block will be handled separately. + if self.llndk: + return False return self._should_omit_tags(version.tags) def should_omit_symbol(self, symbol: Symbol) -> bool: @@ -292,6 +316,14 @@ def symbol_in_arch(tags: Tags, arch: Arch) -> bool: # for the tagged architectures. return not has_arch_tags +def symbol_in_llndk_api(tags: Iterable[Tag], arch: Arch, api: int) -> bool: + """Returns true if the symbol is present for the given LLNDK API level.""" + # Check llndk= first. + for tag in tags: + if tag.startswith('llndk='): + return api >= int(get_tag_value(tag)) + # If not, we keep old behavior: NDK symbols in <= 34 are LLNDK symbols. + return symbol_in_api(tags, arch, 34) def symbol_in_api(tags: Iterable[Tag], arch: Arch, api: int) -> bool: """Returns true if the symbol is present for the given API level.""" @@ -368,6 +400,7 @@ class SymbolFileParser: f'Unexpected contents at top level: {self.current_line}') self.check_no_duplicate_symbols(versions) + self.check_llndk_introduced(versions) return versions def check_no_duplicate_symbols(self, versions: Iterable[Version]) -> None: @@ -396,6 +429,31 @@ class SymbolFileParser: raise MultiplyDefinedSymbolError( sorted(list(multiply_defined_symbols))) + def check_llndk_introduced(self, versions: Iterable[Version]) -> None: + """Raises errors when llndk= is missing for new llndk symbols.""" + if not self.filter.llndk: + return + + def assert_llndk_with_version(tags: Tags, name: str) -> None: + has_llndk_introduced = False + for tag in tags: + if tag.startswith('llndk='): + has_llndk_introduced = True + break + if not has_llndk_introduced: + raise ParseError(f'{name}: missing version. `llndk=yyyymm`') + + arch = self.filter.arch + for version in versions: + # llndk symbols >= introduced=35 should be tagged + # explicitly with llndk=yyyymm. + for symbol in version.symbols: + if not symbol.tags.has_llndk_tags: + continue + if symbol_in_api(symbol.tags, arch, 34): + continue + assert_llndk_with_version(symbol.tags, symbol.name) + def parse_version(self) -> Version: """Parses a single version section and returns a Version object.""" assert self.current_line is not None @@ -429,7 +487,9 @@ class SymbolFileParser: else: raise ParseError('Unknown visiblity label: ' + visibility) elif global_scope and not cpp_symbols: - symbols.append(self.parse_symbol()) + symbol = self.parse_symbol() + symbol.tags.copy_introduced_from(tags) + symbols.append(symbol) else: # We're in a hidden scope or in 'extern "C++"' block. Ignore # everything. diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py index 83becc2c7..8b412b98a 100644 --- a/cc/symbolfile/test_symbolfile.py +++ b/cc/symbolfile/test_symbolfile.py @@ -344,6 +344,45 @@ class OmitSymbolTest(unittest.TestCase): self.assertInclude(f_llndk, s_none) self.assertInclude(f_llndk, s_llndk) + def test_omit_llndk_versioned(self) -> None: + f_ndk = self.filter + f_ndk.api = 35 + + f_llndk = copy(f_ndk) + f_llndk.llndk = True + f_llndk.api = 202404 + + s = Symbol('foo', Tags()) + s_llndk = Symbol('foo', Tags.from_strs(['llndk'])) + s_llndk_202404 = Symbol('foo', Tags.from_strs(['llndk=202404'])) + s_34 = Symbol('foo', Tags.from_strs(['introduced=34'])) + s_34_llndk = Symbol('foo', Tags.from_strs(['introduced=34', 'llndk'])) + s_35 = Symbol('foo', Tags.from_strs(['introduced=35'])) + s_35_llndk_202404 = Symbol('foo', Tags.from_strs(['introduced=35', 'llndk=202404'])) + s_35_llndk_202504 = Symbol('foo', Tags.from_strs(['introduced=35', 'llndk=202504'])) + + # When targeting NDK, omit LLNDK tags + self.assertInclude(f_ndk, s) + self.assertOmit(f_ndk, s_llndk) + self.assertOmit(f_ndk, s_llndk_202404) + self.assertInclude(f_ndk, s_34) + self.assertOmit(f_ndk, s_34_llndk) + self.assertInclude(f_ndk, s_35) + self.assertOmit(f_ndk, s_35_llndk_202404) + self.assertOmit(f_ndk, s_35_llndk_202504) + + # When targeting LLNDK, old symbols without any mode tags are included as LLNDK + self.assertInclude(f_llndk, s) + # When targeting LLNDK, old symbols with #llndk are included as LLNDK + self.assertInclude(f_llndk, s_llndk) + self.assertInclude(f_llndk, s_llndk_202404) + self.assertInclude(f_llndk, s_34) + self.assertInclude(f_llndk, s_34_llndk) + # When targeting LLNDK, new symbols(>=35) should be tagged with llndk-introduced=. + self.assertOmit(f_llndk, s_35) + self.assertInclude(f_llndk, s_35_llndk_202404) + self.assertOmit(f_llndk, s_35_llndk_202504) + def test_omit_apex(self) -> None: f_none = self.filter f_apex = copy(f_none) @@ -451,9 +490,12 @@ class SymbolFileParseTest(unittest.TestCase): self.assertIsNone(version.base) self.assertEqual(Tags.from_strs(['weak', 'introduced=35']), version.tags) + # Inherit introduced= tags from version block so that + # should_omit_tags() can differently based on introduced API level when treating + # LLNDK-available symbols. expected_symbols = [ - Symbol('baz', Tags()), - Symbol('qux', Tags.from_strs(['apex', 'llndk'])), + Symbol('baz', Tags.from_strs(['introduced=35'])), + Symbol('qux', Tags.from_strs(['apex', 'llndk', 'introduced=35'])), ] self.assertEqual(expected_symbols, version.symbols) @@ -601,6 +643,19 @@ class SymbolFileParseTest(unittest.TestCase): ] self.assertEqual(expected_symbols, version.symbols) + def test_parse_llndk_version_is_missing(self) -> None: + input_file = io.StringIO(textwrap.dedent("""\ + VERSION_1 { # introduced=35 + foo; + bar; # llndk + }; + """)) + f = copy(self.filter) + f.llndk = True + parser = symbolfile.SymbolFileParser(input_file, {}, f) + with self.assertRaises(symbolfile.ParseError): + parser.parse() + def main() -> None: suite = unittest.TestLoader().loadTestsFromName(__name__) diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go index 5c2316a31..eafd67a2d 100644 --- a/cmd/soong_build/queryview.go +++ b/cmd/soong_build/queryview.go @@ -22,7 +22,6 @@ import ( "android/soong/android" "android/soong/bp2build" - "android/soong/starlark_import" ) // A helper function to generate a Read-only Bazel workspace in outDir @@ -47,14 +46,6 @@ func createBazelWorkspace(ctx *bp2build.CodegenContext, outDir string, generateF } } - // Add starlark deps here, so that they apply to both queryview and apibp2build which - // both run this function. - starlarkDeps, err2 := starlark_import.GetNinjaDeps() - if err2 != nil { - return err2 - } - ctx.AddNinjaFileDeps(starlarkDeps...) - return nil } diff --git a/cmd/symbols_map/Android.bp b/cmd/symbols_map/Android.bp index 0ba3b07a8..e3ae6ede5 100644 --- a/cmd/symbols_map/Android.bp +++ b/cmd/symbols_map/Android.bp @@ -5,17 +5,16 @@ package { blueprint_go_binary { name: "symbols_map", srcs: [ - "elf.go", "r8.go", "symbols_map.go", ], testSrcs: [ - "elf_test.go", "r8_test.go", ], deps: [ "blueprint-pathtools", "golang-protobuf-encoding-prototext", + "soong-elf", "soong-response", "symbols_map_proto", ], diff --git a/cmd/symbols_map/symbols_map.go b/cmd/symbols_map/symbols_map.go index 938446d48..c56cf93e8 100644 --- a/cmd/symbols_map/symbols_map.go +++ b/cmd/symbols_map/symbols_map.go @@ -22,6 +22,7 @@ import ( "strings" "android/soong/cmd/symbols_map/symbols_map_proto" + "android/soong/elf" "android/soong/response" "github.com/google/blueprint/pathtools" @@ -116,7 +117,7 @@ func main() { if *elfFile != "" { typ = symbols_map_proto.Mapping_ELF location = *elfFile - identifier, err = elfIdentifier(*elfFile, true) + identifier, err = elf.Identifier(*elfFile, true) if err != nil { fmt.Fprintf(os.Stderr, "error reading elf identifier: %s\n", err) os.Exit(1) diff --git a/starlark_import/Android.bp b/elf/Android.bp index b43217b76..6450be137 100644 --- a/starlark_import/Android.bp +++ b/elf/Android.bp @@ -1,4 +1,4 @@ -// Copyright 2023 Google Inc. All rights reserved. +// Copyright 2016 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. @@ -17,20 +17,12 @@ package { } bootstrap_go_package { - name: "soong-starlark", - pkgPath: "android/soong/starlark_import", + name: "soong-elf", + pkgPath: "android/soong/elf", srcs: [ - "starlark_import.go", - "unmarshal.go", + "elf.go", ], testSrcs: [ - "starlark_import_test.go", - "unmarshal_test.go", - ], - deps: [ - "go-starlark-starlark", - "go-starlark-starlarkstruct", - "go-starlark-starlarkjson", - "go-starlark-starlarktest", + "elf_test.go", ], } diff --git a/cmd/symbols_map/elf.go b/elf/elf.go index 950e3b246..e84a8aeea 100644 --- a/cmd/symbols_map/elf.go +++ b/elf/elf.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package elf import ( "debug/elf" @@ -26,9 +26,9 @@ import ( const gnuBuildID = "GNU\x00" -// elfIdentifier extracts the elf build ID from an elf file. If allowMissing is true it returns +// Identifier extracts the elf build ID from an elf file. If allowMissing is true it returns // an empty identifier if the file exists but the build ID note does not. -func elfIdentifier(filename string, allowMissing bool) (string, error) { +func Identifier(filename string, allowMissing bool) (string, error) { f, err := os.Open(filename) if err != nil { return "", fmt.Errorf("failed to open %s: %w", filename, err) diff --git a/cmd/symbols_map/elf_test.go b/elf/elf_test.go index a94c87f21..a22077090 100644 --- a/cmd/symbols_map/elf_test.go +++ b/elf/elf_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package elf import ( "bytes" diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go index 764237821..a42c576d5 100644 --- a/etc/prebuilt_etc.go +++ b/etc/prebuilt_etc.go @@ -71,10 +71,15 @@ var PrepareForTestWithPrebuiltEtc = android.FixtureRegisterWithContext(RegisterP type prebuiltEtcProperties struct { // Source file of this prebuilt. Can reference a genrule type module with the ":module" syntax. + // Mutually exclusive with srcs. Src *string `android:"path,arch_variant"` + // Source files of this prebuilt. Can reference a genrule type module with the ":module" syntax. + // Mutually exclusive with src. When used, filename_from_src is set to true. + Srcs []string `android:"path,arch_variant"` + // Optional name for the installed file. If unspecified, name of the module is used as the file - // name. + // name. Only available when using a single source (src). Filename *string `android:"arch_variant"` // When set to true, and filename property is not set, the name for the installed file @@ -127,9 +132,9 @@ type PrebuiltEtcModule interface { // Returns the sub install directory relative to BaseDir(). SubDir() string - // Returns an android.OutputPath to the intermeidate file, which is the renamed prebuilt source + // Returns an android.OutputPath to the intermediate file, which is the renamed prebuilt source // file. - OutputFile() android.OutputPath + OutputFiles(tag string) (android.Paths, error) } type PrebuiltEtc struct { @@ -142,8 +147,8 @@ type PrebuiltEtc struct { properties prebuiltEtcProperties subdirProperties prebuiltSubdirProperties - sourceFilePath android.Path - outputFilePath android.OutputPath + sourceFilePaths android.Paths + outputFilePaths android.OutputPaths // The base install location, e.g. "etc" for prebuilt_etc, "usr/share" for prebuilt_usr_share. installDirBase string installDirBase64 string @@ -246,6 +251,9 @@ func (p *PrebuiltEtc) SetImageVariation(ctx android.BaseModuleContext, variation } func (p *PrebuiltEtc) SourceFilePath(ctx android.ModuleContext) android.Path { + if len(p.properties.Srcs) > 0 { + panic(fmt.Errorf("SourceFilePath not available on multi-source prebuilt %q", p.Name())) + } return android.PathForModuleSrc(ctx, proptools.String(p.properties.Src)) } @@ -260,7 +268,10 @@ func (p *PrebuiltEtc) SetAdditionalDependencies(paths android.Paths) { } func (p *PrebuiltEtc) OutputFile() android.OutputPath { - return p.outputFilePath + if len(p.properties.Srcs) > 0 { + panic(fmt.Errorf("OutputFile not available on multi-source prebuilt %q", p.Name())) + } + return p.outputFilePaths[0] } var _ android.OutputFileProducer = (*PrebuiltEtc)(nil) @@ -268,7 +279,7 @@ var _ android.OutputFileProducer = (*PrebuiltEtc)(nil) func (p *PrebuiltEtc) OutputFiles(tag string) (android.Paths, error) { switch tag { case "": - return android.Paths{p.outputFilePath}, nil + return p.outputFilePaths.Paths(), nil default: return nil, fmt.Errorf("unsupported module reference tag %q", tag) } @@ -301,11 +312,44 @@ func (p *PrebuiltEtc) ExcludeFromRecoverySnapshot() bool { return false } +func (p *PrebuiltEtc) installBaseDir(ctx android.ModuleContext) string { + // If soc install dir was specified and SOC specific is set, set the installDirPath to the + // specified socInstallDirBase. + installBaseDir := p.installDirBase + if p.Target().Arch.ArchType.Multilib == "lib64" && p.installDirBase64 != "" { + installBaseDir = p.installDirBase64 + } + if p.SocSpecific() && p.socInstallDirBase != "" { + installBaseDir = p.socInstallDirBase + } + if p.installAvoidMultilibConflict && !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) { + installBaseDir = filepath.Join(installBaseDir, ctx.Arch().ArchType.String()) + } + return installBaseDir +} + func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) { + var installs []installProperties + + if p.properties.Src != nil && len(p.properties.Srcs) > 0 { + ctx.PropertyErrorf("src", "src is set. Cannot set srcs") + } + + // Check that `sub_dir` and `relative_install_path` are not set at the same time. + if p.subdirProperties.Sub_dir != nil && p.subdirProperties.Relative_install_path != nil { + ctx.PropertyErrorf("sub_dir", "relative_install_path is set. Cannot set sub_dir") + } + p.installDirPath = android.PathForModuleInstall(ctx, p.installBaseDir(ctx), p.SubDir()) + filename := proptools.String(p.properties.Filename) filenameFromSrc := proptools.Bool(p.properties.Filename_from_src) if p.properties.Src != nil { - p.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(p.properties.Src)) + p.sourceFilePaths = android.PathsForModuleSrc(ctx, []string{proptools.String(p.properties.Src)}) + // If the source was not found, set a fake source path to + // support AllowMissingDependencies executions. + if len(p.sourceFilePaths) == 0 { + p.sourceFilePaths = android.Paths{android.PathForModuleSrc(ctx)} + } // Determine the output file basename. // If Filename is set, use the name specified by the property. @@ -317,87 +361,102 @@ func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) { return } } else if filenameFromSrc { - filename = p.sourceFilePath.Base() + filename = p.sourceFilePaths[0].Base() } else { filename = ctx.ModuleName() } + if strings.Contains(filename, "/") { + ctx.PropertyErrorf("filename", "filename cannot contain separator '/'") + return + } + p.outputFilePaths = android.OutputPaths{android.PathForModuleOut(ctx, filename).OutputPath} + + ip := installProperties{ + filename: filename, + sourceFilePath: p.sourceFilePaths[0], + outputFilePath: p.outputFilePaths[0], + installDirPath: p.installDirPath, + symlinks: p.properties.Symlinks, + } + installs = append(installs, ip) + } else if len(p.properties.Srcs) > 0 { + if filename != "" { + ctx.PropertyErrorf("filename", "filename cannot be set when using srcs") + } + if len(p.properties.Symlinks) > 0 { + ctx.PropertyErrorf("symlinks", "symlinks cannot be set when using srcs") + } + if p.properties.Filename_from_src != nil { + ctx.PropertyErrorf("filename_from_src", "filename_from_src is implicitly set to true when using srcs") + } + p.sourceFilePaths = android.PathsForModuleSrc(ctx, p.properties.Srcs) + for _, src := range p.sourceFilePaths { + filename := src.Base() + output := android.PathForModuleOut(ctx, filename).OutputPath + ip := installProperties{ + filename: filename, + sourceFilePath: src, + outputFilePath: output, + installDirPath: p.installDirPath, + } + p.outputFilePaths = append(p.outputFilePaths, output) + installs = append(installs, ip) + } } else if ctx.Config().AllowMissingDependencies() { // If no srcs was set and AllowMissingDependencies is enabled then // mark the module as missing dependencies and set a fake source path // and file name. ctx.AddMissingDependencies([]string{"MISSING_PREBUILT_SRC_FILE"}) - p.sourceFilePath = android.PathForModuleSrc(ctx) + p.sourceFilePaths = android.Paths{android.PathForModuleSrc(ctx)} if filename == "" { filename = ctx.ModuleName() } + p.outputFilePaths = android.OutputPaths{android.PathForModuleOut(ctx, filename).OutputPath} + ip := installProperties{ + filename: filename, + sourceFilePath: p.sourceFilePaths[0], + outputFilePath: p.outputFilePaths[0], + installDirPath: p.installDirPath, + } + installs = append(installs, ip) } else { ctx.PropertyErrorf("src", "missing prebuilt source file") return } - if strings.Contains(filename, "/") { - ctx.PropertyErrorf("filename", "filename cannot contain separator '/'") - return - } - - // Check that `sub_dir` and `relative_install_path` are not set at the same time. - if p.subdirProperties.Sub_dir != nil && p.subdirProperties.Relative_install_path != nil { - ctx.PropertyErrorf("sub_dir", "relative_install_path is set. Cannot set sub_dir") - } - - // If soc install dir was specified and SOC specific is set, set the installDirPath to the - // specified socInstallDirBase. - installBaseDir := p.installDirBase - if p.Target().Arch.ArchType.Multilib == "lib64" && p.installDirBase64 != "" { - installBaseDir = p.installDirBase64 - } - if p.SocSpecific() && p.socInstallDirBase != "" { - installBaseDir = p.socInstallDirBase - } - if p.installAvoidMultilibConflict && !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) { - installBaseDir = filepath.Join(installBaseDir, ctx.Arch().ArchType.String()) + // Call InstallFile even when uninstallable to make the module included in the package. + if !p.Installable() { + p.SkipInstall() } - - p.installDirPath = android.PathForModuleInstall(ctx, installBaseDir, p.SubDir()) - - // Call InstallFile even when uninstallable to make the module included in the package - ip := installProperties{ - installable: p.Installable(), - filename: filename, - sourceFilePath: p.sourceFilePath, - symlinks: p.properties.Symlinks, + for _, ip := range installs { + ip.addInstallRules(ctx) } - p.addInstallRules(ctx, ip) android.CollectDependencyAconfigFiles(ctx, &p.mergedAconfigFiles) } type installProperties struct { - installable bool filename string sourceFilePath android.Path + outputFilePath android.OutputPath + installDirPath android.InstallPath symlinks []string } // utility function to add install rules to the build graph. // Reduces code duplication between Soong and Mixed build analysis -func (p *PrebuiltEtc) addInstallRules(ctx android.ModuleContext, ip installProperties) { - if !ip.installable { - p.SkipInstall() - } - +func (ip *installProperties) addInstallRules(ctx android.ModuleContext) { // Copy the file from src to a location in out/ with the correct `filename` // This ensures that outputFilePath has the correct name for others to // use, as the source file may have a different name. - p.outputFilePath = android.PathForModuleOut(ctx, ip.filename).OutputPath ctx.Build(pctx, android.BuildParams{ Rule: android.Cp, - Output: p.outputFilePath, + Output: ip.outputFilePath, Input: ip.sourceFilePath, }) - installPath := ctx.InstallFile(p.installDirPath, ip.filename, p.outputFilePath) + installPath := ctx.InstallFile(ip.installDirPath, ip.filename, ip.outputFilePath) for _, sl := range ip.symlinks { - ctx.InstallSymlink(p.installDirPath, sl, installPath) + ctx.InstallSymlink(ip.installDirPath, sl, installPath) } } @@ -421,15 +480,15 @@ func (p *PrebuiltEtc) AndroidMkEntries() []android.AndroidMkEntries { class = "ETC" } - return []android.AndroidMkEntries{android.AndroidMkEntries{ + return []android.AndroidMkEntries{{ Class: class, SubName: nameSuffix, - OutputFile: android.OptionalPathForPath(p.outputFilePath), + OutputFile: android.OptionalPathForPath(p.outputFilePaths[0]), ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetString("LOCAL_MODULE_TAGS", "optional") entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.String()) - entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base()) + entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePaths[0].Base()) if len(p.properties.Symlinks) > 0 { entries.AddStrings("LOCAL_MODULE_SYMLINKS", p.properties.Symlinks...) } @@ -700,7 +759,11 @@ func generatePrebuiltSnapshot(s snapshot.SnapshotSingleton, ctx android.Singleto targetArch := "arch-" + m.Target().Arch.ArchType.String() snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, "etc", m.BaseModuleName()) - snapshotOutputs = append(snapshotOutputs, copyFile(ctx, m.OutputFile(), snapshotLibOut, s.Fake)) + outputs, _ := m.OutputFiles("") + for _, output := range outputs { + cp := copyFile(ctx, output, snapshotLibOut, s.Fake) + snapshotOutputs = append(snapshotOutputs, cp) + } prop := snapshot.SnapshotJsonFlags{} propOut := snapshotLibOut + ".json" diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go index df117097e..1d9aa8e58 100644 --- a/etc/prebuilt_etc_test.go +++ b/etc/prebuilt_etc_test.go @@ -96,7 +96,7 @@ func TestPrebuiltEtcOutputPath(t *testing.T) { `) p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc) - android.AssertStringEquals(t, "output file path", "foo.installed.conf", p.outputFilePath.Base()) + android.AssertStringEquals(t, "output file path", "foo.installed.conf", p.outputFilePaths[0].Base()) } func TestPrebuiltEtcGlob(t *testing.T) { @@ -113,10 +113,24 @@ func TestPrebuiltEtcGlob(t *testing.T) { `) p := result.Module("my_foo", "android_arm64_armv8-a").(*PrebuiltEtc) - android.AssertStringEquals(t, "my_foo output file path", "my_foo", p.outputFilePath.Base()) + android.AssertStringEquals(t, "my_foo output file path", "my_foo", p.outputFilePaths[0].Base()) p = result.Module("my_bar", "android_arm64_armv8-a").(*PrebuiltEtc) - android.AssertStringEquals(t, "my_bar output file path", "bar.conf", p.outputFilePath.Base()) + android.AssertStringEquals(t, "my_bar output file path", "bar.conf", p.outputFilePaths[0].Base()) +} + +func TestPrebuiltEtcMultipleSrcs(t *testing.T) { + result := prepareForPrebuiltEtcTest.RunTestWithBp(t, ` + prebuilt_etc { + name: "foo", + srcs: ["*.conf"], + } + `) + + p := result.Module("foo", "android_arm64_armv8-a").(*PrebuiltEtc) + android.AssertStringEquals(t, "output file path", "bar.conf", p.outputFilePaths[0].Base()) + android.AssertStringEquals(t, "output file path", "baz.conf", p.outputFilePaths[1].Base()) + android.AssertStringEquals(t, "output file path", "foo.conf", p.outputFilePaths[2].Base()) } func TestPrebuiltEtcAndroidMk(t *testing.T) { diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go index 6612a6f09..efc889ccb 100644 --- a/filesystem/filesystem.go +++ b/filesystem/filesystem.go @@ -19,6 +19,7 @@ import ( "fmt" "io" "path/filepath" + "slices" "strings" "android/soong/android" @@ -87,6 +88,10 @@ type filesystemProperties struct { // is ext4. Type *string + // Identifies which partition this is for //visibility:any_system_image (and others) visibility + // checks, and will be used in the future for API surface checks. + Partition_type *string + // file_contexts file to make image. Currently, only ext4 is supported. File_contexts *string `android:"path"` @@ -109,6 +114,12 @@ type filesystemProperties struct { // Mount point for this image. Default is "/" Mount_point *string + + // If set to the name of a partition ("system", "vendor", etc), this filesystem module + // will also include the contents of the make-built staging directories. If any soong + // modules would be installed to the same location as a make module, they will overwrite + // the make version. + Include_make_built_files string } // android_filesystem packages a set of modules and their transitive dependencies into a filesystem @@ -168,6 +179,9 @@ func (f *filesystem) installFileName() string { var pctx = android.NewPackageContext("android/soong/filesystem") func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) { + if !android.InList(f.PartitionType(), validPartitions) { + ctx.PropertyErrorf("partition_type", "partition_type must be one of %s, found: %s", validPartitions, f.PartitionType()) + } switch f.fsType(ctx) { case ext4Type: f.output = f.buildImageUsingBuildImage(ctx) @@ -183,13 +197,9 @@ func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) { ctx.InstallFile(f.installDir, f.installFileName(), f.output) } -// root zip will contain extra files/dirs that are not from the `deps` property. -func (f *filesystem) buildRootZip(ctx android.ModuleContext) android.OutputPath { - rootDir := android.PathForModuleGen(ctx, "root").OutputPath - builder := android.NewRuleBuilder(pctx, ctx) - builder.Command().Text("rm -rf").Text(rootDir.String()) - builder.Command().Text("mkdir -p").Text(rootDir.String()) - +// Copy extra files/dirs that are not from the `deps` property to `rootDir`, checking for conflicts with files +// already in `rootDir`. +func (f *filesystem) buildNonDepsFiles(ctx android.ModuleContext, builder *android.RuleBuilder, rootDir android.OutputPath) { // create dirs and symlinks for _, dir := range f.properties.Dirs { // OutputPath.Join verifies dir @@ -212,65 +222,43 @@ func (f *filesystem) buildRootZip(ctx android.ModuleContext) android.OutputPath // OutputPath.Join verifies name. don't need to verify target. dst := rootDir.Join(ctx, name) - + builder.Command().Textf("(! [ -e %s -o -L %s ] || (echo \"%s already exists from an earlier stage of the build\" && exit 1))", dst, dst, dst) builder.Command().Text("mkdir -p").Text(filepath.Dir(dst.String())) builder.Command().Text("ln -sf").Text(proptools.ShellEscape(target)).Text(dst.String()) } // create extra files if there's any - rootForExtraFiles := android.PathForModuleGen(ctx, "root-extra").OutputPath - var extraFiles android.OutputPaths if f.buildExtraFiles != nil { - extraFiles = f.buildExtraFiles(ctx, rootForExtraFiles) + rootForExtraFiles := android.PathForModuleGen(ctx, "root-extra").OutputPath + extraFiles := f.buildExtraFiles(ctx, rootForExtraFiles) for _, f := range extraFiles { - rel, _ := filepath.Rel(rootForExtraFiles.String(), f.String()) - if strings.HasPrefix(rel, "..") { - panic(fmt.Errorf("%q is not under %q\n", f, rootForExtraFiles)) + rel, err := filepath.Rel(rootForExtraFiles.String(), f.String()) + if err != nil || strings.HasPrefix(rel, "..") { + ctx.ModuleErrorf("can't make %q relative to %q", f, rootForExtraFiles) } } - } - - // Zip them all - zipOut := android.PathForModuleGen(ctx, "root.zip").OutputPath - zipCommand := builder.Command().BuiltTool("soong_zip") - zipCommand.FlagWithOutput("-o ", zipOut). - FlagWithArg("-C ", rootDir.String()). - Flag("-L 0"). // no compression because this will be unzipped soon - FlagWithArg("-D ", rootDir.String()). - Flag("-d") // include empty directories - if len(extraFiles) > 0 { - zipCommand.FlagWithArg("-C ", rootForExtraFiles.String()) - for _, f := range extraFiles { - zipCommand.FlagWithInput("-f ", f) + if len(extraFiles) > 0 { + builder.Command().BuiltTool("merge_directories"). + Implicits(extraFiles.Paths()). + Text(rootDir.String()). + Text(rootForExtraFiles.String()) } } - - builder.Command().Text("rm -rf").Text(rootDir.String()) - - builder.Build("zip_root", fmt.Sprintf("zipping root contents for %s", ctx.ModuleName())) - return zipOut } func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) android.OutputPath { - depsZipFile := android.PathForModuleOut(ctx, "deps.zip").OutputPath - f.entries = f.CopyDepsToZip(ctx, f.gatherFilteredPackagingSpecs(ctx), depsZipFile) - + rootDir := android.PathForModuleOut(ctx, "root").OutputPath + rebasedDir := rootDir + if f.properties.Base_dir != nil { + rebasedDir = rootDir.Join(ctx, *f.properties.Base_dir) + } builder := android.NewRuleBuilder(pctx, ctx) - depsBase := proptools.StringDefault(f.properties.Base_dir, ".") - rebasedDepsZip := android.PathForModuleOut(ctx, "rebased_deps.zip").OutputPath - builder.Command(). - BuiltTool("zip2zip"). - FlagWithInput("-i ", depsZipFile). - FlagWithOutput("-o ", rebasedDepsZip). - Text("**/*:" + proptools.ShellEscape(depsBase)) // zip2zip verifies depsBase + // Wipe the root dir to get rid of leftover files from prior builds + builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir) + f.entries = f.CopySpecsToDir(ctx, builder, f.gatherFilteredPackagingSpecs(ctx), rebasedDir) - rootDir := android.PathForModuleOut(ctx, "root").OutputPath - rootZip := f.buildRootZip(ctx) - builder.Command(). - BuiltTool("zipsync"). - FlagWithArg("-d ", rootDir.String()). // zipsync wipes this. No need to clear. - Input(rootZip). - Input(rebasedDepsZip) + f.buildNonDepsFiles(ctx, builder, rootDir) + f.addMakeBuiltFiles(ctx, builder, rootDir) // run host_init_verifier // Ideally we should have a concept of pluggable linters that verify the generated image. @@ -311,18 +299,16 @@ func (f *filesystem) salt() string { } func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android.OutputPath, toolDeps android.Paths) { - type prop struct { - name string - value string - } - - var props []prop var deps android.Paths + var propFileString strings.Builder addStr := func(name string, value string) { - props = append(props, prop{name, value}) + propFileString.WriteString(name) + propFileString.WriteRune('=') + propFileString.WriteString(value) + propFileString.WriteRune('\n') } addPath := func(name string, path android.Path) { - props = append(props, prop{name, path.String()}) + addStr(name, path.String()) deps = append(deps, path) } @@ -376,15 +362,7 @@ func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android. addStr("hash_seed", uuid) } propFile = android.PathForModuleOut(ctx, "prop").OutputPath - builder := android.NewRuleBuilder(pctx, ctx) - builder.Command().Text("rm").Flag("-rf").Output(propFile) - for _, p := range props { - builder.Command(). - Text("echo"). - Flag(`"` + p.name + "=" + p.value + `"`). - Text(">>").Output(propFile) - } - builder.Build("build_filesystem_prop", fmt.Sprintf("Creating filesystem props for %s", f.BaseModuleName())) + android.WriteFileRuleVerbatim(ctx, propFile, propFileString.String()) return propFile, deps } @@ -398,25 +376,21 @@ func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool) ctx.PropertyErrorf("file_contexts", "file_contexts is not supported for compressed cpio image.") } - depsZipFile := android.PathForModuleOut(ctx, "deps.zip").OutputPath - f.entries = f.CopyDepsToZip(ctx, f.gatherFilteredPackagingSpecs(ctx), depsZipFile) + if f.properties.Include_make_built_files != "" { + ctx.PropertyErrorf("include_make_built_files", "include_make_built_files is not supported for compressed cpio image.") + } + rootDir := android.PathForModuleOut(ctx, "root").OutputPath + rebasedDir := rootDir + if f.properties.Base_dir != nil { + rebasedDir = rootDir.Join(ctx, *f.properties.Base_dir) + } builder := android.NewRuleBuilder(pctx, ctx) - depsBase := proptools.StringDefault(f.properties.Base_dir, ".") - rebasedDepsZip := android.PathForModuleOut(ctx, "rebased_deps.zip").OutputPath - builder.Command(). - BuiltTool("zip2zip"). - FlagWithInput("-i ", depsZipFile). - FlagWithOutput("-o ", rebasedDepsZip). - Text("**/*:" + proptools.ShellEscape(depsBase)) // zip2zip verifies depsBase + // Wipe the root dir to get rid of leftover files from prior builds + builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir) + f.entries = f.CopySpecsToDir(ctx, builder, f.gatherFilteredPackagingSpecs(ctx), rebasedDir) - rootDir := android.PathForModuleOut(ctx, "root").OutputPath - rootZip := f.buildRootZip(ctx) - builder.Command(). - BuiltTool("zipsync"). - FlagWithArg("-d ", rootDir.String()). // zipsync wipes this. No need to clear. - Input(rootZip). - Input(rebasedDepsZip) + f.buildNonDepsFiles(ctx, builder, rootDir) output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath cmd := builder.Command(). @@ -439,6 +413,45 @@ func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool) return output } +var validPartitions = []string{ + "system", + "userdata", + "cache", + "system_other", + "vendor", + "product", + "system_ext", + "odm", + "vendor_dlkm", + "odm_dlkm", + "system_dlkm", +} + +func (f *filesystem) addMakeBuiltFiles(ctx android.ModuleContext, builder *android.RuleBuilder, rootDir android.Path) { + partition := f.properties.Include_make_built_files + if partition == "" { + return + } + if !slices.Contains(validPartitions, partition) { + ctx.PropertyErrorf("include_make_built_files", "Expected one of %#v, found %q", validPartitions, partition) + return + } + stampFile := fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/staging_dir.stamp", ctx.Config().DeviceName(), partition) + fileListFile := fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/file_list.txt", ctx.Config().DeviceName(), partition) + stagingDir := fmt.Sprintf("target/product/%s/%s", ctx.Config().DeviceName(), partition) + + builder.Command().BuiltTool("merge_directories"). + Implicit(android.PathForArbitraryOutput(ctx, stampFile)). + Text("--ignore-duplicates"). + FlagWithInput("--file-list", android.PathForArbitraryOutput(ctx, fileListFile)). + Text(rootDir.String()). + Text(android.PathForArbitraryOutput(ctx, stagingDir).String()) +} + +func (f *filesystem) PartitionType() string { + return proptools.StringDefault(f.properties.Partition_type, "system") +} + var _ android.AndroidMkEntriesProvider = (*filesystem)(nil) // Implements android.AndroidMkEntriesProvider diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go index c44810517..6d62746ec 100644 --- a/filesystem/filesystem_test.go +++ b/filesystem/filesystem_test.go @@ -16,6 +16,7 @@ package filesystem import ( "os" + "path/filepath" "testing" "android/soong/android" @@ -93,6 +94,22 @@ func TestFileSystemDeps(t *testing.T) { } } +func TestIncludeMakeBuiltFiles(t *testing.T) { + result := fixture.RunTestWithBp(t, ` + android_filesystem { + name: "myfilesystem", + include_make_built_files: "system", + } + `) + + output := result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img") + + stampFile := filepath.Join(result.Config.OutDir(), "target/product/test_device/obj/PACKAGING/system_intermediates/staging_dir.stamp") + fileListFile := filepath.Join(result.Config.OutDir(), "target/product/test_device/obj/PACKAGING/system_intermediates/file_list.txt") + android.AssertStringListContains(t, "deps of filesystem must include the staging dir stamp file", output.Implicits.Strings(), stampFile) + android.AssertStringListContains(t, "deps of filesystem must include the staging dir file list", output.Implicits.Strings(), fileListFile) +} + func TestFileSystemFillsLinkerConfigWithStubLibs(t *testing.T) { result := fixture.RunTestWithBp(t, ` android_system_image { @@ -270,7 +287,7 @@ func TestFileSystemShouldInstallCoreVariantIfTargetBuildAppsIsSet(t *testing.T) } `) - inputs := result.ModuleForTests("myfilesystem", "android_common").Output("deps.zip").Implicits + inputs := result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img").Implicits android.AssertStringListContains(t, "filesystem should have libbar even for unbundled build", inputs.Strings(), "out/soong/.intermediates/libbar/android_arm64_armv8-a_shared/libbar.so") @@ -314,7 +331,7 @@ func TestFileSystemWithCoverageVariants(t *testing.T) { `) filesystem := result.ModuleForTests("myfilesystem", "android_common_cov") - inputs := filesystem.Output("deps.zip").Implicits + inputs := filesystem.Output("myfilesystem.img").Implicits android.AssertStringListContains(t, "filesystem should have libfoo(cov)", inputs.Strings(), "out/soong/.intermediates/libfoo/android_arm64_armv8-a_shared_cov/libfoo.so") diff --git a/filesystem/system_image.go b/filesystem/system_image.go index 34f4ffb6d..78ce3770c 100644 --- a/filesystem/system_image.go +++ b/filesystem/system_image.go @@ -43,6 +43,9 @@ func systemImageFactory() android.Module { } func (s *systemImage) buildExtraFiles(ctx android.ModuleContext, root android.OutputPath) android.OutputPaths { + if s.filesystem.properties.Partition_type != nil { + ctx.PropertyErrorf("partition_type", "partition_type must be unset on an android_system_image module. It is assumed to be 'system'.") + } lc := s.buildLinkerConfigFile(ctx, root) // Add more files if needed return []android.OutputPath{lc} diff --git a/genrule/allowlists.go b/genrule/allowlists.go index 60b1366d0..7c71b77ef 100644 --- a/genrule/allowlists.go +++ b/genrule/allowlists.go @@ -21,9 +21,4 @@ var ( "com.google.pixel.camera.hal.manifest", // go/keep-sorted end } - - SandboxingDenyPathList = []string{ - // go/keep-sorted start - // go/keep-sorted end - } ) diff --git a/genrule/genrule.go b/genrule/genrule.go index 6f6608817..cf2a96673 100644 --- a/genrule/genrule.go +++ b/genrule/genrule.go @@ -808,7 +808,7 @@ func GenRuleFactory() android.Module { type genRuleProperties struct { // names of the output files that will be generated - Out []string + Out []string `android:"arch_variant"` } var Bool = proptools.Bool @@ -842,19 +842,15 @@ var sandboxingAllowlistKey = android.NewOnceKey("genruleSandboxingAllowlistKey") type sandboxingAllowlistSets struct { sandboxingDenyModuleSet map[string]bool - sandboxingDenyPathSet map[string]bool } func getSandboxingAllowlistSets(ctx android.PathContext) *sandboxingAllowlistSets { return ctx.Config().Once(sandboxingAllowlistKey, func() interface{} { sandboxingDenyModuleSet := map[string]bool{} - sandboxingDenyPathSet := map[string]bool{} android.AddToStringSet(sandboxingDenyModuleSet, SandboxingDenyModuleList) - android.AddToStringSet(sandboxingDenyPathSet, SandboxingDenyPathList) return &sandboxingAllowlistSets{ sandboxingDenyModuleSet: sandboxingDenyModuleSet, - sandboxingDenyPathSet: sandboxingDenyPathSet, } }).(*sandboxingAllowlistSets) } @@ -864,8 +860,7 @@ func getSandboxedRuleBuilder(ctx android.ModuleContext, r *android.RuleBuilder) return r.SandboxTools() } sandboxingAllowlistSets := getSandboxingAllowlistSets(ctx) - if sandboxingAllowlistSets.sandboxingDenyPathSet[ctx.ModuleDir()] || - sandboxingAllowlistSets.sandboxingDenyModuleSet[ctx.ModuleName()] { + if sandboxingAllowlistSets.sandboxingDenyModuleSet[ctx.ModuleName()] { return r.SandboxTools() } return r.SandboxInputs() diff --git a/java/aapt2.go b/java/aapt2.go index 445e91298..f704fc6fc 100644 --- a/java/aapt2.go +++ b/java/aapt2.go @@ -309,7 +309,8 @@ func aapt2ExtractExtraPackages(ctx android.ModuleContext, out android.WritablePa var aapt2ConvertRule = pctx.AndroidStaticRule("aapt2Convert", blueprint.RuleParams{ - Command: `${config.Aapt2Cmd} convert --output-format $format $in -o $out`, + Command: `${config.Aapt2Cmd} convert --enable-compact-entries ` + + `--output-format $format $in -o $out`, CommandDeps: []string{"${config.Aapt2Cmd}"}, }, "format", ) diff --git a/java/aar.go b/java/aar.go index 7cb362a9f..506187959 100644 --- a/java/aar.go +++ b/java/aar.go @@ -104,6 +104,9 @@ type aaptProperties struct { // Filter only specified product and ignore other products Filter_product *string `blueprint:"mutated"` + + // Names of aconfig_declarations modules that specify aconfig flags that the module depends on. + Flags_packages []string } type aapt struct { @@ -204,6 +207,8 @@ func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext android.SdkConte // Flags specified in Android.bp linkFlags = append(linkFlags, a.aaptProperties.Aaptflags...) + linkFlags = append(linkFlags, "--enable-compact-entries") + // Find implicit or explicit asset and resource dirs assets := android.PathsRelativeToModuleSourceDir(android.SourceInput{ Context: ctx, @@ -346,6 +351,7 @@ type aaptBuildActionOptions struct { classLoaderContexts dexpreopt.ClassLoaderContextMap excludedLibs []string enforceDefaultTargetSdkVersion bool + forceNonFinalResourceIDs bool extraLinkFlags []string aconfigTextFiles android.Paths } @@ -453,6 +459,11 @@ func (a *aapt) buildActions(ctx android.ModuleContext, opts aaptBuildActionOptio // as imports. The resources from dependencies will not be merged into this module's package-res.apk, and // instead modules depending on this module will reference package-res.apk from all transitive static // dependencies. + for _, sharedDep := range sharedDeps { + if sharedDep.usedResourceProcessor { + transitiveRJars = append(transitiveRJars, sharedDep.rJar) + } + } for _, staticDep := range staticDeps { linkDeps = append(linkDeps, staticDep.resPackage) linkFlags = append(linkFlags, "-I "+staticDep.resPackage.String()) @@ -460,11 +471,6 @@ func (a *aapt) buildActions(ctx android.ModuleContext, opts aaptBuildActionOptio transitiveRJars = append(transitiveRJars, staticDep.rJar) } } - for _, sharedDep := range sharedDeps { - if sharedDep.usedResourceProcessor { - transitiveRJars = append(transitiveRJars, sharedDep.rJar) - } - } } else { // When building an app or building a library without ResourceProcessorBusyBox enabled all static // dependencies are compiled into this module's package-res.apk as overlays. @@ -539,7 +545,8 @@ func (a *aapt) buildActions(ctx android.ModuleContext, opts aaptBuildActionOptio if a.useResourceProcessorBusyBox(ctx) { rJar := android.PathForModuleOut(ctx, "busybox/R.jar") - resourceProcessorBusyBoxGenerateBinaryR(ctx, rTxt, a.mergedManifestFile, rJar, staticDeps, a.isLibrary, a.aaptProperties.Aaptflags) + resourceProcessorBusyBoxGenerateBinaryR(ctx, rTxt, a.mergedManifestFile, rJar, staticDeps, a.isLibrary, a.aaptProperties.Aaptflags, + opts.forceNonFinalResourceIDs) aapt2ExtractExtraPackages(ctx, extraPackages, rJar) transitiveRJars = append(transitiveRJars, rJar) a.rJar = rJar @@ -554,6 +561,10 @@ func (a *aapt) buildActions(ctx android.ModuleContext, opts aaptBuildActionOptio transitiveAaptResourcePackagesFile := android.PathForModuleOut(ctx, "transitive-res-packages") android.WriteFileRule(ctx, transitiveAaptResourcePackagesFile, strings.Join(transitiveAaptResourcePackages, "\n")) + // Reverse the list of R.jar files so that the current module comes first, and direct dependencies come before + // transitive dependencies. + transitiveRJars = android.ReversePaths(transitiveRJars) + a.aaptSrcJar = srcJar a.transitiveAaptRJars = transitiveRJars a.transitiveAaptResourcePackagesFile = transitiveAaptResourcePackagesFile @@ -599,7 +610,8 @@ var resourceProcessorBusyBox = pctx.AndroidStaticRule("resourceProcessorBusyBox" // using Bazel's ResourceProcessorBusyBox tool, which is faster than compiling the R.java files and // supports producing classes for static dependencies that only include resources from that dependency. func resourceProcessorBusyBoxGenerateBinaryR(ctx android.ModuleContext, rTxt, manifest android.Path, - rJar android.WritablePath, transitiveDeps transitiveAarDeps, isLibrary bool, aaptFlags []string) { + rJar android.WritablePath, transitiveDeps transitiveAarDeps, isLibrary bool, aaptFlags []string, + forceNonFinalIds bool) { var args []string var deps android.Paths @@ -609,6 +621,9 @@ func resourceProcessorBusyBoxGenerateBinaryR(ctx android.ModuleContext, rTxt, ma // to ResourceProcessorBusyBox so that it can regenerate R.class files with the final resource IDs for each // package. args, deps = transitiveDeps.resourceProcessorDeps() + if forceNonFinalIds { + args = append(args, "--finalFields=false") + } } else { // When compiling a library don't pass any dependencies as it only needs to generate an R.class file for this // library. Pass --finalFields=false so that the R.class file contains non-final fields so they don't get @@ -798,6 +813,10 @@ func (a *AndroidLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { a.aapt.deps(ctx, sdkDep) } a.usesLibrary.deps(ctx, false) + + for _, aconfig_declaration := range a.aaptProperties.Flags_packages { + ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration) + } } func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -811,13 +830,14 @@ func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) sdkContext: android.SdkContext(a), classLoaderContexts: a.classLoaderContexts, enforceDefaultTargetSdkVersion: false, + aconfigTextFiles: getAconfigFilePaths(ctx), }, ) apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) a.hideApexVariantFromMake = !apexInfo.IsForPlatform() - a.stem = proptools.StringDefault(a.overridableDeviceProperties.Stem, ctx.ModuleName()) + a.stem = proptools.StringDefault(a.overridableProperties.Stem, ctx.ModuleName()) ctx.CheckbuildFile(a.aapt.proguardOptionsFile) ctx.CheckbuildFile(a.aapt.exportPackage) @@ -1207,7 +1227,7 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { linkFlags, linkDeps, nil, overlayRes, transitiveAssets, nil, nil) a.rJar = android.PathForModuleOut(ctx, "busybox/R.jar") - resourceProcessorBusyBoxGenerateBinaryR(ctx, a.rTxt, a.manifest, a.rJar, nil, true, nil) + resourceProcessorBusyBoxGenerateBinaryR(ctx, a.rTxt, a.manifest, a.rJar, nil, true, nil, false) aapt2ExtractExtraPackages(ctx, a.extraAaptPackagesFile, a.rJar) diff --git a/java/aar_test.go b/java/aar_test.go index 4d4e5d025..6bd53f228 100644 --- a/java/aar_test.go +++ b/java/aar_test.go @@ -81,3 +81,50 @@ func TestAarImportProducesJniPackages(t *testing.T) { }) } } + +func TestLibraryFlagsPackages(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + ).RunTestWithBp(t, ` + android_library { + name: "foo", + srcs: ["a.java"], + sdk_version: "current", + flags_packages: [ + "bar", + "baz", + ], + } + aconfig_declarations { + name: "bar", + package: "com.example.package.bar", + srcs: [ + "bar.aconfig", + ], + } + aconfig_declarations { + name: "baz", + package: "com.example.package.baz", + srcs: [ + "baz.aconfig", + ], + } + `) + + foo := result.ModuleForTests("foo", "android_common") + + // android_library module depends on aconfig_declarations listed in flags_packages + android.AssertBoolEquals(t, "foo expected to depend on bar", true, + CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar")) + + android.AssertBoolEquals(t, "foo expected to depend on baz", true, + CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "baz")) + + aapt2LinkRule := foo.Rule("android/soong/java.aapt2Link") + linkInFlags := aapt2LinkRule.Args["inFlags"] + android.AssertStringDoesContain(t, + "aapt2 link command expected to pass feature flags arguments", + linkInFlags, + "--feature-flags @out/soong/.intermediates/bar/intermediate.txt --feature-flags @out/soong/.intermediates/baz/intermediate.txt", + ) +} diff --git a/java/app.go b/java/app.go index 9736ffd23..2abc45107 100755 --- a/java/app.go +++ b/java/app.go @@ -169,9 +169,6 @@ type overridableAppProperties struct { // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed // from PRODUCT_PACKAGES. Overrides []string - - // Names of aconfig_declarations modules that specify aconfig flags that the app depends on. - Flags_packages []string } type AndroidApp struct { @@ -290,6 +287,10 @@ func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) { } a.usesLibrary.deps(ctx, sdkDep.hasFrameworkLibs()) + + for _, aconfig_declaration := range a.aaptProperties.Flags_packages { + ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration) + } } func (a *AndroidApp) OverridablePropertiesDepsMutator(ctx android.BottomUpMutatorContext) { @@ -317,10 +318,6 @@ func (a *AndroidApp) OverridablePropertiesDepsMutator(ctx android.BottomUpMutato `must be names of android_app_certificate modules in the form ":module"`) } } - - for _, aconfig_declaration := range a.overridableAppProperties.Flags_packages { - ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration) - } } func (a *AndroidTestHelperApp) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -457,6 +454,21 @@ func (a *AndroidApp) renameResourcesPackage() bool { return proptools.BoolDefault(a.overridableAppProperties.Rename_resources_package, true) } +func getAconfigFilePaths(ctx android.ModuleContext) (aconfigTextFilePaths android.Paths) { + ctx.VisitDirectDepsWithTag(aconfigDeclarationTag, func(dep android.Module) { + if provider, ok := android.OtherModuleProvider(ctx, dep, android.AconfigDeclarationsProviderKey); ok { + aconfigTextFilePaths = append(aconfigTextFilePaths, provider.IntermediateDumpOutputPath) + } else { + ctx.ModuleErrorf("Only aconfig_declarations module type is allowed for "+ + "flags_packages property, but %s is not aconfig_declarations module type", + dep.Name(), + ) + } + }) + + return aconfigTextFilePaths +} + func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) { usePlatformAPI := proptools.Bool(a.Module.deviceProperties.Platform_apis) if ctx.Module().(android.SdkContext).SdkVersion(ctx).Kind == android.SdkModule { @@ -507,26 +519,17 @@ func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) { a.aapt.defaultManifestVersion = android.DefaultUpdatableModuleVersion } - var aconfigTextFilePaths android.Paths - ctx.VisitDirectDepsWithTag(aconfigDeclarationTag, func(dep android.Module) { - if provider, ok := android.OtherModuleProvider(ctx, dep, android.AconfigDeclarationsProviderKey); ok { - aconfigTextFilePaths = append(aconfigTextFilePaths, provider.IntermediateDumpOutputPath) - } else { - ctx.ModuleErrorf("Only aconfig_declarations module type is allowed for "+ - "flags_packages property, but %s is not aconfig_declarations module type", - dep.Name(), - ) - } - }) - + // Use non final ids if we are doing optimized shrinking and are using R8. + nonFinalIds := Bool(a.dexProperties.Optimize.Optimized_shrink_resources) && a.dexer.effectiveOptimizeEnabled() a.aapt.buildActions(ctx, aaptBuildActionOptions{ sdkContext: android.SdkContext(a), classLoaderContexts: a.classLoaderContexts, excludedLibs: a.usesLibraryProperties.Exclude_uses_libs, enforceDefaultTargetSdkVersion: a.enforceDefaultTargetSdkVersion(), + forceNonFinalResourceIDs: nonFinalIds, extraLinkFlags: aaptLinkFlags, - aconfigTextFiles: aconfigTextFilePaths, + aconfigTextFiles: getAconfigFilePaths(ctx), }, ) @@ -547,7 +550,13 @@ func (a *AndroidApp) proguardBuildActions(ctx android.ModuleContext) { staticLibProguardFlagFiles = android.FirstUniquePaths(staticLibProguardFlagFiles) a.Module.extraProguardFlagsFiles = append(a.Module.extraProguardFlagsFiles, staticLibProguardFlagFiles...) - a.Module.extraProguardFlagsFiles = append(a.Module.extraProguardFlagsFiles, a.proguardOptionsFile) + if !Bool(a.dexProperties.Optimize.Optimized_shrink_resources) { + // When using the optimized shrinking the R8 enqueuer will traverse the xml files that become + // live for code references and (transitively) mark these as live. + // In this case we explicitly don't wan't the aapt2 generated keep files (which would keep the now + // dead code alive) + a.Module.extraProguardFlagsFiles = append(a.Module.extraProguardFlagsFiles, a.proguardOptionsFile) + } } func (a *AndroidApp) installPath(ctx android.ModuleContext) android.InstallPath { @@ -580,7 +589,7 @@ func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) (android.Path, a var packageResources = a.exportPackage if ctx.ModuleName() != "framework-res" { - if Bool(a.dexProperties.Optimize.Shrink_resources) { + if a.dexProperties.resourceShrinkingEnabled() { protoFile := android.PathForModuleOut(ctx, packageResources.Base()+".proto.apk") aapt2Convert(ctx, protoFile, packageResources, "proto") a.dexer.resourcesInput = android.OptionalPathForPath(protoFile) @@ -603,7 +612,7 @@ func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) (android.Path, a } a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars) - if Bool(a.dexProperties.Optimize.Shrink_resources) { + if a.dexProperties.resourceShrinkingEnabled() { binaryResources := android.PathForModuleOut(ctx, packageResources.Base()+".binary.out.apk") aapt2Convert(ctx, binaryResources, a.dexer.resourcesOutput.Path(), "binary") packageResources = binaryResources @@ -755,7 +764,7 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { // Unlike installApkName, a.stem should respect base module name for override_android_app. // Therefore, use ctx.ModuleName() instead of a.Name(). - a.stem = proptools.StringDefault(a.overridableDeviceProperties.Stem, ctx.ModuleName()) + a.stem = proptools.StringDefault(a.overridableProperties.Stem, ctx.ModuleName()) // Check if the install APK name needs to be overridden. // Both android_app and override_android_app module are expected to possess @@ -763,7 +772,7 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { // from the base module. Therefore, use a.Name() which represents // the module name for both android_app and override_android_app. a.installApkName = ctx.DeviceConfig().OverridePackageNameFor( - proptools.StringDefault(a.overridableDeviceProperties.Stem, a.Name())) + proptools.StringDefault(a.overridableProperties.Stem, a.Name())) if ctx.ModuleName() == "framework-res" { // framework-res.apk is installed as system/framework/framework-res.apk @@ -912,6 +921,13 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { } a.buildAppDependencyInfo(ctx) + + providePrebuiltInfo(ctx, + prebuiltInfoProps{ + baseModuleName: a.BaseModuleName(), + isPrebuilt: false, + }, + ) } type appDepsInterface interface { @@ -1316,6 +1332,12 @@ func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.extraTestConfigs = android.PathsForModuleSrc(ctx, a.testProperties.Test_options.Extra_test_configs) a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data) android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{}) + android.SetProvider(ctx, tradefed.BaseTestProviderKey, tradefed.BaseTestProviderData{ + InstalledFiles: a.data, + OutputFile: a.OutputFile(), + TestConfig: a.testConfig, + HostRequiredModuleNames: a.HostRequiredModuleNames(), + }) } func (a *AndroidTest) FixTestConfig(ctx android.ModuleContext, testConfig android.Path) android.Path { @@ -1494,7 +1516,7 @@ func (i *OverrideAndroidApp) GenerateAndroidBuildActions(_ android.ModuleContext func OverrideAndroidAppModuleFactory() android.Module { m := &OverrideAndroidApp{} m.AddProperties( - &OverridableDeviceProperties{}, + &OverridableProperties{}, &overridableAppProperties{}, ) diff --git a/java/app_import.go b/java/app_import.go index dc84fc26d..7387e168c 100644 --- a/java/app_import.go +++ b/java/app_import.go @@ -150,6 +150,11 @@ type AndroidAppImportProperties struct { // If unspecified, follows the naming convention that the source module of // the prebuilt is Name() without "prebuilt_" prefix Source_module_name *string + + // Path to the .prebuilt_info file of the prebuilt app. + // In case of mainline modules, the .prebuilt_info file contains the build_id that was used + // to generate the prebuilt. + Prebuilt_info *string `android:"path"` } func (a *AndroidAppImport) IsInstallable() bool { @@ -413,6 +418,14 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext } android.CollectDependencyAconfigFiles(ctx, &a.mergedAconfigFiles) + providePrebuiltInfo(ctx, + prebuiltInfoProps{ + baseModuleName: a.BaseModuleName(), + isPrebuilt: true, + prebuiltInfo: a.properties.Prebuilt_info, + }, + ) + // TODO: androidmk converter jni libs } diff --git a/java/app_set.go b/java/app_set.go index d2d3b06ba..33d3adec2 100644 --- a/java/app_set.go +++ b/java/app_set.go @@ -48,6 +48,11 @@ type AndroidAppSetProperties struct { // Names of modules to be overridden. Listed modules can only be other apps // (in Make or Soong). Overrides []string + + // Path to the .prebuilt_info file of the prebuilt app. + // In case of mainline modules, the .prebuilt_info file contains the build_id that was used + // to generate the prebuilt. + Prebuilt_info *string `android:"path"` } type AndroidAppSet struct { @@ -117,6 +122,27 @@ func SupportedAbis(ctx android.ModuleContext, excludeNativeBridgeAbis bool) []st return result } +type prebuiltInfoProps struct { + baseModuleName string + isPrebuilt bool + prebuiltInfo *string +} + +// Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file +// with information about whether source or prebuilt of an apex was used during the build. +func providePrebuiltInfo(ctx android.ModuleContext, p prebuiltInfoProps) { + info := android.PrebuiltInfo{ + Name: p.baseModuleName, + Is_prebuilt: p.isPrebuilt, + } + // If Prebuilt_info information is available in the soong module definition, add it to prebuilt_info.json. + if p.prebuiltInfo != nil { + prebuiltInfoFile := android.PathForModuleSrc(ctx, *p.prebuiltInfo) + info.Prebuilt_info_file_path = prebuiltInfoFile.String() + } + android.SetProvider(ctx, android.PrebuiltInfoProvider, info) +} + func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) { as.packedOutput = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip") as.primaryOutput = android.PathForModuleOut(ctx, as.BaseModuleName()+".apk") @@ -157,6 +183,15 @@ func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) installDir = android.PathForModuleInstall(ctx, "app", as.BaseModuleName()) } ctx.InstallFileWithExtraFilesZip(installDir, as.BaseModuleName()+".apk", as.primaryOutput, as.packedOutput) + + providePrebuiltInfo(ctx, + prebuiltInfoProps{ + baseModuleName: as.BaseModuleName(), + isPrebuilt: true, + prebuiltInfo: as.properties.Prebuilt_info, + }, + ) + } func (as *AndroidAppSet) InstallBypassMake() bool { return true } diff --git a/java/app_test.go b/java/app_test.go index 28bea0a2b..b75cb1678 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -948,10 +948,10 @@ func TestAndroidResourceProcessor(t *testing.T) { directSrcJars: nil, directClasspath: []string{ "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar", - "out/soong/.intermediates/transitive_import/android_common/busybox/R.jar", - "out/soong/.intermediates/transitive_import_dep/android_common/busybox/R.jar", - "out/soong/.intermediates/transitive/android_common/busybox/R.jar", "out/soong/.intermediates/direct/android_common/busybox/R.jar", + "out/soong/.intermediates/transitive/android_common/busybox/R.jar", + "out/soong/.intermediates/transitive_import_dep/android_common/busybox/R.jar", + "out/soong/.intermediates/transitive_import/android_common/busybox/R.jar", "out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar", "out/soong/.intermediates/transitive_import/android_common/aar/classes-combined.jar", }, @@ -981,9 +981,9 @@ func TestAndroidResourceProcessor(t *testing.T) { sharedSrcJars: nil, sharedClasspath: []string{ "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar", + "out/soong/.intermediates/shared/android_common/busybox/R.jar", "out/soong/.intermediates/shared_transitive_static/android_common/busybox/R.jar", "out/soong/.intermediates/shared_transitive_shared/android_common/busybox/R.jar", - "out/soong/.intermediates/shared/android_common/busybox/R.jar", "out/soong/.intermediates/shared_transitive_shared/android_common/turbine-combined/shared_transitive_shared.jar", "out/soong/.intermediates/shared_transitive_static/android_common/turbine-combined/shared_transitive_static.jar", }, @@ -1094,9 +1094,9 @@ func TestAndroidResourceProcessor(t *testing.T) { directSrcJars: nil, directClasspath: []string{ "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar", - "out/soong/.intermediates/transitive_import/android_common/busybox/R.jar", - "out/soong/.intermediates/transitive_import_dep/android_common/busybox/R.jar", "out/soong/.intermediates/direct/android_common/busybox/R.jar", + "out/soong/.intermediates/transitive_import_dep/android_common/busybox/R.jar", + "out/soong/.intermediates/transitive_import/android_common/busybox/R.jar", "out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar", "out/soong/.intermediates/transitive_import/android_common/aar/classes-combined.jar", }, @@ -4401,3 +4401,20 @@ func TestNoDexpreoptOptionalUsesLibDoesNotHaveImpl(t *testing.T) { dexpreopt := result.ModuleForTests("app", "android_common").MaybeRule("dexpreopt").Rule android.AssertBoolEquals(t, "dexpreopt should be disabled if optional_uses_libs does not have an implementation", true, dexpreopt == nil) } + +func TestAppStem(t *testing.T) { + ctx := testApp(t, ` + android_app { + name: "foo", + srcs: ["a.java"], + stem: "foo-new", + sdk_version: "current", + }`) + + foo := ctx.ModuleForTests("foo", "android_common") + + outputs := fmt.Sprint(foo.AllOutputs()) + if !strings.Contains(outputs, "foo-new.apk") { + t.Errorf("Module output does not contain expected apk %s", "foo-new.apk") + } +} diff --git a/java/base.go b/java/base.go index f11e30dc2..be286fe33 100644 --- a/java/base.go +++ b/java/base.go @@ -26,7 +26,6 @@ import ( "github.com/google/blueprint/pathtools" "github.com/google/blueprint/proptools" - "android/soong/aconfig" "android/soong/android" "android/soong/dexpreopt" "android/soong/java/config" @@ -95,6 +94,9 @@ type CommonProperties struct { // if not blank, used as prefix to generate repackage rule Jarjar_prefix *string + // if set to true, skip the jarjar repackaging + Skip_jarjar_repackage *bool + // If not blank, set the java version passed to javac as -source and -target Java_version *string @@ -303,8 +305,8 @@ type DeviceProperties struct { HiddenAPIFlagFileProperties } -// Device properties that can be overridden by overriding module (e.g. override_android_app) -type OverridableDeviceProperties struct { +// Properties that can be overridden by overriding module (e.g. override_android_app) +type OverridableProperties struct { // set the name of the output. If not set, `name` is used. // To override a module with this property set, overriding module might need to set this as well. // Otherwise, both the overridden and the overriding modules will have the same output name, which @@ -432,7 +434,7 @@ type Module struct { protoProperties android.ProtoProperties deviceProperties DeviceProperties - overridableDeviceProperties OverridableDeviceProperties + overridableProperties OverridableProperties // jar file containing header classes including static library dependencies, suitable for // inserting into the bootclasspath/classpath of another compile @@ -614,6 +616,7 @@ func (j *Module) checkHeadersOnly(ctx android.ModuleContext) { func (j *Module) addHostProperties() { j.AddProperties( &j.properties, + &j.overridableProperties, &j.protoProperties, &j.usesLibraryProperties, ) @@ -623,7 +626,6 @@ func (j *Module) addHostAndDeviceProperties() { j.addHostProperties() j.AddProperties( &j.deviceProperties, - &j.overridableDeviceProperties, &j.dexer.dexProperties, &j.dexpreoptProperties, &j.linter.properties, @@ -1101,11 +1103,13 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath jarjarProviderData := j.collectJarJarRules(ctx) if jarjarProviderData != nil { android.SetProvider(ctx, JarJarProvider, *jarjarProviderData) - text := getJarJarRuleText(jarjarProviderData) - if text != "" { - ruleTextFile := android.PathForModuleOut(ctx, "repackaged-jarjar", "repackaging.txt") - android.WriteFileRule(ctx, ruleTextFile, text) - j.repackageJarjarRules = ruleTextFile + if !proptools.Bool(j.properties.Skip_jarjar_repackage) { + text := getJarJarRuleText(jarjarProviderData) + if text != "" { + ruleTextFile := android.PathForModuleOut(ctx, "repackaged-jarjar", "repackaging.txt") + android.WriteFileRule(ctx, ruleTextFile, text) + j.repackageJarjarRules = ruleTextFile + } } } @@ -1215,14 +1219,15 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath } android.SetProvider(ctx, JavaInfoProvider, JavaInfo{ - HeaderJars: android.PathsIfNonNil(j.headerJarFile), - TransitiveLibsHeaderJars: j.transitiveLibsHeaderJars, - TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars, - AidlIncludeDirs: j.exportAidlIncludeDirs, - ExportedPlugins: j.exportedPluginJars, - ExportedPluginClasses: j.exportedPluginClasses, - ExportedPluginDisableTurbine: j.exportedDisableTurbine, - StubsLinkType: j.stubsLinkType, + HeaderJars: android.PathsIfNonNil(j.headerJarFile), + TransitiveLibsHeaderJars: j.transitiveLibsHeaderJars, + TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars, + AidlIncludeDirs: j.exportAidlIncludeDirs, + ExportedPlugins: j.exportedPluginJars, + ExportedPluginClasses: j.exportedPluginClasses, + ExportedPluginDisableTurbine: j.exportedDisableTurbine, + StubsLinkType: j.stubsLinkType, + AconfigIntermediateCacheOutputPaths: deps.aconfigProtoFiles, }) j.outputFile = j.headerJarFile @@ -1725,22 +1730,23 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath android.CollectDependencyAconfigFiles(ctx, &j.mergedAconfigFiles) android.SetProvider(ctx, JavaInfoProvider, JavaInfo{ - HeaderJars: android.PathsIfNonNil(j.headerJarFile), - RepackagedHeaderJars: android.PathsIfNonNil(j.repackagedHeaderJarFile), - TransitiveLibsHeaderJars: j.transitiveLibsHeaderJars, - TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars, - ImplementationAndResourcesJars: android.PathsIfNonNil(j.implementationAndResourcesJar), - ImplementationJars: android.PathsIfNonNil(j.implementationJarFile), - ResourceJars: android.PathsIfNonNil(j.resourceJar), - AidlIncludeDirs: j.exportAidlIncludeDirs, - SrcJarArgs: j.srcJarArgs, - SrcJarDeps: j.srcJarDeps, - TransitiveSrcFiles: j.transitiveSrcFiles, - ExportedPlugins: j.exportedPluginJars, - ExportedPluginClasses: j.exportedPluginClasses, - ExportedPluginDisableTurbine: j.exportedDisableTurbine, - JacocoReportClassesFile: j.jacocoReportClassesFile, - StubsLinkType: j.stubsLinkType, + HeaderJars: android.PathsIfNonNil(j.headerJarFile), + RepackagedHeaderJars: android.PathsIfNonNil(j.repackagedHeaderJarFile), + TransitiveLibsHeaderJars: j.transitiveLibsHeaderJars, + TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars, + ImplementationAndResourcesJars: android.PathsIfNonNil(j.implementationAndResourcesJar), + ImplementationJars: android.PathsIfNonNil(j.implementationJarFile), + ResourceJars: android.PathsIfNonNil(j.resourceJar), + AidlIncludeDirs: j.exportAidlIncludeDirs, + SrcJarArgs: j.srcJarArgs, + SrcJarDeps: j.srcJarDeps, + TransitiveSrcFiles: j.transitiveSrcFiles, + ExportedPlugins: j.exportedPluginJars, + ExportedPluginClasses: j.exportedPluginClasses, + ExportedPluginDisableTurbine: j.exportedDisableTurbine, + JacocoReportClassesFile: j.jacocoReportClassesFile, + StubsLinkType: j.stubsLinkType, + AconfigIntermediateCacheOutputPaths: deps.aconfigProtoFiles, }) // Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource @@ -2291,6 +2297,7 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { // annotation processor that generates API is incompatible with the turbine // optimization. deps.disableTurbine = deps.disableTurbine || dep.ExportedPluginDisableTurbine + deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.AconfigIntermediateCacheOutputPaths...) case pluginTag: if plugin, ok := module.(*Plugin); ok { if plugin.pluginProperties.Processor_class != nil { @@ -2349,6 +2356,8 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { deps.staticJars = append(deps.staticJars, dep.Srcs()...) deps.staticHeaderJars = append(deps.staticHeaderJars, dep.Srcs()...) } + } else if dep, ok := android.OtherModuleProvider(ctx, module, android.CodegenInfoProvider); ok { + deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPaths...) } else { switch tag { case bootClasspathTag: @@ -2546,7 +2555,7 @@ func collectDirectDepsProviders(ctx android.ModuleContext) (result *JarJarProvid default: return RenameUseExclude, "srcfile" } - } else if _, ok := android.OtherModuleProvider(ctx, m, aconfig.CodegenInfoProvider); ok { + } else if _, ok := android.OtherModuleProvider(ctx, m, android.CodegenInfoProvider); ok { return RenameUseInclude, "aconfig_declarations_group" } else { switch tag { diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index 7c45d3043..cc3da7656 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -77,7 +77,7 @@ func (b bootclasspathFragmentContentDependencyTag) SdkMemberType(child android.M return javaSdkLibrarySdkMemberType } - return javaBootLibsSdkMemberType + return JavaBootLibsSdkMemberType } func (b bootclasspathFragmentContentDependencyTag) ExportMember() bool { diff --git a/java/builder.go b/java/builder.go index b07a622e4..5d84d0b43 100644 --- a/java/builder.go +++ b/java/builder.go @@ -120,6 +120,8 @@ var ( `--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED ` + `--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED ` + `--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED ` + + `--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED ` + + `--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED ` + `-jar ${config.JavaKytheExtractorJar} ` + `${config.JavacHeapFlags} ${config.CommonJdkFlags} ` + `$processorpath $processor $javacFlags $bootClasspath $classpath ` + diff --git a/java/config/config.go b/java/config/config.go index 6a945ac9c..d720046a1 100644 --- a/java/config/config.go +++ b/java/config/config.go @@ -131,12 +131,7 @@ func init() { if override := ctx.Config().Getenv("OVERRIDE_JLINK_VERSION_NUMBER"); override != "" { return override } - switch ctx.Config().Getenv("EXPERIMENTAL_USE_OPENJDK21_TOOLCHAIN") { - case "true": - return "21" - default: - return "17" - } + return "21" }) pctx.SourcePathVariable("JavaToolchain", "${JavaHome}/bin") diff --git a/java/config/droidstubs.go b/java/config/droidstubs.go index f46c893fd..39eec444c 100644 --- a/java/config/droidstubs.go +++ b/java/config/droidstubs.go @@ -23,7 +23,6 @@ var ( "--format=v2", "--repeat-errors-max 10", "--hide UnresolvedImport", - "--hide InvalidNullabilityOverride", // Force metalava to ignore classes on the classpath when an API file contains missing classes. // See b/285140653 for more information. @@ -49,9 +48,6 @@ var ( // TODO(tnorbye): find owners to fix these warnings when annotation was enabled. "--hide HiddenTypedefConstant", "--hide SuperfluousPrefix", - "--hide AnnotationExtraction", - // b/222738070 - "--hide BannedThrow", } MetalavaAnnotationsWarningsFlags = strings.Join(metalavaAnnotationsWarningsFlags, " ") diff --git a/java/dex.go b/java/dex.go index 4474c636a..6caaa7f48 100644 --- a/java/dex.go +++ b/java/dex.go @@ -66,6 +66,12 @@ type DexProperties struct { // If true, optimize for size by removing unused resources. Defaults to false. Shrink_resources *bool + // If true, use optimized resource shrinking in R8, overriding the + // Shrink_resources setting. Defaults to false. + // Optimized shrinking means that R8 will trace and treeshake resources together with code + // and apply additional optimizations. This implies non final fields in the R classes. + Optimized_shrink_resources *bool + // Flags to pass to proguard. Proguard_flags []string @@ -105,6 +111,10 @@ func (d *dexer) effectiveOptimizeEnabled() bool { return BoolDefault(d.dexProperties.Optimize.Enabled, d.dexProperties.Optimize.EnabledByDefault) } +func (d *DexProperties) resourceShrinkingEnabled() bool { + return BoolDefault(d.Optimize.Optimized_shrink_resources, Bool(d.Optimize.Shrink_resources)) +} + var d8, d8RE = pctx.MultiCommandRemoteStaticRules("d8", blueprint.RuleParams{ Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` + @@ -351,10 +361,14 @@ func (d *dexer) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Fl r8Flags = append(r8Flags, "-ignorewarnings") } + // resourcesInput is empty when we don't use resource shrinking, if on, pass these to R8 if d.resourcesInput.Valid() { r8Flags = append(r8Flags, "--resource-input", d.resourcesInput.Path().String()) r8Deps = append(r8Deps, d.resourcesInput.Path()) r8Flags = append(r8Flags, "--resource-output", d.resourcesOutput.Path().String()) + if Bool(opt.Optimized_shrink_resources) { + r8Flags = append(r8Flags, "--optimized-resource-shrinking") + } } return r8Flags, r8Deps diff --git a/java/droiddoc.go b/java/droiddoc.go index df40d016c..aec40b312 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -21,7 +21,6 @@ import ( "github.com/google/blueprint/proptools" - "android/soong/aconfig" "android/soong/android" "android/soong/java/config" ) @@ -222,8 +221,6 @@ type Javadoc struct { stubsSrcJar android.WritablePath exportableStubsSrcJar android.WritablePath - - runtimeStubsSrcJar android.WritablePath } func (j *Javadoc) OutputFiles(tag string) (android.Paths, error) { @@ -416,7 +413,7 @@ func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps { case aconfigDeclarationTag: if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey); ok { deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPath) - } else if dep, ok := android.OtherModuleProvider(ctx, module, aconfig.CodegenInfoProvider); ok { + } else if dep, ok := android.OtherModuleProvider(ctx, module, android.CodegenInfoProvider); ok { deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPaths...) } else { ctx.ModuleErrorf("Only aconfig_declarations and aconfig_declarations_group "+ diff --git a/java/droidstubs.go b/java/droidstubs.go index f4bcaca15..9556e956a 100644 --- a/java/droidstubs.go +++ b/java/droidstubs.go @@ -227,7 +227,6 @@ type currentApiTimestampProvider interface { type annotationFlagsParams struct { migratingNullability bool validatingNullability bool - extractAnnotations bool nullabilityWarningsFile android.WritablePath annotationsZip android.WritablePath } @@ -243,19 +242,16 @@ type stubsCommandParams struct { stubConfig stubsCommandConfigParams } type stubsCommandConfigParams struct { - stubsType StubsType - javaVersion javaVersion - deps deps - checkApi bool - generateStubs bool - doApiLint bool - doCheckReleased bool - writeSdkValues bool - migratingNullability bool - validatingNullability bool - annotationsEnabled bool - apiLevelsAnnotationsEnabled bool - extractAnnotations bool + stubsType StubsType + javaVersion javaVersion + deps deps + checkApi bool + generateStubs bool + doApiLint bool + doCheckReleased bool + writeSdkValues bool + migratingNullability bool + validatingNullability bool } // droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be @@ -372,7 +368,7 @@ func (d *Droidstubs) ApiFilePath(stubsType StubsType) (ret android.Path, err err ret, err = nil, fmt.Errorf("api file path not supported for the stub type %s", stubsType.String()) } if ret == nil && err == nil { - err = fmt.Errorf("stubs srcjar is null for the stub type %s", stubsType.String()) + err = fmt.Errorf("api file is null for the stub type %s", stubsType.String()) } return ret, err } @@ -482,34 +478,41 @@ func (d *Droidstubs) sdkValuesFlags(ctx android.ModuleContext, cmd *android.Rule } func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath, stubsType StubsType, checkApi bool) { - if checkApi || String(d.properties.Api_filename) != "" { - filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt") - uncheckedApiFile := android.PathForModuleOut(ctx, stubsType.String(), filename) - cmd.FlagWithOutput("--api ", uncheckedApiFile) + apiFileName := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt") + uncheckedApiFile := android.PathForModuleOut(ctx, stubsType.String(), apiFileName) + cmd.FlagWithOutput("--api ", uncheckedApiFile) + if checkApi || String(d.properties.Api_filename) != "" { if stubsType == Everything { d.apiFile = uncheckedApiFile } else if stubsType == Exportable { d.exportableApiFile = uncheckedApiFile } } else if sourceApiFile := proptools.String(d.properties.Check_api.Current.Api_file); sourceApiFile != "" { - // If check api is disabled then make the source file available for export. - d.apiFile = android.PathForModuleSrc(ctx, sourceApiFile) + if stubsType == Everything { + // If check api is disabled then make the source file available for export. + d.apiFile = android.PathForModuleSrc(ctx, sourceApiFile) + } else if stubsType == Exportable { + d.exportableApiFile = uncheckedApiFile + } } + removedApiFileName := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt") + uncheckedRemovedFile := android.PathForModuleOut(ctx, stubsType.String(), removedApiFileName) + cmd.FlagWithOutput("--removed-api ", uncheckedRemovedFile) if checkApi || String(d.properties.Removed_api_filename) != "" { - filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt") - uncheckedRemovedFile := android.PathForModuleOut(ctx, stubsType.String(), filename) - cmd.FlagWithOutput("--removed-api ", uncheckedRemovedFile) - if stubsType == Everything { d.removedApiFile = uncheckedRemovedFile } else if stubsType == Exportable { d.exportableRemovedApiFile = uncheckedRemovedFile } } else if sourceRemovedApiFile := proptools.String(d.properties.Check_api.Current.Removed_api_file); sourceRemovedApiFile != "" { - // If check api is disabled then make the source removed api file available for export. - d.removedApiFile = android.PathForModuleSrc(ctx, sourceRemovedApiFile) + if stubsType == Everything { + // If check api is disabled then make the source removed api file available for export. + d.removedApiFile = android.PathForModuleSrc(ctx, sourceRemovedApiFile) + } else if stubsType == Exportable { + d.exportableRemovedApiFile = uncheckedRemovedFile + } } if stubsDir.Valid() { @@ -525,30 +528,30 @@ func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuil } func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, params annotationFlagsParams) { - cmd.Flag(config.MetalavaAnnotationsFlags) + if Bool(d.properties.Annotations_enabled) { + cmd.Flag(config.MetalavaAnnotationsFlags) - if params.migratingNullability { - previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api)) - cmd.FlagWithInput("--migrate-nullness ", previousApi) - } + if params.migratingNullability { + previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api)) + cmd.FlagWithInput("--migrate-nullness ", previousApi) + } - if s := String(d.properties.Validate_nullability_from_list); s != "" { - cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s)) - } + if s := String(d.properties.Validate_nullability_from_list); s != "" { + cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s)) + } - if params.validatingNullability { - cmd.FlagWithOutput("--nullability-warnings-txt ", params.nullabilityWarningsFile) - } + if params.validatingNullability { + cmd.FlagWithOutput("--nullability-warnings-txt ", params.nullabilityWarningsFile) + } - if params.extractAnnotations { cmd.FlagWithOutput("--extract-annotations ", params.annotationsZip) - } - if len(d.properties.Merge_annotations_dirs) != 0 { - d.mergeAnnoDirFlags(ctx, cmd) - } + if len(d.properties.Merge_annotations_dirs) != 0 { + d.mergeAnnoDirFlags(ctx, cmd) + } - cmd.Flag(config.MetalavaAnnotationsWarningsFlags) + cmd.Flag(config.MetalavaAnnotationsWarningsFlags) + } } func (d *Droidstubs) mergeAnnoDirFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) { @@ -573,11 +576,9 @@ func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *a }) } -func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, params stubsCommandParams) { +func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, apiVersionsXml android.WritablePath) { var apiVersions android.Path - stubsType := params.stubConfig.stubsType - apiVersionsXml := params.apiVersionsXml - if params.stubConfig.apiLevelsAnnotationsEnabled { + if proptools.Bool(d.properties.Api_levels_annotations_enabled) { d.apiLevelsGenerationFlags(ctx, cmd, stubsType, apiVersionsXml) apiVersions = apiVersionsXml } else { @@ -588,9 +589,7 @@ func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *a } else if stubsType == Exportable { apiVersions = s.exportableArtifacts.apiVersionsXml } else { - // if the stubs type does not generate api-versions.xml file, default to using the - // everything artifacts - apiVersions = s.everythingArtifacts.apiVersionsXml + ctx.ModuleErrorf("%s stubs type does not generate api-versions.xml file", stubsType.String()) } } else { ctx.PropertyErrorf("api_levels_module", @@ -824,16 +823,13 @@ func (d *Droidstubs) commonMetalavaStubCmd(ctx android.ModuleContext, rule *andr annotationParams := annotationFlagsParams{ migratingNullability: params.stubConfig.migratingNullability, validatingNullability: params.stubConfig.validatingNullability, - extractAnnotations: params.stubConfig.extractAnnotations, nullabilityWarningsFile: params.nullabilityWarningsFile, annotationsZip: params.annotationsZip, } - if params.stubConfig.annotationsEnabled { - d.annotationsFlags(ctx, cmd, annotationParams) - } + d.annotationsFlags(ctx, cmd, annotationParams) d.inclusionAnnotationsFlags(ctx, cmd) - d.apiLevelsAnnotationsFlags(ctx, cmd, params) + d.apiLevelsAnnotationsFlags(ctx, cmd, params.stubConfig.stubsType, params.apiVersionsXml) d.expandArgs(ctx, cmd) @@ -863,13 +859,13 @@ func (d *Droidstubs) everythingStubCmd(ctx android.ModuleContext, params stubsCo d.everythingArtifacts.metadataZip = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"-metadata.zip") } - if params.annotationsEnabled { + if Bool(d.properties.Annotations_enabled) { if params.validatingNullability { d.everythingArtifacts.nullabilityWarningsFile = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"_nullability_warnings.txt") } d.everythingArtifacts.annotationsZip = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"_annotations.zip") } - if params.apiLevelsAnnotationsEnabled { + if Bool(d.properties.Api_levels_annotations_enabled) { d.everythingArtifacts.apiVersionsXml = android.PathForModuleOut(ctx, Everything.String(), "api-versions.xml") } @@ -1047,7 +1043,7 @@ func (d *Droidstubs) exportableStubCmd(ctx android.ModuleContext, params stubsCo optionalCmdParams.metadataDir = d.exportableArtifacts.metadataDir } - if params.annotationsEnabled { + if Bool(d.properties.Annotations_enabled) { if params.validatingNullability { d.exportableArtifacts.nullabilityWarningsFile = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"_nullability_warnings.txt") optionalCmdParams.nullabilityWarningsFile = d.exportableArtifacts.nullabilityWarningsFile @@ -1055,7 +1051,7 @@ func (d *Droidstubs) exportableStubCmd(ctx android.ModuleContext, params stubsCo d.exportableArtifacts.annotationsZip = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"_annotations.zip") optionalCmdParams.annotationsZip = d.exportableArtifacts.annotationsZip } - if params.apiLevelsAnnotationsEnabled { + if Bool(d.properties.Api_levels_annotations_enabled) { d.exportableArtifacts.apiVersionsXml = android.PathForModuleOut(ctx, params.stubsType.String(), "api-versions.xml") optionalCmdParams.apiVersionsXml = d.exportableArtifacts.apiVersionsXml } @@ -1073,38 +1069,6 @@ func (d *Droidstubs) exportableStubCmd(ctx android.ModuleContext, params stubsCo d.optionalStubCmd(ctx, optionalCmdParams) } -// Sandbox rule for generating runtime stubs -func (d *Droidstubs) runtimeStubCmd(ctx android.ModuleContext, params stubsCommandConfigParams) { - - // We are only interested in generating the stubs srcjar, - // not other artifacts for the runtime stubs - params.checkApi = false - params.writeSdkValues = false - params.validatingNullability = false - params.extractAnnotations = false - params.apiLevelsAnnotationsEnabled = false - - optionalCmdParams := stubsCommandParams{ - stubConfig: params, - } - - d.Javadoc.runtimeStubsSrcJar = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"-"+"stubs.srcjar") - optionalCmdParams.stubsSrcJar = d.Javadoc.runtimeStubsSrcJar - - // If aconfig_declarations property is not defined, all flagged apis symbols are stripped - // as no aconfig flags are enabled. In such case, the runtime stubs are identical to the - // exportable stubs, thus no additional metalava invocation is needed. - if len(d.properties.Aconfig_declarations) == 0 { - rule := android.NewRuleBuilder(pctx, ctx) - rule.Command(). - Text("cp").Flag("-f"). - Input(d.exportableStubsSrcJar).Output(d.runtimeStubsSrcJar) - rule.Build(fmt.Sprintf("metalava_%s", params.stubsType.String()), "metalava merged") - } else { - d.optionalStubCmd(ctx, optionalCmdParams) - } -} - func (d *Droidstubs) optionalStubCmd(ctx android.ModuleContext, params stubsCommandParams) { params.srcJarDir = android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "srcjars") @@ -1176,8 +1140,6 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { annotationsEnabled := Bool(d.properties.Annotations_enabled) - extractAnnotations := annotationsEnabled - migratingNullability := annotationsEnabled && String(d.properties.Previous_api) != "" validatingNullability := annotationsEnabled && (strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") || String(d.properties.Validate_nullability_from_list) != "") @@ -1185,40 +1147,27 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { checkApi := apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") || apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") - apiLevelsAnnotationsEnabled := proptools.Bool(d.properties.Api_levels_annotations_enabled) - stubCmdParams := stubsCommandConfigParams{ - javaVersion: javaVersion, - deps: deps, - checkApi: checkApi, - generateStubs: generateStubs, - doApiLint: doApiLint, - doCheckReleased: doCheckReleased, - writeSdkValues: writeSdkValues, - migratingNullability: migratingNullability, - validatingNullability: validatingNullability, - annotationsEnabled: annotationsEnabled, - apiLevelsAnnotationsEnabled: apiLevelsAnnotationsEnabled, - extractAnnotations: extractAnnotations, + javaVersion: javaVersion, + deps: deps, + checkApi: checkApi, + generateStubs: generateStubs, + doApiLint: doApiLint, + doCheckReleased: doCheckReleased, + writeSdkValues: writeSdkValues, + migratingNullability: migratingNullability, + validatingNullability: validatingNullability, } stubCmdParams.stubsType = Everything // Create default (i.e. "everything" stubs) rule for metalava d.everythingStubCmd(ctx, stubCmdParams) - // The module generates "exportable" stubs regardless of whether + // The module generates "exportable" (and "runtime" eventually) stubs regardless of whether // aconfig_declarations property is defined or not. If the property is not defined, the module simply // strips all flagged apis to generate the "exportable" stubs stubCmdParams.stubsType = Exportable d.exportableStubCmd(ctx, stubCmdParams) - // "runtime" stubs do not generate any other artifacts than the stubs. - // Therefore, metalava does not have to run for "runtime" configuration - // when the module does not generate stubs. - if stubCmdParams.generateStubs { - stubCmdParams.stubsType = Runtime - d.runtimeStubCmd(ctx, stubCmdParams) - } - if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") { if len(d.Javadoc.properties.Out) > 0 { diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go index c86e8bf0f..8da695f08 100644 --- a/java/droidstubs_test.go +++ b/java/droidstubs_test.go @@ -397,47 +397,23 @@ func TestAconfigDeclarations(t *testing.T) { "bar", ], } - droidstubs { - name: "baz", - srcs: ["a/A.java"], - api_surface: "public", - check_api: { - current: { - api_file: "a/current.txt", - removed_api_file: "a/removed.txt", - } - }, - } `) // Check that droidstubs depend on aconfig_declarations android.AssertBoolEquals(t, "foo expected to depend on bar", CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar"), true) - fooModule := result.ModuleForTests("foo", "android_common") + m := result.ModuleForTests("foo", "android_common") android.AssertStringDoesContain(t, "foo generates revert annotations file", - strings.Join(fooModule.AllOutputs(), ""), "revert-annotations-exportable.txt") + strings.Join(m.AllOutputs(), ""), "revert-annotations-exportable.txt") // revert-annotations.txt passed to exportable stubs generation metalava command - exportableManifest := fooModule.Output("metalava_exportable.sbox.textproto") - exportableCmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, exportableManifest).Commands[0].Command) - android.AssertStringDoesContain(t, "flagged api hide command not included", exportableCmdline, "revert-annotations-exportable.txt") + manifest := m.Output("metalava_exportable.sbox.textproto") + cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command) + android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "revert-annotations-exportable.txt") android.AssertStringDoesContain(t, "foo generates exportable stubs jar", - strings.Join(fooModule.AllOutputs(), ""), "exportable/foo-stubs.srcjar") - - // revert-annotations.txt passed to runtime stubs generation metalava command - runtimeManifest := fooModule.Output("metalava_runtime.sbox.textproto") - runtimeCmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, runtimeManifest).Commands[0].Command) - android.AssertStringDoesContain(t, "flagged api hide command not included", runtimeCmdline, "revert-annotations-runtime.txt") - - android.AssertStringDoesContain(t, "foo generates runtime stubs jar", - strings.Join(fooModule.AllOutputs(), ""), "runtime/foo-stubs.srcjar") - - // If aconfig_declarations property is not defined, the runtime stubs is a copy of the exportable stubs - bazModule := result.ModuleForTests("baz", "android_common") - bazRuntimeCmdline := bazModule.Rule("metalava_runtime").RuleParams.Command - android.AssertStringDoesContain(t, "copy command should include the input stub", bazRuntimeCmdline, "exportable/baz-stubs.srcjar") + strings.Join(m.AllOutputs(), ""), "exportable/foo-stubs.srcjar") } func TestReleaseExportRuntimeApis(t *testing.T) { diff --git a/java/java.go b/java/java.go index 794020dc5..97feb9bb2 100644 --- a/java/java.go +++ b/java/java.go @@ -24,7 +24,6 @@ import ( "sort" "strings" - "android/soong/aconfig" "android/soong/remoteexec" "android/soong/testing" @@ -83,8 +82,8 @@ func RegisterJavaSdkMemberTypes() { // Register sdk member types. android.RegisterSdkMemberType(javaHeaderLibsSdkMemberType) android.RegisterSdkMemberType(javaLibsSdkMemberType) - android.RegisterSdkMemberType(javaBootLibsSdkMemberType) - android.RegisterSdkMemberType(javaSystemserverLibsSdkMemberType) + android.RegisterSdkMemberType(JavaBootLibsSdkMemberType) + android.RegisterSdkMemberType(JavaSystemserverLibsSdkMemberType) android.RegisterSdkMemberType(javaTestSdkMemberType) } @@ -155,7 +154,7 @@ var ( // either java_libs, or java_header_libs would end up exporting more information than was strictly // necessary. The java_boot_libs property to allow those modules to be exported as part of the // sdk/module_exports without exposing any unnecessary information. - javaBootLibsSdkMemberType = &librarySdkMemberType{ + JavaBootLibsSdkMemberType = &librarySdkMemberType{ android.SdkMemberTypeBase{ PropertyName: "java_boot_libs", SupportsSdk: true, @@ -194,7 +193,7 @@ var ( // either java_libs, or java_header_libs would end up exporting more information than was strictly // necessary. The java_systemserver_libs property to allow those modules to be exported as part of // the sdk/module_exports without exposing any unnecessary information. - javaSystemserverLibsSdkMemberType = &librarySdkMemberType{ + JavaSystemserverLibsSdkMemberType = &librarySdkMemberType{ android.SdkMemberTypeBase{ PropertyName: "java_systemserver_libs", SupportsSdk: true, @@ -310,6 +309,10 @@ type JavaInfo struct { // implementation jars. If the provider is set by java_sdk_library, the link type is "unknown" // and selection between the stub jar vs implementation jar is deferred to SdkLibrary.sdkJars(...) StubsLinkType StubsLinkType + + // AconfigIntermediateCacheOutputPaths is a path to the cache files collected from the + // java_aconfig_library modules that are statically linked to this module. + AconfigIntermediateCacheOutputPaths android.Paths } var JavaInfoProvider = blueprint.NewProvider[JavaInfo]() @@ -346,6 +349,12 @@ func (j *Module) XrefJavaFiles() android.Paths { return j.kytheFiles } +func (d dependencyTag) PropagateAconfigValidation() bool { + return d.static +} + +var _ android.PropagateAconfigValidationDependencyTag = dependencyTag{} + type dependencyTag struct { blueprint.BaseDependencyTag name string @@ -355,6 +364,8 @@ type dependencyTag struct { // True if the dependency is a toolchain, for example an annotation processor. toolchain bool + + static bool } // installDependencyTag is a dependency tag that is annotated to cause the installed files of the @@ -400,7 +411,7 @@ func IsJniDepTag(depTag blueprint.DependencyTag) bool { var ( dataNativeBinsTag = dependencyTag{name: "dataNativeBins"} dataDeviceBinsTag = dependencyTag{name: "dataDeviceBins"} - staticLibTag = dependencyTag{name: "staticlib"} + staticLibTag = dependencyTag{name: "staticlib", static: true} libTag = dependencyTag{name: "javalib", runtimeLinked: true} sdkLibTag = dependencyTag{name: "sdklib", runtimeLinked: true} java9LibTag = dependencyTag{name: "java9lib", runtimeLinked: true} @@ -680,10 +691,11 @@ func shouldUncompressDex(ctx android.ModuleContext, libName string, dexpreopter return true } - // Store uncompressed dex files that are preopted on /system. - if !dexpreopter.dexpreoptDisabled(ctx, libName) && (ctx.Host() || !dexpreopter.odexOnSystemOther(ctx, libName, dexpreopter.installPath)) { + // Store uncompressed dex files that are preopted on /system or /system_other. + if !dexpreopter.dexpreoptDisabled(ctx, libName) { return true } + if ctx.Config().UncompressPrivAppDex() && inList(ctx.ModuleName(), ctx.Config().ModulesLoadedByPrivilegedModules()) { return true @@ -889,7 +901,7 @@ func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { } } - j.stem = proptools.StringDefault(j.overridableDeviceProperties.Stem, ctx.ModuleName()) + j.stem = proptools.StringDefault(j.overridableProperties.Stem, ctx.ModuleName()) proguardSpecInfo := j.collectProguardSpecInfo(ctx) android.SetProvider(ctx, ProguardSpecInfoProvider, proguardSpecInfo) @@ -1686,7 +1698,7 @@ func (j *Binary) HostToolPath() android.OptionalPath { } func (j *Binary) GenerateAndroidBuildActions(ctx android.ModuleContext) { - j.stem = proptools.StringDefault(j.overridableDeviceProperties.Stem, ctx.ModuleName()) + j.stem = proptools.StringDefault(j.overridableProperties.Stem, ctx.ModuleName()) if ctx.Arch().ArchType == android.Common { // Compile the jar @@ -2172,7 +2184,7 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { case aconfigDeclarationTag: if provider, ok := android.OtherModuleProvider(ctx, dep, android.AconfigDeclarationsProviderKey); ok { al.aconfigProtoFiles = append(al.aconfigProtoFiles, provider.IntermediateCacheOutputPath) - } else if provider, ok := android.OtherModuleProvider(ctx, dep, aconfig.CodegenInfoProvider); ok { + } else if provider, ok := android.OtherModuleProvider(ctx, dep, android.CodegenInfoProvider); ok { al.aconfigProtoFiles = append(al.aconfigProtoFiles, provider.IntermediateCacheOutputPaths...) } else { ctx.ModuleErrorf("Only aconfig_declarations and aconfig_declarations_group "+ @@ -2997,7 +3009,7 @@ func DefaultsFactory() android.Module { module.AddProperties( &CommonProperties{}, &DeviceProperties{}, - &OverridableDeviceProperties{}, + &OverridableProperties{}, &DexProperties{}, &DexpreoptProperties{}, &android.ProtoProperties{}, diff --git a/java/java_test.go b/java/java_test.go index 2f3ccb98d..194f9d974 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -2643,6 +2643,70 @@ func TestMultiplePrebuilts(t *testing.T) { } } +func TestMultiplePlatformCompatConfigPrebuilts(t *testing.T) { + bp := ` + // multiple variations of platform_compat_config + // source + platform_compat_config { + name: "myconfig", + } + // prebuilt "v1" + prebuilt_platform_compat_config { + name: "myconfig", + metadata: "myconfig.xml", + } + // prebuilt "v2" + prebuilt_platform_compat_config { + name: "myconfig.v2", + source_module_name: "myconfig", // without source_module_name, the singleton will merge two .xml files + metadata: "myconfig.v2.xml", + } + + // selectors + apex_contributions { + name: "myapex_contributions", + contents: ["%v"], + } + ` + testCases := []struct { + desc string + selectedDependencyName string + expectedPlatformCompatConfigXml string + }{ + { + desc: "Source platform_compat_config is selected using apex_contributions", + selectedDependencyName: "myconfig", + expectedPlatformCompatConfigXml: "out/soong/.intermediates/myconfig/android_common/myconfig_meta.xml", + }, + { + desc: "Prebuilt platform_compat_config v1 is selected using apex_contributions", + selectedDependencyName: "prebuilt_myconfig", + expectedPlatformCompatConfigXml: "myconfig.xml", + }, + { + desc: "Prebuilt platform_compat_config v2 is selected using apex_contributions", + selectedDependencyName: "prebuilt_myconfig.v2", + expectedPlatformCompatConfigXml: "myconfig.v2.xml", + }, + } + + for _, tc := range testCases { + ctx := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithPlatformCompatConfig, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildFlags = map[string]string{ + "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions", + } + }), + ).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName)) + + mergedGlobalConfig := ctx.SingletonForTests("platform_compat_config_singleton").Output("compat_config/merged_compat_config.xml") + android.AssertIntEquals(t, "The merged compat config file should only have a single dependency", 1, len(mergedGlobalConfig.Implicits)) + android.AssertStringEquals(t, "The merged compat config file is missing the appropriate platform compat config", mergedGlobalConfig.Implicits[0].String(), tc.expectedPlatformCompatConfigXml) + } +} + func TestApiLibraryAconfigDeclarations(t *testing.T) { result := android.GroupFixturePreparers( prepareForJavaTest, @@ -2693,3 +2757,38 @@ func TestApiLibraryAconfigDeclarations(t *testing.T) { cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command) android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "revert-annotations-exportable.txt") } + +func TestJavaLibHostWithStem(t *testing.T) { + ctx, _ := testJava(t, ` + java_library_host { + name: "foo", + srcs: ["a.java"], + stem: "foo-new", + } + `) + + buildOS := ctx.Config().BuildOS.String() + foo := ctx.ModuleForTests("foo", buildOS+"_common") + + outputs := fmt.Sprint(foo.AllOutputs()) + if !strings.Contains(outputs, "foo-new.jar") { + t.Errorf("Module output does not contain expected jar %s", "foo-new.jar") + } +} + +func TestJavaLibWithStem(t *testing.T) { + ctx, _ := testJava(t, ` + java_library { + name: "foo", + srcs: ["a.java"], + stem: "foo-new", + } + `) + + foo := ctx.ModuleForTests("foo", "android_common") + + outputs := fmt.Sprint(foo.AllOutputs()) + if !strings.Contains(outputs, "foo-new.jar") { + t.Errorf("Module output does not contain expected jar %s", "foo-new.jar") + } +} diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go index 2197304a5..2fc6c02a6 100644 --- a/java/platform_compat_config.go +++ b/java/platform_compat_config.go @@ -20,6 +20,7 @@ import ( "android/soong/android" "github.com/google/blueprint" + "github.com/google/blueprint/proptools" ) func init() { @@ -184,6 +185,11 @@ type prebuiltCompatConfigModule struct { type prebuiltCompatConfigProperties struct { Metadata *string `android:"path"` + + // Name of the source soong module that gets shadowed by this prebuilt + // If unspecified, follows the naming convention that the source module of + // the prebuilt is Name() without "prebuilt_" prefix + Source_module_name *string } func (module *prebuiltCompatConfigModule) Prebuilt() *android.Prebuilt { @@ -198,6 +204,10 @@ func (module *prebuiltCompatConfigModule) compatConfigMetadata() android.Path { return module.metadataFile } +func (module *prebuiltCompatConfigModule) BaseModuleName() string { + return proptools.StringDefault(module.properties.Source_module_name, module.ModuleBase.Name()) +} + var _ platformCompatConfigMetadataProvider = (*prebuiltCompatConfigModule)(nil) func (module *prebuiltCompatConfigModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go index 94e9c6c57..6a79e5883 100644 --- a/java/prebuilt_apis.go +++ b/java/prebuilt_apis.go @@ -158,6 +158,21 @@ func createApiModule(mctx android.LoadHookContext, name string, path string) { mctx.CreateModule(genrule.GenRuleFactory, &genruleProps) } +func createCombinedApiFilegroupModule(mctx android.LoadHookContext, name string, srcs []string) { + filegroupProps := struct { + Name *string + Srcs []string + }{} + filegroupProps.Name = proptools.StringPtr(name) + + var transformedSrcs []string + for _, src := range srcs { + transformedSrcs = append(transformedSrcs, ":"+src) + } + filegroupProps.Srcs = transformedSrcs + mctx.CreateModule(android.FileGroupFactory, &filegroupProps) +} + func createLatestApiModuleExtensionVersionFile(mctx android.LoadHookContext, name string, version string) { genruleProps := struct { Name *string @@ -252,6 +267,10 @@ func PrebuiltApiModuleName(module, scope, version string) string { return module + ".api." + scope + "." + version } +func PrebuiltApiCombinedModuleName(module, scope, version string) string { + return module + ".api.combined." + scope + "." + version +} + func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { // <apiver>/<scope>/api/<module>.txt apiLevelFiles := globApiDirs(mctx, p, "api/*.txt") @@ -307,7 +326,9 @@ func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { } // Sort the keys in order to make build.ninja stable - for _, k := range android.SortedKeys(latest) { + sortedLatestKeys := android.SortedKeys(latest) + + for _, k := range sortedLatestKeys { info := latest[k] name := PrebuiltApiModuleName(info.module, info.scope, "latest") latestExtensionVersionModuleName := PrebuiltApiModuleName(info.module, info.scope, "latest.extension_version") @@ -333,11 +354,25 @@ func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { } } // Create empty incompatibilities files for remaining modules - for _, k := range android.SortedKeys(latest) { + // If the incompatibility module has been created, create a corresponding combined module + for _, k := range sortedLatestKeys { if _, ok := incompatibilities[k]; !ok { createEmptyFile(mctx, PrebuiltApiModuleName(latest[k].module+"-incompatibilities", latest[k].scope, "latest")) } } + + // Create combined latest api and removed api files modules. + // The combined modules list all api files of the api scope and its subset api scopes. + for _, k := range sortedLatestKeys { + info := latest[k] + name := PrebuiltApiCombinedModuleName(info.module, info.scope, "latest") + + var srcs []string + currentApiScope := scopeByName[info.scope] + srcs = append(srcs, PrebuiltApiModuleName(info.module, currentApiScope.name, "latest")) + + createCombinedApiFilegroupModule(mctx, name, srcs) + } } func createPrebuiltApiModules(mctx android.LoadHookContext) { diff --git a/java/rro.go b/java/rro.go index 3e0f8e94d..72170fc6d 100644 --- a/java/rro.go +++ b/java/rro.go @@ -122,6 +122,10 @@ func (r *RuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) ctx.AddVariationDependencies(nil, staticLibTag, r.properties.Static_libs...) ctx.AddVariationDependencies(nil, libTag, r.properties.Resource_libs...) + + for _, aconfig_declaration := range r.aaptProperties.Flags_packages { + ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration) + } } func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -151,6 +155,7 @@ func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleC sdkContext: r, enforceDefaultTargetSdkVersion: false, extraLinkFlags: aaptLinkFlags, + aconfigTextFiles: getAconfigFilePaths(ctx), }, ) diff --git a/java/rro_test.go b/java/rro_test.go index c4a4d04be..d697ec627 100644 --- a/java/rro_test.go +++ b/java/rro_test.go @@ -405,3 +405,49 @@ func TestRuntimeResourceOverlayPartition(t *testing.T) { android.AssertPathRelativeToTopEquals(t, "Install dir is not correct for "+testCase.name, testCase.expectedPath, mod.installDir) } } + +func TestRuntimeResourceOverlayFlagsPackages(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + ).RunTestWithBp(t, ` + runtime_resource_overlay { + name: "foo", + sdk_version: "current", + flags_packages: [ + "bar", + "baz", + ], + } + aconfig_declarations { + name: "bar", + package: "com.example.package.bar", + srcs: [ + "bar.aconfig", + ], + } + aconfig_declarations { + name: "baz", + package: "com.example.package.baz", + srcs: [ + "baz.aconfig", + ], + } + `) + + foo := result.ModuleForTests("foo", "android_common") + + // runtime_resource_overlay module depends on aconfig_declarations listed in flags_packages + android.AssertBoolEquals(t, "foo expected to depend on bar", true, + CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar")) + + android.AssertBoolEquals(t, "foo expected to depend on baz", true, + CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "baz")) + + aapt2LinkRule := foo.Rule("android/soong/java.aapt2Link") + linkInFlags := aapt2LinkRule.Args["inFlags"] + android.AssertStringDoesContain(t, + "aapt2 link command expected to pass feature flags arguments", + linkInFlags, + "--feature-flags @out/soong/.intermediates/bar/intermediate.txt --feature-flags @out/soong/.intermediates/baz/intermediate.txt", + ) +} diff --git a/java/sdk_library.go b/java/sdk_library.go index cdd04483d..5ddc6751c 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -30,6 +30,7 @@ import ( "android/soong/android" "android/soong/dexpreopt" + "android/soong/etc" ) const ( @@ -1672,12 +1673,16 @@ func latestPrebuiltApiModuleName(name string, apiScope *apiScope) string { return PrebuiltApiModuleName(name, apiScope.name, "latest") } +func latestPrebuiltApiCombinedModuleName(name string, apiScope *apiScope) string { + return PrebuiltApiCombinedModuleName(name, apiScope.name, "latest") +} + func (module *SdkLibrary) latestApiFilegroupName(apiScope *apiScope) string { return ":" + module.latestApiModuleName(apiScope) } func (module *SdkLibrary) latestApiModuleName(apiScope *apiScope) string { - return latestPrebuiltApiModuleName(module.distStem(), apiScope) + return latestPrebuiltApiCombinedModuleName(module.distStem(), apiScope) } func (module *SdkLibrary) latestRemovedApiFilegroupName(apiScope *apiScope) string { @@ -1685,7 +1690,7 @@ func (module *SdkLibrary) latestRemovedApiFilegroupName(apiScope *apiScope) stri } func (module *SdkLibrary) latestRemovedApiModuleName(apiScope *apiScope) string { - return latestPrebuiltApiModuleName(module.distStem()+"-removed", apiScope) + return latestPrebuiltApiCombinedModuleName(module.distStem()+"-removed", apiScope) } func (module *SdkLibrary) latestIncompatibilitiesFilegroupName(apiScope *apiScope) string { @@ -1993,20 +1998,25 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC if !Bool(module.sdkLibraryProperties.No_dist) { // Dist the api txt and removed api txt artifacts for sdk builds. distDir := proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api")) + stubsTypeTagPrefix := "" + if mctx.Config().ReleaseHiddenApiExportableStubs() { + stubsTypeTagPrefix = ".exportable" + } for _, p := range []struct { tag string pattern string }{ // "exportable" api files are copied to the dist directory instead of the - // "everything" api files. - {tag: ".exportable.api.txt", pattern: "%s.txt"}, - {tag: ".exportable.removed-api.txt", pattern: "%s-removed.txt"}, + // "everything" api files when "RELEASE_HIDDEN_API_EXPORTABLE_STUBS" build flag + // is set. Otherwise, the "everything" api files are copied to the dist directory. + {tag: "%s.api.txt", pattern: "%s.txt"}, + {tag: "%s.removed-api.txt", pattern: "%s-removed.txt"}, } { props.Dists = append(props.Dists, android.Dist{ Targets: []string{"sdk", "win_sdk"}, Dir: distDir, Dest: proptools.StringPtr(fmt.Sprintf(p.pattern, module.distStem())), - Tag: proptools.StringPtr(p.tag), + Tag: proptools.StringPtr(fmt.Sprintf(p.tag, stubsTypeTagPrefix)), }) } } @@ -2079,7 +2089,7 @@ func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, mctx.CreateModule(ApiLibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) } -func (module *SdkLibrary) topLevelStubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope) libraryProperties { +func (module *SdkLibrary) topLevelStubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope, doDist bool) libraryProperties { props := libraryProperties{} props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_library_visibility) @@ -2095,13 +2105,22 @@ func (module *SdkLibrary) topLevelStubsLibraryProps(mctx android.DefaultableHook } props.Compile_dex = compileDex + if !Bool(module.sdkLibraryProperties.No_dist) && doDist { + props.Dist.Targets = []string{"sdk", "win_sdk"} + props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.distStem())) + props.Dist.Dir = proptools.StringPtr(module.apiDistPath(apiScope)) + props.Dist.Tag = proptools.StringPtr(".jar") + } + return props } func (module *SdkLibrary) createTopLevelStubsLibrary( mctx android.DefaultableHookContext, apiScope *apiScope, contributesToApiSurface bool) { - props := module.topLevelStubsLibraryProps(mctx, apiScope) + // Dist the "everything" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is false + doDist := !mctx.Config().ReleaseHiddenApiExportableStubs() + props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist) props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope)) // Add the stub compiling java_library/java_api_library as static lib based on build config @@ -2117,18 +2136,11 @@ func (module *SdkLibrary) createTopLevelStubsLibrary( func (module *SdkLibrary) createTopLevelExportableStubsLibrary( mctx android.DefaultableHookContext, apiScope *apiScope) { - props := module.topLevelStubsLibraryProps(mctx, apiScope) + // Dist the "exportable" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is true + doDist := mctx.Config().ReleaseHiddenApiExportableStubs() + props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist) props.Name = proptools.StringPtr(module.exportableStubsLibraryModuleName(apiScope)) - // Dist the class jar artifact for sdk builds. - // "exportable" stubs are copied to dist for sdk builds instead of the "everything" stubs. - if !Bool(module.sdkLibraryProperties.No_dist) { - props.Dist.Targets = []string{"sdk", "win_sdk"} - props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.distStem())) - props.Dist.Dir = proptools.StringPtr(module.apiDistPath(apiScope)) - props.Dist.Tag = proptools.StringPtr(".jar") - } - staticLib := module.exportableSourceStubsLibraryModuleName(apiScope) props.Static_libs = append(props.Static_libs, staticLib) @@ -3163,10 +3175,12 @@ func (module *sdkLibraryXml) SubDir() string { } // from android.PrebuiltEtcModule -func (module *sdkLibraryXml) OutputFile() android.OutputPath { - return module.outputFilePath +func (module *sdkLibraryXml) OutputFiles(tag string) (android.Paths, error) { + return android.OutputPaths{module.outputFilePath}.Paths(), nil } +var _ etc.PrebuiltEtcModule = (*sdkLibraryXml)(nil) + // from android.ApexModule func (module *sdkLibraryXml) AvailableFor(what string) bool { return true diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go index 93ef40872..fb584c5c7 100644 --- a/java/sdk_library_test.go +++ b/java/sdk_library_test.go @@ -139,10 +139,10 @@ func TestJavaSdkLibrary(t *testing.T) { exportedComponentsInfo, _ := android.SingletonModuleProvider(result, foo.Module(), android.ExportedComponentsInfoProvider) expectedFooExportedComponents := []string{ - "foo-removed.api.public.latest", - "foo-removed.api.system.latest", - "foo.api.public.latest", - "foo.api.system.latest", + "foo-removed.api.combined.public.latest", + "foo-removed.api.combined.system.latest", + "foo.api.combined.public.latest", + "foo.api.combined.system.latest", "foo.stubs", "foo.stubs.exportable", "foo.stubs.exportable.system", @@ -556,8 +556,8 @@ func TestJavaSdkLibrary_Deps(t *testing.T) { CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{ `dex2oatd`, - `sdklib-removed.api.public.latest`, - `sdklib.api.public.latest`, + `sdklib-removed.api.combined.public.latest`, + `sdklib.api.combined.public.latest`, `sdklib.impl`, `sdklib.stubs`, `sdklib.stubs.exportable`, @@ -960,8 +960,8 @@ func TestJavaSdkLibraryImport_WithSource(t *testing.T) { CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{ `dex2oatd`, `prebuilt_sdklib`, - `sdklib-removed.api.public.latest`, - `sdklib.api.public.latest`, + `sdklib-removed.api.combined.public.latest`, + `sdklib.api.combined.public.latest`, `sdklib.impl`, `sdklib.stubs`, `sdklib.stubs.exportable`, @@ -1039,8 +1039,8 @@ func testJavaSdkLibraryImport_Preferred(t *testing.T, prefer string, preparer an CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{ `prebuilt_sdklib`, - `sdklib-removed.api.public.latest`, - `sdklib.api.public.latest`, + `sdklib-removed.api.combined.public.latest`, + `sdklib.api.combined.public.latest`, `sdklib.impl`, `sdklib.stubs`, `sdklib.stubs.exportable`, @@ -1393,6 +1393,11 @@ func TestJavaSdkLibraryDist(t *testing.T) { "sdklib_group_foo", "sdklib_owner_foo", "foo"), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildFlags = map[string]string{ + "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", + } + }), ).RunTestWithBp(t, ` java_sdk_library { name: "sdklib_no_group", diff --git a/java/system_modules.go b/java/system_modules.go index 92e31cdf9..8e2d5d8ff 100644 --- a/java/system_modules.go +++ b/java/system_modules.go @@ -64,6 +64,7 @@ var ( // useful on Android, and (b) it causes errors with later versions of jlink // when the jdk.internal.module is absent from java.base (as it is here). ` --disable-plugin system-modules && ` + + `rm -rf ${workDir} && ` + `cp ${config.JrtFsJar} ${outDir}/lib/`, CommandDeps: []string{ "${moduleInfoJavaPath}", diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go index 59c546634..b291e708b 100644 --- a/java/systemserver_classpath_fragment.go +++ b/java/systemserver_classpath_fragment.go @@ -189,7 +189,7 @@ func (b systemServerClasspathFragmentContentDependencyTag) SdkMemberType(child a return javaSdkLibrarySdkMemberType } - return javaSystemserverLibsSdkMemberType + return JavaSystemserverLibsSdkMemberType } func (b systemServerClasspathFragmentContentDependencyTag) ExportMember() bool { diff --git a/mk2rbc/test/version_defaults.mk.test b/mk2rbc/test/version_defaults.mk.test index 166639278..3ce60bcdb 100644 --- a/mk2rbc/test/version_defaults.mk.test +++ b/mk2rbc/test/version_defaults.mk.test @@ -3,8 +3,8 @@ ifdef INTERNAL_BUILD_ID_MAKEFILE include $(INTERNAL_BUILD_ID_MAKEFILE) endif -DEFAULT_PLATFORM_VERSION := TP1A -.KATI_READONLY := DEFAULT_PLATFORM_VERSION +RELEASE_PLATFORM_VERSION := TP1A +.KATI_READONLY := RELEASE_PLATFORM_VERSION MIN_PLATFORM_VERSION := TP1A MAX_PLATFORM_VERSION := TP1A PLATFORM_VERSION_LAST_STABLE := 12 diff --git a/rust/compiler.go b/rust/compiler.go index c1bdbeb91..03fdf2b7a 100644 --- a/rust/compiler.go +++ b/rust/compiler.go @@ -75,6 +75,8 @@ type compiler interface { strippedOutputFilePath() android.OptionalPath checkedCrateRootPath() (android.Path, error) + + Aliases() map[string]string } func (compiler *baseCompiler) edition() string { @@ -140,6 +142,12 @@ type BaseCompilerProperties struct { // flags to pass to the linker Ld_flags []string `android:"arch_variant"` + // Rust crate dependencies to rename. Each entry should be a string of the form "dependencyname:alias". + // + // "dependencyname" here should be the name of the crate, not the Android module. This is + // equivalent to writing `alias = { package = "dependencyname" }` in a `Cargo.toml`. + Aliases []string + // list of rust rlib crate dependencies Rlibs []string `android:"arch_variant"` @@ -281,6 +289,18 @@ func (compiler *baseCompiler) preferRlib() bool { return Bool(compiler.Properties.Prefer_rlib) } +func (compiler *baseCompiler) Aliases() map[string]string { + aliases := map[string]string{} + for _, entry := range compiler.Properties.Aliases { + dep, alias, found := strings.Cut(entry, ":") + if !found { + panic(fmt.Errorf("invalid aliases entry %q missing ':'", entry)) + } + aliases[dep] = alias + } + return aliases +} + func (compiler *baseCompiler) stdLinkage(ctx *depsContext) RustLinkage { // For devices, we always link stdlibs in as dylibs by default. if compiler.preferRlib() { diff --git a/rust/config/global.go b/rust/config/global.go index 418eca455..e28dbaaea 100644 --- a/rust/config/global.go +++ b/rust/config/global.go @@ -25,7 +25,7 @@ var ( pctx = android.NewPackageContext("android/soong/rust/config") ExportedVars = android.NewExportedVariables(pctx) - RustDefaultVersion = "1.75.0" + RustDefaultVersion = "1.76.0" RustDefaultBase = "prebuilts/rust/" DefaultEdition = "2021" Stdlibs = []string{ diff --git a/rust/rust.go b/rust/rust.go index 245ed2e8e..7d81c721e 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -1063,6 +1063,12 @@ func (d dependencyTag) LicenseAnnotations() []android.LicenseAnnotation { return nil } +func (d dependencyTag) PropagateAconfigValidation() bool { + return d == rlibDepTag || d == sourceDepTag +} + +var _ android.PropagateAconfigValidationDependencyTag = dependencyTag{} + var _ android.LicenseAnnotationsDependencyTag = dependencyTag{} var ( @@ -1136,6 +1142,7 @@ func collectIncludedProtos(mod *Module, dep *Module) { } } } + func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { var depPaths PathDeps @@ -1427,16 +1434,29 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { mod.transitiveAndroidMkSharedLibs = android.NewDepSet[string](android.PREORDER, directAndroidMkSharedLibs, transitiveAndroidMkSharedLibs) var rlibDepFiles RustLibraries + aliases := mod.compiler.Aliases() for _, dep := range directRlibDeps { - rlibDepFiles = append(rlibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: dep.CrateName()}) + crateName := dep.CrateName() + if alias, aliased := aliases[crateName]; aliased { + crateName = alias + } + rlibDepFiles = append(rlibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: crateName}) } var dylibDepFiles RustLibraries for _, dep := range directDylibDeps { - dylibDepFiles = append(dylibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: dep.CrateName()}) + crateName := dep.CrateName() + if alias, aliased := aliases[crateName]; aliased { + crateName = alias + } + dylibDepFiles = append(dylibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: crateName}) } var procMacroDepFiles RustLibraries for _, dep := range directProcMacroDeps { - procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: dep.CrateName()}) + crateName := dep.CrateName() + if alias, aliased := aliases[crateName]; aliased { + crateName = alias + } + procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: crateName}) } var staticLibDepFiles android.Paths diff --git a/rust/rust_test.go b/rust/rust_test.go index d609c2faa..295a734b6 100644 --- a/rust/rust_test.go +++ b/rust/rust_test.go @@ -470,6 +470,35 @@ func TestLibrarySizes(t *testing.T) { m.Output("libwaldo.dylib.so.bloaty.csv") } +// Test that aliases are respected. +func TestRustAliases(t *testing.T) { + ctx := testRust(t, ` + rust_library { + name: "libbar", + crate_name: "bar", + srcs: ["src/lib.rs"], + } + rust_library { + name: "libbaz", + crate_name: "baz", + srcs: ["src/lib.rs"], + } + rust_binary { + name: "foo", + srcs: ["src/main.rs"], + rustlibs: ["libbar", "libbaz"], + aliases: ["bar:bar_renamed"], + }`) + + fooRustc := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustc") + if !strings.Contains(fooRustc.Args["libFlags"], "--extern bar_renamed=out/soong/.intermediates/libbar/android_arm64_armv8-a_dylib/unstripped/libbar.dylib.so") { + t.Errorf("--extern bar_renamed=out/soong/.intermediates/libbar/android_arm64_armv8-a_dylib/unstripped/libbar.dylib.so flag not being passed to rustc for rust_binary with aliases. libFlags: %#v", fooRustc.Args["libFlags"]) + } + if !strings.Contains(fooRustc.Args["libFlags"], "--extern baz=out/soong/.intermediates/libbaz/android_arm64_armv8-a_dylib/unstripped/libbaz.dylib.so") { + t.Errorf("--extern baz=out/soong/.intermediates/libbaz/android_arm64_armv8-a_dylib/unstripped/libbaz.dylib.so flag not being passed to rustc for rust_binary with aliases. libFlags: %#v", fooRustc.Args["libFlags"]) + } +} + func assertString(t *testing.T, got, expected string) { t.Helper() if got != expected { diff --git a/scripts/Android.bp b/scripts/Android.bp index e2fd59f17..f36329b1d 100644 --- a/scripts/Android.bp +++ b/scripts/Android.bp @@ -292,3 +292,16 @@ sh_binary_host { name: "keep-flagged-apis", src: "keep-flagged-apis.sh", } + +python_binary_host { + name: "merge_directories", + main: "merge_directories.py", + srcs: [ + "merge_directories.py", + ], + version: { + py3: { + embedded_launcher: true, + }, + }, +} diff --git a/scripts/merge_directories.py b/scripts/merge_directories.py new file mode 100755 index 000000000..3f8631bab --- /dev/null +++ b/scripts/merge_directories.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +import argparse +import os +import shutil +import sys + +def main(): + parser = argparse.ArgumentParser( + description="Given a list of directories, this script will copy the contents of all of " + "them into the first directory, erroring out if any duplicate files are found." + ) + parser.add_argument( + "--ignore-duplicates", + action="store_true", + help="Don't error out on duplicate files, just skip them. The file from the earliest " + "directory listed on the command line will be the winner." + ) + parser.add_argument( + "--file-list", + help="Path to a text file containing paths relative to in_dir. Only these paths will be " + "copied out of in_dir." + ) + parser.add_argument("out_dir") + parser.add_argument("in_dir") + args = parser.parse_args() + + if not os.path.isdir(args.out_dir): + sys.exit(f"error: {args.out_dir} must be a directory") + if not os.path.isdir(args.in_dir): + sys.exit(f"error: {args.in_dir} must be a directory") + + file_list = None + if args.file_list: + with open(file_list_file, "r") as f: + file_list = f.read().strip().splitlines() + + in_dir = args.in_dir + for root, dirs, files in os.walk(in_dir): + rel_root = os.path.relpath(root, in_dir) + dst_root = os.path.join(args.out_dir, rel_root) + made_parent_dirs = False + for f in files: + src = os.path.join(root, f) + dst = os.path.join(dst_root, f) + p = os.path.normpath(os.path.join(rel_root, f)) + if file_list is not None and p not in file_list: + continue + if os.path.lexists(dst): + if args.ignore_duplicates: + continue + sys.exit(f"error: {p} exists in both {args.out_dir} and {in_dir}") + + if not made_parent_dirs: + os.makedirs(dst_root, exist_ok=True) + made_parent_dirs = True + + shutil.copy2(src, dst, follow_symlinks=False) + +if __name__ == "__main__": + main() diff --git a/scripts/test_config_fixer.py b/scripts/test_config_fixer.py index 07e01a14a..2876bcb63 100644 --- a/scripts/test_config_fixer.py +++ b/scripts/test_config_fixer.py @@ -19,6 +19,7 @@ from __future__ import print_function import argparse +import json import sys from xml.dom import minidom @@ -31,6 +32,8 @@ from manifest import write_xml KNOWN_PREPARERS = ['com.android.tradefed.targetprep.TestAppInstallSetup', 'com.android.tradefed.targetprep.suite.SuiteApkInstaller'] +KNOWN_TEST_RUNNERS = ['com.android.tradefed.testtype.AndroidJUnitTest'] + MAINLINE_CONTROLLER = 'com.android.tradefed.testtype.suite.module.MainlineTestModuleController' def parse_args(): @@ -43,8 +46,12 @@ def parse_args(): help=('overwrite package fields in the test config')) parser.add_argument('--test-file-name', default='', dest='test_file_name', help=('overwrite test file name in the test config')) + parser.add_argument('--orig-test-file-name', default='', dest='orig_test_file_name', + help=('Use with test-file-name to only override a single apk')) parser.add_argument('--mainline-package-name', default='', dest='mainline_package_name', help=('overwrite mainline module package name in the test config')) + parser.add_argument('--test-runner-options', default='', dest='test_runner_options', + help=('Add test runner options in the test config')) parser.add_argument('input', help='input test config file') parser.add_argument('output', help='output test config file') return parser.parse_args() @@ -76,6 +83,18 @@ def overwrite_test_file_name(test_config_doc, test_file_name): if option.getAttribute('name') == "test-file-name": option.setAttribute('value', test_file_name) +def overwrite_single_test_file_name(test_config_doc, orig_test_file_name, new_test_file_name): + + test_config = parse_test_config(test_config_doc) + tests = get_children_with_tag(test_config, 'target_preparer') + + for test in tests: + if test.getAttribute('class') in KNOWN_PREPARERS: + options = get_children_with_tag(test, 'option') + for option in options: + if option.getAttribute('name') == "test-file-name" and option.getAttribute('value') == orig_test_file_name: + option.setAttribute('value', new_test_file_name) + def overwrite_mainline_module_package_name(test_config_doc, mainline_package_name): test_config = parse_test_config(test_config_doc) @@ -86,6 +105,31 @@ def overwrite_mainline_module_package_name(test_config_doc, mainline_package_nam if option.getAttribute('name') == "mainline-module-package-name": option.setAttribute('value', mainline_package_name) +def add_test_runner_options_toplevel(test_config_doc, test_runner_options): + + test_config = parse_test_config(test_config_doc) + + test_config.appendChild(test_config_doc.createComment("Options from Android.bp")) + test_config.appendChild(test_config_doc.createTextNode("\n")) + for new_option in json.loads(test_runner_options): + option = test_config_doc.createElement("option") + # name and value are mandatory, + name = new_option.get('Name') + if not name: + raise RuntimeError('"name" must set in test_runner_option"') + value = new_option.get('Value') + if not value: + raise RuntimeError('"value" must set in test_runner_option"') + option.setAttribute('name', name) # 'include-filter') + option.setAttribute('value', value) # 'android.test.example.devcodelab.DevCodelabTest#testHelloFail') + key = new_option.get('Key') + if key: + option.setAttribute('key', key) # 'include-filter') + # add tab and newline for readability + test_config.appendChild(test_config_doc.createTextNode(" ")) + test_config.appendChild(option) + test_config.appendChild(test_config_doc.createTextNode("\n")) + def main(): """Program entry point.""" try: @@ -100,11 +144,20 @@ def main(): overwrite_package_name(doc, manifest_doc, args.package_name) if args.test_file_name: - overwrite_test_file_name(doc, args.test_file_name) + if args.orig_test_file_name: + overwrite_single_test_file_name(doc, args.orig_test_file_name, args.test_file_name) + else: + # You probably never want to override the test_file_name if there + # are several in the xml, but this is currently only used on generated + # AndroidTest.xml where there is only a single test-file-name (no data) + overwrite_test_file_name(doc, args.test_file_name) if args.mainline_package_name: overwrite_mainline_module_package_name(doc, args.mainline_package_name) + if args.test_runner_options: + add_test_runner_options_toplevel(doc, args.test_runner_options) + with open(args.output, 'w') as f: write_xml(f, doc) diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go index 5d419584a..8d3bbfa1b 100644 --- a/sdk/bootclasspath_fragment_sdk_test.go +++ b/sdk/bootclasspath_fragment_sdk_test.go @@ -152,6 +152,11 @@ func TestSnapshotWithBootclasspathFragment_ImageName(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: [], +} + prebuilt_bootclasspath_fragment { name: "art-bootclasspath-fragment", prefer: false, @@ -187,6 +192,7 @@ java_import { apex_available: ["com.android.art"], jars: ["java_boot_libs/snapshot/jars/are/invalid/core2.jar"], } + `), checkAllCopyRules(` .intermediates/art-bootclasspath-fragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv @@ -353,6 +359,15 @@ func testSnapshotWithBootClasspathFragment_Contents(t *testing.T, sdk string, co checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: [ + "prebuilt_myothersdklibrary", + "prebuilt_mysdklibrary", + "prebuilt_mycoreplatform", + ], +} + prebuilt_bootclasspath_fragment { name: "mybootclasspathfragment", prefer: false, @@ -642,6 +657,11 @@ func TestSnapshotWithBootClasspathFragment_Fragments(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_mysdklibrary"], +} + prebuilt_bootclasspath_fragment { name: "mybootclasspathfragment", prefer: false, @@ -881,6 +901,14 @@ func TestSnapshotWithBootclasspathFragment_HiddenAPI(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: [ + "prebuilt_mynewlibrary", + "prebuilt_mysdklibrary", + ], +} + prebuilt_bootclasspath_fragment { name: "mybootclasspathfragment", prefer: false, @@ -1008,6 +1036,9 @@ func testSnapshotWithBootClasspathFragment_MinSdkVersion(t *testing.T, targetBui android.FixtureMergeEnv(map[string]string{ "SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": targetBuildRelease, }), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_version_active_codenames = []string{"VanillaIceCream"} + }), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.BuildFlags = map[string]string{ @@ -1226,6 +1257,9 @@ func TestSnapshotWithEmptyBootClasspathFragment(t *testing.T) { android.FixtureMergeEnv(map[string]string{ "SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": "S", }), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_version_active_codenames = []string{"VanillaIceCream"} + }), android.FixtureWithRootAndroidBp(` sdk { name: "mysdk", diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go index 265579aa1..9490d1256 100644 --- a/sdk/cc_sdk_test.go +++ b/sdk/cc_sdk_test.go @@ -123,6 +123,11 @@ func TestSdkCompileMultilibOverride(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_sdkmember"], +} + cc_prebuilt_library_shared { name: "sdkmember", prefer: false, @@ -226,6 +231,11 @@ func TestSnapshotWithObject(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_crtobj"], +} + cc_prebuilt_object { name: "crtobj", prefer: false, @@ -333,6 +343,11 @@ func TestSnapshotWithCcExportGeneratedHeaders(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_mynativelib"], +} + cc_prebuilt_library_shared { name: "mynativelib", prefer: false, @@ -406,6 +421,11 @@ func TestSnapshotWithCcSharedLibraryCommonProperties(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_mynativelib"], +} + cc_prebuilt_library_shared { name: "mynativelib", prefer: false, @@ -465,6 +485,11 @@ func TestSnapshotWithCcBinary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mymodule_exports.contributions", + contents: ["prebuilt_mynativebinary"], +} + cc_prebuilt_binary { name: "mynativebinary", prefer: false, @@ -523,6 +548,11 @@ func TestMultipleHostOsTypesSnapshotWithCcBinary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "myexports.contributions", + contents: ["prebuilt_mynativebinary"], +} + cc_prebuilt_binary { name: "mynativebinary", prefer: false, @@ -621,6 +651,14 @@ func TestSnapshotWithSingleHostOsType(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "myexports.contributions", + contents: [ + "prebuilt_mynativebinary", + "prebuilt_mynativelib", + ], +} + cc_prebuilt_binary { name: "mynativebinary", prefer: false, @@ -696,6 +734,11 @@ func TestSnapshotWithCcStaticNocrtBinary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mymodule_exports.contributions", + contents: ["prebuilt_linker"], +} + cc_prebuilt_binary { name: "linker", prefer: false, @@ -755,6 +798,11 @@ func TestSnapshotWithCcSharedLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_mynativelib"], +} + cc_prebuilt_library_shared { name: "mynativelib", prefer: false, @@ -856,6 +904,15 @@ func TestSnapshotWithCcSharedLibrarySharedLibs(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: [ + "prebuilt_mynativelib", + "prebuilt_myothernativelib", + "prebuilt_mysystemnativelib", + ], +} + cc_prebuilt_library_shared { name: "mynativelib", prefer: false, @@ -953,6 +1010,11 @@ func TestHostSnapshotWithCcSharedLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_mynativelib"], +} + cc_prebuilt_library_shared { name: "mynativelib", prefer: false, @@ -1029,6 +1091,11 @@ func TestMultipleHostOsTypesSnapshotWithCcSharedLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_mynativelib"], +} + cc_prebuilt_library_shared { name: "mynativelib", prefer: false, @@ -1095,6 +1162,11 @@ func TestSnapshotWithCcStaticLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "myexports.contributions", + contents: ["prebuilt_mynativelib"], +} + cc_prebuilt_library_static { name: "mynativelib", prefer: false, @@ -1158,6 +1230,11 @@ func TestHostSnapshotWithCcStaticLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "myexports.contributions", + contents: ["prebuilt_mynativelib"], +} + cc_prebuilt_library_static { name: "mynativelib", prefer: false, @@ -1222,6 +1299,11 @@ func TestSnapshotWithCcLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "myexports.contributions", + contents: ["prebuilt_mynativelib"], +} + cc_prebuilt_library { name: "mynativelib", prefer: false, @@ -1298,6 +1380,11 @@ func TestSnapshotSameLibraryWithNativeLibsAndNativeSharedLib(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "myexports.contributions", + contents: ["prebuilt_mynativelib"], +} + cc_prebuilt_library { name: "mynativelib", prefer: false, @@ -1394,6 +1481,11 @@ func TestSnapshotSameLibraryWithAndroidNativeLibsAndHostNativeSharedLib(t *testi checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "myexports.contributions", + contents: ["prebuilt_mynativelib"], +} + cc_prebuilt_library { name: "mynativelib", prefer: false, @@ -1520,6 +1612,11 @@ func TestHostSnapshotWithMultiLib64(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "myexports.contributions", + contents: ["prebuilt_mynativelib"], +} + cc_prebuilt_library_static { name: "mynativelib", prefer: false, @@ -1572,6 +1669,11 @@ func TestSnapshotWithCcHeadersLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_mynativeheaders"], +} + cc_prebuilt_library_headers { name: "mynativeheaders", prefer: false, @@ -1594,6 +1696,9 @@ func TestSnapshotWithCcHeadersLibraryAndNativeBridgeSupport(t *testing.T) { PrepareForTestWithSdkBuildComponents, ccTestFs.AddToFixture(), prepareForTestWithNativeBridgeTarget, + android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { + android.RegisterApexContributionsBuildComponents(ctx) + }), ).RunTestWithBp(t, ` sdk { name: "mysdk", @@ -1616,6 +1721,11 @@ func TestSnapshotWithCcHeadersLibraryAndNativeBridgeSupport(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_mynativeheaders"], +} + cc_prebuilt_library_headers { name: "mynativeheaders", prefer: false, @@ -1679,6 +1789,9 @@ func TestSnapshotWithCcHeadersLibraryAndImageVariants(t *testing.T) { cc.PrepareForTestWithCcDefaultModules, PrepareForTestWithSdkBuildComponents, ccTestFs.AddToFixture(), + android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { + android.RegisterApexContributionsBuildComponents(ctx) + }), ).RunTestWithBp(t, fmt.Sprintf(` sdk { name: "mysdk", @@ -1701,6 +1814,11 @@ func TestSnapshotWithCcHeadersLibraryAndImageVariants(t *testing.T) { checkAndroidBpContents(fmt.Sprintf(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_mynativeheaders"], +} + cc_prebuilt_library_headers { name: "mynativeheaders", prefer: false, @@ -1750,6 +1868,11 @@ func TestHostSnapshotWithCcHeadersLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_mynativeheaders"], +} + cc_prebuilt_library_headers { name: "mynativeheaders", prefer: false, @@ -1807,6 +1930,11 @@ func TestDeviceAndHostSnapshotWithCcHeadersLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_mynativeheaders"], +} + cc_prebuilt_library_headers { name: "mynativeheaders", prefer: false, @@ -1870,6 +1998,15 @@ func TestSystemSharedLibPropagation(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: [ + "prebuilt_sslnil", + "prebuilt_sslempty", + "prebuilt_sslnonempty", + ], +} + cc_prebuilt_library_shared { name: "sslnil", prefer: false, @@ -1943,6 +2080,11 @@ cc_prebuilt_library_shared { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_sslvariants"], +} + cc_prebuilt_library_shared { name: "sslvariants", prefer: false, @@ -2002,6 +2144,11 @@ func TestStubsLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_stubslib"], +} + cc_prebuilt_library_shared { name: "stubslib", prefer: false, @@ -2056,6 +2203,11 @@ func TestDeviceAndHostSnapshotWithStubsLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_stubslib"], +} + cc_prebuilt_library_shared { name: "stubslib", prefer: false, @@ -2114,6 +2266,11 @@ func TestUniqueHostSoname(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_mylib"], +} + cc_prebuilt_library_shared { name: "mylib", prefer: false, @@ -2178,6 +2335,11 @@ func TestNoSanitizerMembers(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_mynativelib"], +} + cc_prebuilt_library_shared { name: "mynativelib", prefer: false, diff --git a/sdk/compat_config_sdk_test.go b/sdk/compat_config_sdk_test.go index 45e8e0ed6..75b5229bf 100644 --- a/sdk/compat_config_sdk_test.go +++ b/sdk/compat_config_sdk_test.go @@ -36,6 +36,11 @@ func testSnapshotWithCompatConfig(t *testing.T, sdk string) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myconfig"], +} + prebuilt_platform_compat_config { name: "myconfig", prefer: false, diff --git a/sdk/exports_test.go b/sdk/exports_test.go index 2605fd141..9d0a24210 100644 --- a/sdk/exports_test.go +++ b/sdk/exports_test.go @@ -46,6 +46,11 @@ func TestModuleExportsSnapshot(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "myexports.contributions", + contents: ["prebuilt_myjavalib"], +} + java_import { name: "myjavalib", prefer: false, diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go index 1b2b0f1f3..275860f32 100644 --- a/sdk/java_sdk_test.go +++ b/sdk/java_sdk_test.go @@ -108,6 +108,11 @@ func TestSnapshotWithJavaHeaderLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib"], +} + java_import { name: "myjavalib", prefer: false, @@ -154,6 +159,11 @@ func TestHostSnapshotWithJavaHeaderLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib"], +} + java_import { name: "myjavalib", prefer: false, @@ -193,6 +203,11 @@ func TestDeviceAndHostSnapshotWithJavaHeaderLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib"], +} + java_import { name: "myjavalib", prefer: false, @@ -245,6 +260,11 @@ func TestSnapshotWithJavaImplLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "myexports.contributions", + contents: ["prebuilt_myjavalib"], +} + java_import { name: "myjavalib", prefer: false, @@ -291,6 +311,11 @@ func TestSnapshotWithJavaBootLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: [], +} + java_import { name: "myjavalib", prefer: false, @@ -313,6 +338,9 @@ func TestSnapshotWithJavaBootLibrary_UpdatableMedia(t *testing.T) { android.FixtureMergeEnv(map[string]string{ "SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": targetBuildRelease, }), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_version_active_codenames = []string{"VanillaIceCream"} + }), ).RunTestWithBp(t, ` sdk { name: "mysdk", @@ -395,6 +423,11 @@ func TestSnapshotWithJavaLibrary_MinSdkVersion(t *testing.T) { checkAndroidBpContents(fmt.Sprintf(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_mylib"], +} + java_import { name: "mylib", prefer: false, @@ -459,6 +492,11 @@ func TestSnapshotWithJavaSystemserverLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "myexports.contributions", + contents: [], +} + java_import { name: "myjavalib", prefer: false, @@ -504,6 +542,11 @@ func TestHostSnapshotWithJavaImplLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "myexports.contributions", + contents: ["prebuilt_myjavalib"], +} + java_import { name: "myjavalib", prefer: false, @@ -542,6 +585,11 @@ func TestSnapshotWithJavaTest(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "myexports.contributions", + contents: ["prebuilt_myjavatests"], +} + java_test_import { name: "myjavatests", prefer: false, @@ -582,6 +630,11 @@ func TestHostSnapshotWithJavaTest(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "myexports.contributions", + contents: ["prebuilt_myjavatests"], +} + java_test_import { name: "myjavatests", prefer: false, @@ -661,6 +714,15 @@ func TestSnapshotWithJavaSystemModules(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: [ + "prebuilt_exported-system-module", + "prebuilt_myjavalib", + "prebuilt_my-system-modules", + ], +} + java_import { name: "exported-system-module", prefer: false, @@ -790,6 +852,11 @@ func TestHostSnapshotWithJavaSystemModules(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_my-system-modules"], +} + java_import { name: "mysdk_system-module", prefer: false, @@ -854,6 +921,15 @@ func TestDeviceAndHostSnapshotWithOsSpecificMembers(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "myexports.contributions", + contents: [ + "prebuilt_hostjavalib", + "prebuilt_androidjavalib", + "prebuilt_myjavalib", + ], +} + java_import { name: "hostjavalib", prefer: false, @@ -920,6 +996,11 @@ func TestSnapshotWithJavaSdkLibrary(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib"], +} + java_sdk_library_import { name: "myjavalib", prefer: false, @@ -993,6 +1074,11 @@ func TestSnapshotWithJavaSdkLibrary_DistStem(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib-foo"], +} + java_sdk_library_import { name: "myjavalib-foo", prefer: false, @@ -1046,6 +1132,11 @@ func TestSnapshotWithJavaSdkLibrary_UseSrcJar(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib"], +} + java_sdk_library_import { name: "myjavalib", prefer: false, @@ -1093,6 +1184,11 @@ func TestSnapshotWithJavaSdkLibrary_AnnotationsZip(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib"], +} + java_sdk_library_import { name: "myjavalib", prefer: false, @@ -1147,6 +1243,11 @@ func TestSnapshotWithJavaSdkLibrary_AnnotationsZip_PreT(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib"], +} + java_sdk_library_import { name: "myjavalib", prefer: false, @@ -1204,6 +1305,11 @@ func TestSnapshotWithJavaSdkLibrary_CompileDex(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib"], +} + java_sdk_library_import { name: "myjavalib", prefer: false, @@ -1272,6 +1378,11 @@ func TestSnapshotWithJavaSdkLibrary_SdkVersion_None(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib"], +} + java_sdk_library_import { name: "myjavalib", prefer: false, @@ -1320,6 +1431,11 @@ func TestSnapshotWithJavaSdkLibrary_SdkVersion_ForScope(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib"], +} + java_sdk_library_import { name: "myjavalib", prefer: false, @@ -1371,6 +1487,11 @@ func TestSnapshotWithJavaSdkLibrary_ApiScopes(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib"], +} + java_sdk_library_import { name: "myjavalib", prefer: false, @@ -1436,6 +1557,11 @@ func TestSnapshotWithJavaSdkLibrary_ModuleLib(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib"], +} + java_sdk_library_import { name: "myjavalib", prefer: false, @@ -1509,6 +1635,11 @@ func TestSnapshotWithJavaSdkLibrary_SystemServer(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib"], +} + java_sdk_library_import { name: "myjavalib", prefer: false, @@ -1569,6 +1700,11 @@ func TestSnapshotWithJavaSdkLibrary_NamingScheme(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib"], +} + java_sdk_library_import { name: "myjavalib", prefer: false, @@ -1626,6 +1762,11 @@ func TestSnapshotWithJavaSdkLibrary_DoctagFiles(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib"], +} + java_sdk_library_import { name: "myjavalib", prefer: false, diff --git a/sdk/license_sdk_test.go b/sdk/license_sdk_test.go index 829edf117..754f01961 100644 --- a/sdk/license_sdk_test.go +++ b/sdk/license_sdk_test.go @@ -69,6 +69,11 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], } +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib"], +} + java_import { name: "myjavalib", prefer: false, diff --git a/sdk/member_trait_test.go b/sdk/member_trait_test.go index 99caf13e3..673d6fb6e 100644 --- a/sdk/member_trait_test.go +++ b/sdk/member_trait_test.go @@ -137,6 +137,11 @@ func TestBasicTrait_WithoutTrait(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib"], +} + java_import { name: "myjavalib", prefer: false, @@ -202,6 +207,17 @@ func TestBasicTrait_MultipleTraits(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: [ + "prebuilt_myjavalib", + "prebuilt_myjavalib_extra", + "prebuilt_myjavalib_special", + "prebuilt_anotherjavalib", + "prebuilt_anotherjavalib_special", + ], +} + java_test_import { name: "myjavalib", prefer: false, diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index c4df146b6..f9d49d98d 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -118,6 +118,16 @@ func TestSnapshotVisibility(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: [ + "prebuilt_myjavalib", + "prebuilt_mypublicjavalib", + "prebuilt_mydefaultedjavalib", + "prebuilt_myprivatejavalib", + ], +} + java_import { name: "myjavalib", prefer: false, @@ -398,6 +408,11 @@ func TestSnapshot_EnvConfiguration(t *testing.T) { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_myjavalib"], +} + java_import { name: "myjavalib", prefer: false, @@ -453,6 +468,11 @@ java_import { checkAndroidBpContents(` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_mysdklibrary"], +} + prebuilt_bootclasspath_fragment { name: "mybootclasspathfragment", prefer: false, diff --git a/sdk/systemserverclasspath_fragment_sdk_test.go b/sdk/systemserverclasspath_fragment_sdk_test.go index 3c0b8ae01..c1c4ed699 100644 --- a/sdk/systemserverclasspath_fragment_sdk_test.go +++ b/sdk/systemserverclasspath_fragment_sdk_test.go @@ -34,6 +34,9 @@ func testSnapshotWithSystemServerClasspathFragment(t *testing.T, sdk string, tar env["SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE"] = targetBuildRelease } }), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_version_active_codenames = []string{"VanillaIceCream"} + }), prepareForSdkTestWithApex, android.FixtureWithRootAndroidBp(sdk+` @@ -242,6 +245,11 @@ sdk { expectedLatestSnapshot := ` // This is auto-generated. DO NOT EDIT. +apex_contributions_defaults { + name: "mysdk.contributions", + contents: ["prebuilt_mysdklibrary"], +} + java_sdk_library_import { name: "mysdklibrary", prefer: false, diff --git a/sdk/update.go b/sdk/update.go index 095e0c276..b8ae38a33 100644 --- a/sdk/update.go +++ b/sdk/update.go @@ -24,6 +24,7 @@ import ( "android/soong/apex" "android/soong/cc" + "android/soong/java" "github.com/google/blueprint" "github.com/google/blueprint/proptools" @@ -89,19 +90,6 @@ type generatedContents struct { indentLevel int } -// generatedFile abstracts operations for writing contents into a file and emit a build rule -// for the file. -type generatedFile struct { - generatedContents - path android.OutputPath -} - -func newGeneratedFile(ctx android.ModuleContext, path ...string) *generatedFile { - return &generatedFile{ - path: android.PathForModuleOut(ctx, path...).OutputPath, - } -} - func (gc *generatedContents) Indent() { gc.indentLevel++ } @@ -122,26 +110,6 @@ func (gc *generatedContents) UnindentedPrintf(format string, args ...interface{} _, _ = fmt.Fprintf(&(gc.content), format, args...) } -func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderContext, implicits android.Paths) { - rb := android.NewRuleBuilder(pctx, ctx) - - content := gf.content.String() - - // ninja consumes newline characters in rspfile_content. Prevent it by - // escaping the backslash in the newline character. The extra backslash - // is removed when the rspfile is written to the actual script file - content = strings.ReplaceAll(content, "\n", "\\n") - - rb.Command(). - Implicits(implicits). - Text("echo -n").Text(proptools.ShellEscape(content)). - // convert \\n to \n - Text("| sed 's/\\\\n/\\n/g' >").Output(gf.path) - rb.Command(). - Text("chmod a+x").Output(gf.path) - rb.Build(gf.path.Base(), "Build "+gf.path.Base()) -} - // Collect all the members. // // Updates the sdk module with a list of sdkMemberVariantDep instances and details as to which @@ -170,7 +138,7 @@ func (s *sdk) collectMembers(ctx android.ModuleContext) { var container android.Module if parent != ctx.Module() { - container = parent.(android.Module) + container = parent } minApiLevel := android.MinApiLevelForSdkSnapshot(ctx, child) @@ -179,7 +147,7 @@ func (s *sdk) collectMembers(ctx android.ModuleContext) { s.memberVariantDeps = append(s.memberVariantDeps, sdkMemberVariantDep{ sdkVariant: s, memberType: memberType, - variant: child.(android.Module), + variant: child, minApiLevel: minApiLevel, container: container, export: export, @@ -375,7 +343,7 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) { snapshotDir := android.PathForModuleOut(ctx, "snapshot") - bp := newGeneratedFile(ctx, "snapshot", "Android.bp") + bp := android.PathForModuleOut(ctx, "snapshot", "Android.bp") bpFile := &bpFile{ modules: make(map[string]*bpModule), @@ -389,7 +357,7 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) { sdk: s, snapshotDir: snapshotDir.OutputPath, copies: make(map[string]string), - filesToZip: []android.Path{bp.path}, + filesToZip: []android.Path{bp}, bpFile: bpFile, prebuiltModules: make(map[string]*bpModule), allMembersByName: allMembersByName, @@ -421,6 +389,7 @@ be unnecessary as every module in the sdk already has its own licenses property. // Create the prebuilt modules for each of the member modules. traits := s.gatherTraits() + memberNames := []string{} // soong module names of the members. contains the prebuilt_ prefix. for _, member := range members { memberType := member.memberType if !memberType.ArePrebuiltsRequired() { @@ -442,6 +411,38 @@ be unnecessary as every module in the sdk already has its own licenses property. prebuiltModule := memberType.AddPrebuiltModule(memberCtx, member) s.createMemberSnapshot(memberCtx, member, prebuiltModule.(*bpModule)) + + if member.memberType != android.LicenseModuleSdkMemberType && !builder.isInternalMember(member.name) { + // More exceptions + // 1. Skip BCP and SCCP fragments + // 2. Skip non-sdk contents of BCP and SCCP fragments + // + // The non-sdk contents of BCP/SSCP fragments should only be used for dexpreopt and hiddenapi, + // and are not available to the rest of the build. + if android.InList(member.memberType, + []android.SdkMemberType{ + // bcp + java.BootclasspathFragmentSdkMemberType, + java.JavaBootLibsSdkMemberType, + // sscp + java.SystemServerClasspathFragmentSdkMemberType, + java.JavaSystemserverLibsSdkMemberType, + }, + ) { + continue + } + + memberNames = append(memberNames, android.PrebuiltNameFromSource(member.name)) + } + } + + // create an apex_contributions_defaults for this module's sdk. + // this module type is supported in V and above. + if targetApiLevel.GreaterThan(android.ApiLevelUpsideDownCake) { + ac := newModule("apex_contributions_defaults") + ac.AddProperty("name", s.Name()+".contributions") + ac.AddProperty("contents", memberNames) + bpFile.AddModule(ac) } // Create a transformer that will transform a module by replacing any references @@ -463,17 +464,14 @@ be unnecessary as every module in the sdk already has its own licenses property. } // generate Android.bp - bp = newGeneratedFile(ctx, "snapshot", "Android.bp") - generateBpContents(&bp.generatedContents, bpFile) - - contents := bp.content.String() + contents := generateBpContents(bpFile) // If the snapshot is being generated for the current build release then check the syntax to make // sure that it is compatible. if targetBuildRelease == buildReleaseCurrent { syntaxCheckSnapshotBpFile(ctx, contents) } - bp.build(pctx, ctx, nil) + android.WriteFileRuleVerbatim(ctx, bp, contents) // Copy the build number file into the snapshot. builder.CopyToSnapshot(ctx.Config().BuildNumberFile(ctx), BUILD_NUMBER_FILE) @@ -522,16 +520,14 @@ be unnecessary as every module in the sdk already has its own licenses property. modules := s.generateInfoData(ctx, memberVariantDeps) // Output the modules information as pretty printed JSON. - info := newGeneratedFile(ctx, fmt.Sprintf("%s%s.info", ctx.ModuleName(), snapshotFileSuffix)) + info := android.PathForModuleOut(ctx, fmt.Sprintf("%s%s.info", ctx.ModuleName(), snapshotFileSuffix)) output, err := json.MarshalIndent(modules, "", " ") if err != nil { ctx.ModuleErrorf("error generating %q: %s", info, err) } builder.infoContents = string(output) - info.generatedContents.UnindentedPrintf("%s", output) - info.build(pctx, ctx, nil) - infoPath := info.path - installedInfo := ctx.InstallFile(android.PathForMainlineSdksInstall(ctx), infoPath.Base(), infoPath) + android.WriteFileRuleVerbatim(ctx, info, builder.infoContents) + installedInfo := ctx.InstallFile(android.PathForMainlineSdksInstall(ctx), info.Base(), info) s.infoFile = android.OptionalPathForPath(installedInfo) // Install the zip, making sure that the info file has been installed as well. @@ -718,105 +714,6 @@ func extractCommonProperties(ctx android.ModuleContext, extractor *commonValueEx } } -// snapshotModuleStaticProperties contains snapshot static (i.e. not dynamically generated) properties. -type snapshotModuleStaticProperties struct { - Compile_multilib string `android:"arch_variant"` -} - -// combinedSnapshotModuleProperties are the properties that are associated with the snapshot module. -type combinedSnapshotModuleProperties struct { - // The sdk variant from which this information was collected. - sdkVariant *sdk - - // Static snapshot module properties. - staticProperties *snapshotModuleStaticProperties - - // The dynamically generated member list properties. - dynamicProperties interface{} -} - -// collateSnapshotModuleInfo collates all the snapshot module info from supplied sdk variants. -func (s *sdk) collateSnapshotModuleInfo(ctx android.BaseModuleContext, sdkVariants []*sdk, memberVariantDeps []sdkMemberVariantDep) []*combinedSnapshotModuleProperties { - sdkVariantToCombinedProperties := map[*sdk]*combinedSnapshotModuleProperties{} - var list []*combinedSnapshotModuleProperties - for _, sdkVariant := range sdkVariants { - staticProperties := &snapshotModuleStaticProperties{ - Compile_multilib: sdkVariant.multilibUsages.String(), - } - dynamicProperties := s.dynamicSdkMemberTypes.createMemberTypeListProperties() - - combinedProperties := &combinedSnapshotModuleProperties{ - sdkVariant: sdkVariant, - staticProperties: staticProperties, - dynamicProperties: dynamicProperties, - } - sdkVariantToCombinedProperties[sdkVariant] = combinedProperties - - list = append(list, combinedProperties) - } - - for _, memberVariantDep := range memberVariantDeps { - // If the member dependency is internal then do not add the dependency to the snapshot member - // list properties. - if !memberVariantDep.export { - continue - } - - combined := sdkVariantToCombinedProperties[memberVariantDep.sdkVariant] - memberListProperty := s.memberTypeListProperty(memberVariantDep.memberType) - memberName := ctx.OtherModuleName(memberVariantDep.variant) - - if memberListProperty.getter == nil { - continue - } - - // Append the member to the appropriate list, if it is not already present in the list. - memberList := memberListProperty.getter(combined.dynamicProperties) - if !android.InList(memberName, memberList) { - memberList = append(memberList, memberName) - } - memberListProperty.setter(combined.dynamicProperties, memberList) - } - - return list -} - -func (s *sdk) optimizeSnapshotModuleProperties(ctx android.ModuleContext, list []*combinedSnapshotModuleProperties) *combinedSnapshotModuleProperties { - - // Extract the dynamic properties and add them to a list of propertiesContainer. - propertyContainers := []propertiesContainer{} - for _, i := range list { - propertyContainers = append(propertyContainers, sdkVariantPropertiesContainer{ - sdkVariant: i.sdkVariant, - properties: i.dynamicProperties, - }) - } - - // Extract the common members, removing them from the original properties. - commonDynamicProperties := s.dynamicSdkMemberTypes.createMemberTypeListProperties() - extractor := newCommonValueExtractor(commonDynamicProperties) - extractCommonProperties(ctx, extractor, commonDynamicProperties, propertyContainers) - - // Extract the static properties and add them to a list of propertiesContainer. - propertyContainers = []propertiesContainer{} - for _, i := range list { - propertyContainers = append(propertyContainers, sdkVariantPropertiesContainer{ - sdkVariant: i.sdkVariant, - properties: i.staticProperties, - }) - } - - commonStaticProperties := &snapshotModuleStaticProperties{} - extractor = newCommonValueExtractor(commonStaticProperties) - extractCommonProperties(ctx, extractor, &commonStaticProperties, propertyContainers) - - return &combinedSnapshotModuleProperties{ - sdkVariant: nil, - staticProperties: commonStaticProperties, - dynamicProperties: commonDynamicProperties, - } -} - type propertyTag struct { name string } @@ -885,7 +782,8 @@ func (t pruneEmptySetTransformer) transformPropertySetAfterContents(_ string, pr } } -func generateBpContents(contents *generatedContents, bpFile *bpFile) { +func generateBpContents(bpFile *bpFile) string { + contents := &generatedContents{} contents.IndentedPrintf("// This is auto-generated. DO NOT EDIT.\n") for _, bpModule := range bpFile.order { contents.IndentedPrintf("\n") @@ -893,6 +791,7 @@ func generateBpContents(contents *generatedContents, bpFile *bpFile) { outputPropertySet(contents, bpModule.bpPropertySet) contents.IndentedPrintf("}\n") } + return contents.content.String() } func outputPropertySet(contents *generatedContents, set *bpPropertySet) { @@ -1007,7 +906,7 @@ func outputUnnamedValue(contents *generatedContents, value reflect.Value) { contents.IndentedPrintf("}") default: - panic(fmt.Errorf("Unknown type: %T of value %#v", value, value)) + panic(fmt.Errorf("unknown type: %T of value %#v", value, value)) } } @@ -1018,9 +917,7 @@ func multiLineValue(value reflect.Value) bool { } func (s *sdk) GetAndroidBpContentsForTests() string { - contents := &generatedContents{} - generateBpContents(contents, s.builderForTests.bpFile) - return contents.content.String() + return generateBpContents(s.builderForTests.bpFile) } func (s *sdk) GetInfoContentsForTests() string { @@ -1334,7 +1231,7 @@ func (m multilibUsage) addArchType(archType android.ArchType) multilibUsage { case "lib64": return m | multilib64 default: - panic(fmt.Errorf("Unknown Multilib field in ArchType, expected 'lib32' or 'lib64', found %q", multilib)) + panic(fmt.Errorf("unknown Multilib field in ArchType, expected 'lib32' or 'lib64', found %q", multilib)) } } @@ -1349,7 +1246,7 @@ func (m multilibUsage) String() string { case multilibBoth: return "both" default: - panic(fmt.Errorf("Unknown multilib value, found %b, expected one of %b, %b, %b or %b", + panic(fmt.Errorf("unknown multilib value, found %b, expected one of %b, %b, %b or %b", m, multilibNone, multilib32, multilib64, multilibBoth)) } } @@ -2302,20 +2199,6 @@ type propertiesContainer interface { optimizableProperties() interface{} } -// A wrapper for sdk variant related properties to allow them to be optimized. -type sdkVariantPropertiesContainer struct { - sdkVariant *sdk - properties interface{} -} - -func (c sdkVariantPropertiesContainer) optimizableProperties() interface{} { - return c.properties -} - -func (c sdkVariantPropertiesContainer) String() string { - return c.sdkVariant.String() -} - // Extract common properties from a slice of property structures of the same type. // // All the property structures must be of the same type. diff --git a/starlark_import/README.md b/starlark_import/README.md deleted file mode 100644 index e444759d0..000000000 --- a/starlark_import/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# starlark_import package - -This allows soong to read constant information from starlark files. At package initialization -time, soong will read `build/bazel/constants_exported_to_soong.bzl`, and then make the -variables from that file available via `starlark_import.GetStarlarkValue()`. So to import -a new variable, it must be added to `constants_exported_to_soong.bzl` and then it can -be accessed by name. - -Only constant information can be read, since this is not a full bazel execution but a -standalone starlark interpreter. This means you can't use bazel contructs like `rule`, -`provider`, `select`, `glob`, etc. - -All starlark files that were loaded must be added as ninja deps that cause soong to rerun. -The loaded files can be retrieved via `starlark_import.GetNinjaDeps()`. diff --git a/starlark_import/starlark_import.go b/starlark_import/starlark_import.go deleted file mode 100644 index ebe42472c..000000000 --- a/starlark_import/starlark_import.go +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2023 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 starlark_import - -import ( - "fmt" - "os" - "path/filepath" - "sort" - "strings" - "sync" - "time" - - "go.starlark.net/starlark" - "go.starlark.net/starlarkjson" - "go.starlark.net/starlarkstruct" -) - -func init() { - go func() { - startTime := time.Now() - v, d, err := runStarlarkFile("//build/bazel/constants_exported_to_soong.bzl") - endTime := time.Now() - //fmt.Fprintf(os.Stderr, "starlark run time: %s\n", endTime.Sub(startTime).String()) - globalResult.Set(starlarkResult{ - values: v, - ninjaDeps: d, - err: err, - startTime: startTime, - endTime: endTime, - }) - }() -} - -type starlarkResult struct { - values starlark.StringDict - ninjaDeps []string - err error - startTime time.Time - endTime time.Time -} - -// setOnce wraps a value and exposes Set() and Get() accessors for it. -// The Get() calls will block until a Set() has been called. -// A second call to Set() will panic. -// setOnce must be created using newSetOnce() -type setOnce[T any] struct { - value T - lock sync.Mutex - wg sync.WaitGroup - isSet bool -} - -func (o *setOnce[T]) Set(value T) { - o.lock.Lock() - defer o.lock.Unlock() - if o.isSet { - panic("Value already set") - } - - o.value = value - o.isSet = true - o.wg.Done() -} - -func (o *setOnce[T]) Get() T { - if !o.isSet { - o.wg.Wait() - } - return o.value -} - -func newSetOnce[T any]() *setOnce[T] { - result := &setOnce[T]{} - result.wg.Add(1) - return result -} - -var globalResult = newSetOnce[starlarkResult]() - -func GetStarlarkValue[T any](key string) (T, error) { - result := globalResult.Get() - if result.err != nil { - var zero T - return zero, result.err - } - if !result.values.Has(key) { - var zero T - return zero, fmt.Errorf("a starlark variable by that name wasn't found, did you update //build/bazel/constants_exported_to_soong.bzl?") - } - return Unmarshal[T](result.values[key]) -} - -func GetNinjaDeps() ([]string, error) { - result := globalResult.Get() - if result.err != nil { - return nil, result.err - } - return result.ninjaDeps, nil -} - -func getTopDir() (string, error) { - // It's hard to communicate the top dir to this package in any other way than reading the - // arguments directly, because we need to know this at package initialization time. Many - // soong constants that we'd like to read from starlark are initialized during package - // initialization. - for i, arg := range os.Args { - if arg == "--top" { - if i < len(os.Args)-1 && os.Args[i+1] != "" { - return os.Args[i+1], nil - } - } - } - - // When running tests, --top is not passed. Instead, search for the top dir manually - cwd, err := os.Getwd() - if err != nil { - return "", err - } - for cwd != "/" { - if _, err := os.Stat(filepath.Join(cwd, "build/soong/soong_ui.bash")); err == nil { - return cwd, nil - } - cwd = filepath.Dir(cwd) - } - return "", fmt.Errorf("could not find top dir") -} - -const callerDirKey = "callerDir" - -type modentry struct { - globals starlark.StringDict - err error -} - -func unsupportedMethod(t *starlark.Thread, fn *starlark.Builtin, _ starlark.Tuple, _ []starlark.Tuple) (starlark.Value, error) { - return nil, fmt.Errorf("%sthis file is read by soong, and must therefore be pure starlark and include only constant information. %q is not allowed", t.CallStack().String(), fn.Name()) -} - -var builtins = starlark.StringDict{ - "aspect": starlark.NewBuiltin("aspect", unsupportedMethod), - "glob": starlark.NewBuiltin("glob", unsupportedMethod), - "json": starlarkjson.Module, - "provider": starlark.NewBuiltin("provider", unsupportedMethod), - "rule": starlark.NewBuiltin("rule", unsupportedMethod), - "struct": starlark.NewBuiltin("struct", starlarkstruct.Make), - "select": starlark.NewBuiltin("select", unsupportedMethod), - "transition": starlark.NewBuiltin("transition", unsupportedMethod), -} - -// Takes a module name (the first argument to the load() function) and returns the path -// it's trying to load, stripping out leading //, and handling leading :s. -func cleanModuleName(moduleName string, callerDir string) (string, error) { - if strings.Count(moduleName, ":") > 1 { - return "", fmt.Errorf("at most 1 colon must be present in starlark path: %s", moduleName) - } - - // We don't have full support for external repositories, but at least support skylib's dicts. - if moduleName == "@bazel_skylib//lib:dicts.bzl" { - return "external/bazel-skylib/lib/dicts.bzl", nil - } - - localLoad := false - if strings.HasPrefix(moduleName, "@//") { - moduleName = moduleName[3:] - } else if strings.HasPrefix(moduleName, "//") { - moduleName = moduleName[2:] - } else if strings.HasPrefix(moduleName, ":") { - moduleName = moduleName[1:] - localLoad = true - } else { - return "", fmt.Errorf("load path must start with // or :") - } - - if ix := strings.LastIndex(moduleName, ":"); ix >= 0 { - moduleName = moduleName[:ix] + string(os.PathSeparator) + moduleName[ix+1:] - } - - if filepath.Clean(moduleName) != moduleName { - return "", fmt.Errorf("load path must be clean, found: %s, expected: %s", moduleName, filepath.Clean(moduleName)) - } - if strings.HasPrefix(moduleName, "../") { - return "", fmt.Errorf("load path must not start with ../: %s", moduleName) - } - if strings.HasPrefix(moduleName, "/") { - return "", fmt.Errorf("load path starts with /, use // for a absolute path: %s", moduleName) - } - - if localLoad { - return filepath.Join(callerDir, moduleName), nil - } - - return moduleName, nil -} - -// loader implements load statement. The format of the loaded module URI is -// -// [//path]:base -// -// The file path is $ROOT/path/base if path is present, <caller_dir>/base otherwise. -func loader(thread *starlark.Thread, module string, topDir string, moduleCache map[string]*modentry, moduleCacheLock *sync.Mutex, filesystem map[string]string) (starlark.StringDict, error) { - modulePath, err := cleanModuleName(module, thread.Local(callerDirKey).(string)) - if err != nil { - return nil, err - } - moduleCacheLock.Lock() - e, ok := moduleCache[modulePath] - if e == nil { - if ok { - moduleCacheLock.Unlock() - return nil, fmt.Errorf("cycle in load graph") - } - - // Add a placeholder to indicate "load in progress". - moduleCache[modulePath] = nil - moduleCacheLock.Unlock() - - childThread := &starlark.Thread{Name: "exec " + module, Load: thread.Load} - - // Cheating for the sake of testing: - // propagate starlarktest's Reporter key, otherwise testing - // the load function may cause panic in starlarktest code. - const testReporterKey = "Reporter" - if v := thread.Local(testReporterKey); v != nil { - childThread.SetLocal(testReporterKey, v) - } - - childThread.SetLocal(callerDirKey, filepath.Dir(modulePath)) - - if filesystem != nil { - globals, err := starlark.ExecFile(childThread, filepath.Join(topDir, modulePath), filesystem[modulePath], builtins) - e = &modentry{globals, err} - } else { - globals, err := starlark.ExecFile(childThread, filepath.Join(topDir, modulePath), nil, builtins) - e = &modentry{globals, err} - } - - // Update the cache. - moduleCacheLock.Lock() - moduleCache[modulePath] = e - } - moduleCacheLock.Unlock() - return e.globals, e.err -} - -// Run runs the given starlark file and returns its global variables and a list of all starlark -// files that were loaded. The top dir for starlark's // is found via getTopDir(). -func runStarlarkFile(filename string) (starlark.StringDict, []string, error) { - topDir, err := getTopDir() - if err != nil { - return nil, nil, err - } - return runStarlarkFileWithFilesystem(filename, topDir, nil) -} - -func runStarlarkFileWithFilesystem(filename string, topDir string, filesystem map[string]string) (starlark.StringDict, []string, error) { - if !strings.HasPrefix(filename, "//") && !strings.HasPrefix(filename, ":") { - filename = "//" + filename - } - filename, err := cleanModuleName(filename, "") - if err != nil { - return nil, nil, err - } - moduleCache := make(map[string]*modentry) - moduleCache[filename] = nil - moduleCacheLock := &sync.Mutex{} - mainThread := &starlark.Thread{ - Name: "main", - Print: func(_ *starlark.Thread, msg string) { - // Ignore prints - }, - Load: func(thread *starlark.Thread, module string) (starlark.StringDict, error) { - return loader(thread, module, topDir, moduleCache, moduleCacheLock, filesystem) - }, - } - mainThread.SetLocal(callerDirKey, filepath.Dir(filename)) - - var result starlark.StringDict - if filesystem != nil { - result, err = starlark.ExecFile(mainThread, filepath.Join(topDir, filename), filesystem[filename], builtins) - } else { - result, err = starlark.ExecFile(mainThread, filepath.Join(topDir, filename), nil, builtins) - } - return result, sortedStringKeys(moduleCache), err -} - -func sortedStringKeys(m map[string]*modentry) []string { - s := make([]string, 0, len(m)) - for k := range m { - s = append(s, k) - } - sort.Strings(s) - return s -} diff --git a/starlark_import/starlark_import_test.go b/starlark_import/starlark_import_test.go deleted file mode 100644 index 8a58e3b3d..000000000 --- a/starlark_import/starlark_import_test.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2023 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 starlark_import - -import ( - "strings" - "testing" - - "go.starlark.net/starlark" -) - -func TestBasic(t *testing.T) { - globals, _, err := runStarlarkFileWithFilesystem("a.bzl", "", map[string]string{ - "a.bzl": ` -my_string = "hello, world!" -`}) - if err != nil { - t.Error(err) - return - } - - if globals["my_string"].(starlark.String) != "hello, world!" { - t.Errorf("Expected %q, got %q", "hello, world!", globals["my_string"].String()) - } -} - -func TestLoad(t *testing.T) { - globals, _, err := runStarlarkFileWithFilesystem("a.bzl", "", map[string]string{ - "a.bzl": ` -load("//b.bzl", _b_string = "my_string") -my_string = "hello, " + _b_string -`, - "b.bzl": ` -my_string = "world!" -`}) - if err != nil { - t.Error(err) - return - } - - if globals["my_string"].(starlark.String) != "hello, world!" { - t.Errorf("Expected %q, got %q", "hello, world!", globals["my_string"].String()) - } -} - -func TestLoadRelative(t *testing.T) { - globals, ninjaDeps, err := runStarlarkFileWithFilesystem("a.bzl", "", map[string]string{ - "a.bzl": ` -load(":b.bzl", _b_string = "my_string") -load("//foo/c.bzl", _c_string = "my_string") -my_string = "hello, " + _b_string -c_string = _c_string -`, - "b.bzl": ` -my_string = "world!" -`, - "foo/c.bzl": ` -load(":d.bzl", _d_string = "my_string") -my_string = "hello, " + _d_string -`, - "foo/d.bzl": ` -my_string = "world!" -`}) - if err != nil { - t.Error(err) - return - } - - if globals["my_string"].(starlark.String) != "hello, world!" { - t.Errorf("Expected %q, got %q", "hello, world!", globals["my_string"].String()) - } - - expectedNinjaDeps := []string{ - "a.bzl", - "b.bzl", - "foo/c.bzl", - "foo/d.bzl", - } - if !slicesEqual(ninjaDeps, expectedNinjaDeps) { - t.Errorf("Expected %v ninja deps, got %v", expectedNinjaDeps, ninjaDeps) - } -} - -func TestLoadCycle(t *testing.T) { - _, _, err := runStarlarkFileWithFilesystem("a.bzl", "", map[string]string{ - "a.bzl": ` -load(":b.bzl", _b_string = "my_string") -my_string = "hello, " + _b_string -`, - "b.bzl": ` -load(":a.bzl", _a_string = "my_string") -my_string = "hello, " + _a_string -`}) - if err == nil || !strings.Contains(err.Error(), "cycle in load graph") { - t.Errorf("Expected cycle in load graph, got: %v", err) - return - } -} - -func slicesEqual[T comparable](a []T, b []T) bool { - if len(a) != len(b) { - return false - } - for i := range a { - if a[i] != b[i] { - return false - } - } - return true -} diff --git a/starlark_import/unmarshal.go b/starlark_import/unmarshal.go deleted file mode 100644 index b2434712b..000000000 --- a/starlark_import/unmarshal.go +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright 2023 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 starlark_import - -import ( - "fmt" - "math" - "reflect" - "unsafe" - - "go.starlark.net/starlark" - "go.starlark.net/starlarkstruct" -) - -func Unmarshal[T any](value starlark.Value) (T, error) { - x, err := UnmarshalReflect(value, reflect.TypeOf((*T)(nil)).Elem()) - return x.Interface().(T), err -} - -func UnmarshalReflect(value starlark.Value, ty reflect.Type) (reflect.Value, error) { - if ty == reflect.TypeOf((*starlark.Value)(nil)).Elem() { - return reflect.ValueOf(value), nil - } - zero := reflect.Zero(ty) - if value == nil { - panic("nil value") - } - var result reflect.Value - if ty.Kind() == reflect.Interface { - var err error - ty, err = typeOfStarlarkValue(value) - if err != nil { - return zero, err - } - } - if ty.Kind() == reflect.Map { - result = reflect.MakeMap(ty) - } else { - result = reflect.Indirect(reflect.New(ty)) - } - - switch v := value.(type) { - case starlark.String: - if result.Type().Kind() != reflect.String { - return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String()) - } - result.SetString(v.GoString()) - case starlark.Int: - signedValue, signedOk := v.Int64() - unsignedValue, unsignedOk := v.Uint64() - switch result.Type().Kind() { - case reflect.Int64: - if !signedOk { - return zero, fmt.Errorf("starlark int didn't fit in go int64") - } - result.SetInt(signedValue) - case reflect.Int32: - if !signedOk || signedValue > math.MaxInt32 || signedValue < math.MinInt32 { - return zero, fmt.Errorf("starlark int didn't fit in go int32") - } - result.SetInt(signedValue) - case reflect.Int16: - if !signedOk || signedValue > math.MaxInt16 || signedValue < math.MinInt16 { - return zero, fmt.Errorf("starlark int didn't fit in go int16") - } - result.SetInt(signedValue) - case reflect.Int8: - if !signedOk || signedValue > math.MaxInt8 || signedValue < math.MinInt8 { - return zero, fmt.Errorf("starlark int didn't fit in go int8") - } - result.SetInt(signedValue) - case reflect.Int: - if !signedOk || signedValue > math.MaxInt || signedValue < math.MinInt { - return zero, fmt.Errorf("starlark int didn't fit in go int") - } - result.SetInt(signedValue) - case reflect.Uint64: - if !unsignedOk { - return zero, fmt.Errorf("starlark int didn't fit in go uint64") - } - result.SetUint(unsignedValue) - case reflect.Uint32: - if !unsignedOk || unsignedValue > math.MaxUint32 { - return zero, fmt.Errorf("starlark int didn't fit in go uint32") - } - result.SetUint(unsignedValue) - case reflect.Uint16: - if !unsignedOk || unsignedValue > math.MaxUint16 { - return zero, fmt.Errorf("starlark int didn't fit in go uint16") - } - result.SetUint(unsignedValue) - case reflect.Uint8: - if !unsignedOk || unsignedValue > math.MaxUint8 { - return zero, fmt.Errorf("starlark int didn't fit in go uint8") - } - result.SetUint(unsignedValue) - case reflect.Uint: - if !unsignedOk || unsignedValue > math.MaxUint { - return zero, fmt.Errorf("starlark int didn't fit in go uint") - } - result.SetUint(unsignedValue) - default: - return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String()) - } - case starlark.Float: - f := float64(v) - switch result.Type().Kind() { - case reflect.Float64: - result.SetFloat(f) - case reflect.Float32: - if f > math.MaxFloat32 || f < -math.MaxFloat32 { - return zero, fmt.Errorf("starlark float didn't fit in go float32") - } - result.SetFloat(f) - default: - return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String()) - } - case starlark.Bool: - if result.Type().Kind() != reflect.Bool { - return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String()) - } - result.SetBool(bool(v)) - case starlark.Tuple: - if result.Type().Kind() != reflect.Slice { - return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String()) - } - elemType := result.Type().Elem() - // TODO: Add this grow call when we're on go 1.20 - //result.Grow(v.Len()) - for i := 0; i < v.Len(); i++ { - elem, err := UnmarshalReflect(v.Index(i), elemType) - if err != nil { - return zero, err - } - result = reflect.Append(result, elem) - } - case *starlark.List: - if result.Type().Kind() != reflect.Slice { - return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String()) - } - elemType := result.Type().Elem() - // TODO: Add this grow call when we're on go 1.20 - //result.Grow(v.Len()) - for i := 0; i < v.Len(); i++ { - elem, err := UnmarshalReflect(v.Index(i), elemType) - if err != nil { - return zero, err - } - result = reflect.Append(result, elem) - } - case *starlark.Dict: - if result.Type().Kind() != reflect.Map { - return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String()) - } - keyType := result.Type().Key() - valueType := result.Type().Elem() - for _, pair := range v.Items() { - key := pair.Index(0) - value := pair.Index(1) - - unmarshalledKey, err := UnmarshalReflect(key, keyType) - if err != nil { - return zero, err - } - unmarshalledValue, err := UnmarshalReflect(value, valueType) - if err != nil { - return zero, err - } - - result.SetMapIndex(unmarshalledKey, unmarshalledValue) - } - case *starlarkstruct.Struct: - if result.Type().Kind() != reflect.Struct { - return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String()) - } - if result.NumField() != len(v.AttrNames()) { - return zero, fmt.Errorf("starlark struct and go struct have different number of fields (%d and %d)", len(v.AttrNames()), result.NumField()) - } - for _, attrName := range v.AttrNames() { - attr, err := v.Attr(attrName) - if err != nil { - return zero, err - } - - // TODO(b/279787235): this should probably support tags to rename the field - resultField := result.FieldByName(attrName) - if resultField == (reflect.Value{}) { - return zero, fmt.Errorf("starlark struct had field %s, but requested struct type did not", attrName) - } - // This hack allows us to change unexported fields - resultField = reflect.NewAt(resultField.Type(), unsafe.Pointer(resultField.UnsafeAddr())).Elem() - x, err := UnmarshalReflect(attr, resultField.Type()) - if err != nil { - return zero, err - } - resultField.Set(x) - } - default: - return zero, fmt.Errorf("unimplemented starlark type: %s", value.Type()) - } - - return result, nil -} - -func typeOfStarlarkValue(value starlark.Value) (reflect.Type, error) { - var err error - switch v := value.(type) { - case starlark.String: - return reflect.TypeOf(""), nil - case *starlark.List: - innerType := reflect.TypeOf("") - if v.Len() > 0 { - innerType, err = typeOfStarlarkValue(v.Index(0)) - if err != nil { - return nil, err - } - } - for i := 1; i < v.Len(); i++ { - innerTypeI, err := typeOfStarlarkValue(v.Index(i)) - if err != nil { - return nil, err - } - if innerType != innerTypeI { - return nil, fmt.Errorf("List must contain elements of entirely the same type, found %v and %v", innerType, innerTypeI) - } - } - return reflect.SliceOf(innerType), nil - case *starlark.Dict: - keyType := reflect.TypeOf("") - valueType := reflect.TypeOf("") - keys := v.Keys() - if v.Len() > 0 { - firstKey := keys[0] - keyType, err = typeOfStarlarkValue(firstKey) - if err != nil { - return nil, err - } - firstValue, found, err := v.Get(firstKey) - if !found { - err = fmt.Errorf("value not found") - } - if err != nil { - return nil, err - } - valueType, err = typeOfStarlarkValue(firstValue) - if err != nil { - return nil, err - } - } - for _, key := range keys { - keyTypeI, err := typeOfStarlarkValue(key) - if err != nil { - return nil, err - } - if keyType != keyTypeI { - return nil, fmt.Errorf("dict must contain elements of entirely the same type, found %v and %v", keyType, keyTypeI) - } - value, found, err := v.Get(key) - if !found { - err = fmt.Errorf("value not found") - } - if err != nil { - return nil, err - } - valueTypeI, err := typeOfStarlarkValue(value) - if valueType.Kind() != reflect.Interface && valueTypeI != valueType { - // If we see conflicting value types, change the result value type to an empty interface - valueType = reflect.TypeOf([]interface{}{}).Elem() - } - } - return reflect.MapOf(keyType, valueType), nil - case starlark.Int: - return reflect.TypeOf(0), nil - case starlark.Float: - return reflect.TypeOf(0.0), nil - case starlark.Bool: - return reflect.TypeOf(true), nil - default: - return nil, fmt.Errorf("unimplemented starlark type: %s", value.Type()) - } -} - -// UnmarshalNoneable is like Unmarshal, but it will accept None as the top level (but not nested) -// starlark value. If the value is None, a nil pointer will be returned, otherwise a pointer -// to the result of Unmarshal will be returned. -func UnmarshalNoneable[T any](value starlark.Value) (*T, error) { - if _, ok := value.(starlark.NoneType); ok { - return nil, nil - } - ret, err := Unmarshal[T](value) - return &ret, err -} diff --git a/starlark_import/unmarshal_test.go b/starlark_import/unmarshal_test.go deleted file mode 100644 index bc0ea4c49..000000000 --- a/starlark_import/unmarshal_test.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2023 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 starlark_import - -import ( - "reflect" - "testing" - - "go.starlark.net/starlark" -) - -func createStarlarkValue(t *testing.T, code string) starlark.Value { - t.Helper() - result, err := starlark.ExecFile(&starlark.Thread{}, "main.bzl", "x = "+code, builtins) - if err != nil { - panic(err) - } - return result["x"] -} - -func TestUnmarshalConcreteType(t *testing.T) { - x, err := Unmarshal[string](createStarlarkValue(t, `"foo"`)) - if err != nil { - t.Error(err) - return - } - if x != "foo" { - t.Errorf(`Expected "foo", got %q`, x) - } -} - -func TestUnmarshalConcreteTypeWithInterfaces(t *testing.T) { - x, err := Unmarshal[map[string]map[string]interface{}](createStarlarkValue(t, - `{"foo": {"foo2": "foo3"}, "bar": {"bar2": ["bar3"]}}`)) - if err != nil { - t.Error(err) - return - } - expected := map[string]map[string]interface{}{ - "foo": {"foo2": "foo3"}, - "bar": {"bar2": []string{"bar3"}}, - } - if !reflect.DeepEqual(x, expected) { - t.Errorf(`Expected %v, got %v`, expected, x) - } -} - -func TestUnmarshalToStarlarkValue(t *testing.T) { - x, err := Unmarshal[map[string]starlark.Value](createStarlarkValue(t, - `{"foo": "Hi", "bar": None}`)) - if err != nil { - t.Error(err) - return - } - if x["foo"].(starlark.String).GoString() != "Hi" { - t.Errorf("Expected \"Hi\", got: %q", x["foo"].(starlark.String).GoString()) - } - if x["bar"].Type() != "NoneType" { - t.Errorf("Expected \"NoneType\", got: %q", x["bar"].Type()) - } -} - -func TestUnmarshal(t *testing.T) { - testCases := []struct { - input string - expected interface{} - }{ - { - input: `"foo"`, - expected: "foo", - }, - { - input: `5`, - expected: 5, - }, - { - input: `["foo", "bar"]`, - expected: []string{"foo", "bar"}, - }, - { - input: `("foo", "bar")`, - expected: []string{"foo", "bar"}, - }, - { - input: `("foo",5)`, - expected: []interface{}{"foo", 5}, - }, - { - input: `{"foo": 5, "bar": 10}`, - expected: map[string]int{"foo": 5, "bar": 10}, - }, - { - input: `{"foo": ["qux"], "bar": []}`, - expected: map[string][]string{"foo": {"qux"}, "bar": nil}, - }, - { - input: `struct(Foo="foo", Bar=5)`, - expected: struct { - Foo string - Bar int - }{Foo: "foo", Bar: 5}, - }, - { - // Unexported fields version of the above - input: `struct(foo="foo", bar=5)`, - expected: struct { - foo string - bar int - }{foo: "foo", bar: 5}, - }, - { - input: `{"foo": "foo2", "bar": ["bar2"], "baz": 5, "qux": {"qux2": "qux3"}, "quux": {"quux2": "quux3", "quux4": 5}}`, - expected: map[string]interface{}{ - "foo": "foo2", - "bar": []string{"bar2"}, - "baz": 5, - "qux": map[string]string{"qux2": "qux3"}, - "quux": map[string]interface{}{ - "quux2": "quux3", - "quux4": 5, - }, - }, - }, - } - - for _, tc := range testCases { - x, err := UnmarshalReflect(createStarlarkValue(t, tc.input), reflect.TypeOf(tc.expected)) - if err != nil { - t.Error(err) - continue - } - if !reflect.DeepEqual(x.Interface(), tc.expected) { - t.Errorf(`Expected %#v, got %#v`, tc.expected, x.Interface()) - } - } -} diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go index 22582327e..b9b68be2d 100644 --- a/sysprop/sysprop_library.go +++ b/sysprop/sysprop_library.go @@ -54,15 +54,13 @@ type syspropJavaGenRule struct { } type syspropRustGenRule struct { - android.ModuleBase - - properties syspropGenProperties + *rust.BaseSourceProvider - genSrcs android.Paths + properties rustLibraryProperties } var _ android.OutputFileProducer = (*syspropJavaGenRule)(nil) -var _ android.OutputFileProducer = (*syspropRustGenRule)(nil) +var _ rust.SourceProvider = (*syspropRustGenRule)(nil) var ( syspropJava = pctx.AndroidStaticRule("syspropJava", @@ -144,7 +142,7 @@ func syspropJavaGenFactory() android.Module { // syspropRustGenRule module generates rust source files containing generated rust APIs. // It also depends on check api rule, so api check has to pass to use sysprop_library. -func (g *syspropRustGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) { +func (g *syspropRustGenRule) GenerateSource(ctx rust.ModuleContext, deps rust.PathDeps) android.Path { var checkApiFileTimeStamp android.WritablePath ctx.VisitDirectDeps(func(dep android.Module) { @@ -153,26 +151,47 @@ func (g *syspropRustGenRule) GenerateAndroidBuildActions(ctx android.ModuleConte } }) - for _, syspropFile := range android.PathsForModuleSrc(ctx, g.properties.Srcs) { - syspropDir := strings.TrimSuffix(syspropFile.String(), syspropFile.Ext()) - outputDir := android.PathForModuleGen(ctx, syspropDir, "src") - libPath := android.PathForModuleGen(ctx, syspropDir, "src", "lib.rs") - parsersPath := android.PathForModuleGen(ctx, syspropDir, "src", "gen_parsers_and_formatters.rs") + outputDir := android.PathForModuleOut(ctx, "src") + libFile := outputDir.Join(ctx, "lib.rs") + g.BaseSourceProvider.OutputFiles = append(g.BaseSourceProvider.OutputFiles, libFile) + libFileLines := []string{"//! Autogenerated system property accessors."} + + for _, syspropFile := range android.PathsForModuleSrc(ctx, g.properties.Sysprop_srcs) { + moduleName := syspropPathToRustModule(syspropFile) + moduleDir := outputDir.Join(ctx, moduleName) + modulePath := moduleDir.Join(ctx, "mod.rs") ctx.Build(pctx, android.BuildParams{ Rule: syspropRust, Description: "sysprop_rust " + syspropFile.Rel(), - Outputs: android.WritablePaths{libPath, parsersPath}, + Output: modulePath, Input: syspropFile, Implicit: checkApiFileTimeStamp, Args: map[string]string{ "scope": g.properties.Scope, - "out_dir": outputDir.String(), + "out_dir": moduleDir.String(), }, }) - g.genSrcs = append(g.genSrcs, libPath, parsersPath) + g.BaseSourceProvider.OutputFiles = append(g.BaseSourceProvider.OutputFiles, modulePath) + libFileLines = append(libFileLines, fmt.Sprintf("pub mod %s;", moduleName)) } + + libFileSource := strings.Join(libFileLines, "\n") + android.WriteFileRule(ctx, libFile, libFileSource) + + return libFile +} + +func (g *syspropRustGenRule) SourceProviderProps() []interface{} { + return append(g.BaseSourceProvider.SourceProviderProps(), &g.Properties) +} + +// syspropPathToRustModule takes a path to a .sysprop file and returns the name to use for the +// corresponding Rust module. +func syspropPathToRustModule(syspropFilename android.Path) string { + filenameBase := strings.TrimSuffix(syspropFilename.Base(), ".sysprop") + return strings.ToLower(filenameBase) } func (g *syspropRustGenRule) DepsMutator(ctx android.BottomUpMutatorContext) { @@ -181,15 +200,13 @@ func (g *syspropRustGenRule) DepsMutator(ctx android.BottomUpMutatorContext) { ctx.AddFarVariationDependencies(nil, nil, proptools.String(g.properties.Check_api)) } -func (g *syspropRustGenRule) OutputFiles(_ string) (android.Paths, error) { - return g.genSrcs, nil -} - func syspropRustGenFactory() android.Module { - g := &syspropRustGenRule{} - g.AddProperties(&g.properties) - android.InitAndroidModule(g) - return g + g := &syspropRustGenRule{ + BaseSourceProvider: rust.NewSourceProvider(), + } + sourceProvider := rust.NewSourceProviderModule(android.DeviceSupported, g, false, false) + sourceProvider.AddProperties(&g.properties) + return sourceProvider.Init() } type syspropLibrary struct { @@ -309,10 +326,6 @@ func (m *syspropLibrary) javaGenPublicStubName() string { return m.BaseModuleName() + "_java_gen_public" } -func (m *syspropLibrary) rustGenModuleName() string { - return m.rustCrateName() + "_rust_gen" -} - func (m *syspropLibrary) rustGenStubName() string { return "lib" + m.rustCrateName() + "_rust" } @@ -529,6 +542,9 @@ type javaLibraryProperties struct { type rustLibraryProperties struct { Name *string + Sysprop_srcs []string `android:"path"` + Scope string + Check_api *string Srcs []string Installable *bool Crate_name string @@ -668,18 +684,15 @@ func syspropLibraryHook(ctx android.LoadHookContext, m *syspropLibrary) { } // Generate a Rust implementation library. - ctx.CreateModule(syspropRustGenFactory, &syspropGenProperties{ - Srcs: m.properties.Srcs, - Scope: scope, - Name: proptools.StringPtr(m.rustGenModuleName()), - Check_api: proptools.StringPtr(ctx.ModuleName()), - }) rustProps := rustLibraryProperties{ - Name: proptools.StringPtr(m.rustGenStubName()), - Srcs: []string{":" + m.rustGenModuleName()}, - Installable: proptools.BoolPtr(false), - Crate_name: m.rustCrateName(), + Name: proptools.StringPtr(m.rustGenStubName()), + Sysprop_srcs: m.properties.Srcs, + Scope: scope, + Check_api: proptools.StringPtr(ctx.ModuleName()), + Installable: proptools.BoolPtr(false), + Crate_name: m.rustCrateName(), Rustlibs: []string{ + "liblog_rust", "librustutils", }, Vendor_available: m.properties.Vendor_available, @@ -687,7 +700,7 @@ func syspropLibraryHook(ctx android.LoadHookContext, m *syspropLibrary) { Apex_available: m.ApexProperties.Apex_available, Min_sdk_version: proptools.StringPtr("29"), } - ctx.CreateModule(rust.RustLibraryFactory, &rustProps) + ctx.CreateModule(syspropRustGenFactory, &rustProps) // syspropLibraries will be used by property_contexts to check types. // Record absolute paths of sysprop_library to prevent soong_namespace problem. diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go index 9dd696f75..dfbbe7dd9 100644 --- a/sysprop/sysprop_test.go +++ b/sysprop/sysprop_test.go @@ -72,6 +72,15 @@ func test(t *testing.T, bp string) *android.TestResult { vendor_available: true, min_sdk_version: "29", } + + rust_library { + name: "liblog_rust", + crate_name: "log", + srcs: ["log/src/lib.rs"], + product_available: true, + vendor_available: true, + min_sdk_version: "29", + } ` mockFS := android.MockFS{ @@ -115,6 +124,7 @@ func test(t *testing.T, bp string) *android.TestResult { "com/android2/OdmProperties.sysprop": nil, "librustutils/lib.rs": nil, + "log/src/lib.rs": nil, } result := android.GroupFixturePreparers( diff --git a/tradefed/Android.bp b/tradefed/Android.bp index f0336a34f..e852584bc 100644 --- a/tradefed/Android.bp +++ b/tradefed/Android.bp @@ -13,6 +13,7 @@ bootstrap_go_package { "autogen.go", "config.go", "makevars.go", + "providers.go", ], pluginFor: ["soong_build"], } diff --git a/tradefed/providers.go b/tradefed/providers.go new file mode 100644 index 000000000..f41e09eb6 --- /dev/null +++ b/tradefed/providers.go @@ -0,0 +1,21 @@ +package tradefed + +import ( + "android/soong/android" + + "github.com/google/blueprint" +) + +// Output files we need from a base test that we derive from. +type BaseTestProviderData struct { + // data files and apps for android_test + InstalledFiles android.Paths + // apk for android_test + OutputFile android.Path + // Either handwritten or generated TF xml. + TestConfig android.Path + // Other modules we require to be installed to run tests. We expect base to build them. + HostRequiredModuleNames []string +} + +var BaseTestProviderKey = blueprint.NewProvider[BaseTestProviderData]() diff --git a/tradefed_modules/Android.bp b/tradefed_modules/Android.bp new file mode 100644 index 000000000..9969ae280 --- /dev/null +++ b/tradefed_modules/Android.bp @@ -0,0 +1,21 @@ +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +bootstrap_go_package { + name: "soong-tradefed-modules", + pkgPath: "android/soong/tradefed_modules", + deps: [ + "blueprint", + "soong-android", + "soong-java", + "soong-tradefed", + ], + srcs: [ + "test_module_config.go", + ], + testSrcs: [ + "test_module_config_test.go", + ], + pluginFor: ["soong_build"], +} diff --git a/tradefed_modules/test_module_config.go b/tradefed_modules/test_module_config.go new file mode 100644 index 000000000..ba6ab9400 --- /dev/null +++ b/tradefed_modules/test_module_config.go @@ -0,0 +1,219 @@ +package tradefed_modules + +import ( + "android/soong/android" + "android/soong/tradefed" + "encoding/json" + "fmt" + + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" +) + +func init() { + RegisterTestModuleConfigBuildComponents(android.InitRegistrationContext) +} + +// Register the license_kind module type. +func RegisterTestModuleConfigBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("test_module_config", TestModuleConfigFactory) +} + +type testModuleConfigModule struct { + android.ModuleBase + android.DefaultableModuleBase + base android.Module + + tradefedProperties + + // Our updated testConfig. + testConfig android.OutputPath + manifest android.InstallPath + provider tradefed.BaseTestProviderData +} + +// Properties to list in Android.bp for this module. +type tradefedProperties struct { + // Module name of the base test that we will run. + Base *string `android:"path,arch_variant"` + + // Tradefed Options to add to tradefed xml when not one of the include or exclude filter or property. + // Sample: [{name: "TestRunnerOptionName", value: "OptionValue" }] + Options []tradefed.Option + + // List of tradefed include annotations to add to tradefed xml, like "android.platform.test.annotations.Presubmit". + // Tests will be restricted to those matching an include_annotation or include_filter. + Include_annotations []string + + // List of tradefed include annotations to add to tradefed xml, like "android.support.test.filters.FlakyTest". + // Tests matching an exclude annotation or filter will be skipped. + Exclude_annotations []string + + // List of tradefed include filters to add to tradefed xml, like "fully.qualified.class#method". + // Tests will be restricted to those matching an include_annotation or include_filter. + Include_filters []string + + // List of tradefed exclude filters to add to tradefed xml, like "fully.qualified.class#method". + // Tests matching an exclude annotation or filter will be skipped. + Exclude_filters []string + + // List of compatibility suites (for example "cts", "vts") that the module should be + // installed into. + Test_suites []string +} + +type dependencyTag struct { + blueprint.BaseDependencyTag + name string +} + +var ( + testModuleConfigTag = dependencyTag{name: "TestModuleConfigBase"} + pctx = android.NewPackageContext("android/soong/tradefed_modules") +) + +func (m *testModuleConfigModule) InstallInTestcases() bool { + return true +} + +func (m *testModuleConfigModule) DepsMutator(ctx android.BottomUpMutatorContext) { + ctx.AddDependency(ctx.Module(), testModuleConfigTag, *m.Base) +} + +// Takes base's Tradefed Config xml file and generates a new one with the test properties +// appeneded from this module. +// Rewrite the name of the apk in "test-file-name" to be our module's name, rather than the original one. +func (m *testModuleConfigModule) fixTestConfig(ctx android.ModuleContext, baseTestConfig android.Path) android.OutputPath { + // Test safe to do when no test_runner_options, but check for that earlier? + fixedConfig := android.PathForModuleOut(ctx, "test_config_fixer", ctx.ModuleName()+".config") + rule := android.NewRuleBuilder(pctx, ctx) + command := rule.Command().BuiltTool("test_config_fixer").Input(baseTestConfig).Output(fixedConfig) + options := m.composeOptions() + if len(options) == 0 { + ctx.ModuleErrorf("Test options must be given when using test_module_config. Set include/exclude filter or annotation.") + } + xmlTestModuleConfigSnippet, _ := json.Marshal(options) + escaped := proptools.NinjaAndShellEscape(string(xmlTestModuleConfigSnippet)) + command.FlagWithArg("--test-file-name=", ctx.ModuleName()+".apk"). + FlagWithArg("--orig-test-file-name=", *m.tradefedProperties.Base+".apk"). + FlagWithArg("--test-runner-options=", escaped) + rule.Build("fix_test_config", "fix test config") + return fixedConfig.OutputPath +} + +// Convert --exclude_filters: ["filter1", "filter2"] -> +// [ Option{Name: "exclude-filters", Value: "filter1"}, Option{Name: "exclude-filters", Value: "filter2"}, +// ... + include + annotations ] +func (m *testModuleConfigModule) composeOptions() []tradefed.Option { + options := m.Options + for _, e := range m.Exclude_filters { + options = append(options, tradefed.Option{Name: "exclude-filter", Value: e}) + } + for _, i := range m.Include_filters { + options = append(options, tradefed.Option{Name: "include-filter", Value: i}) + } + for _, e := range m.Exclude_annotations { + options = append(options, tradefed.Option{Name: "exclude-annotation", Value: e}) + } + for _, i := range m.Include_annotations { + options = append(options, tradefed.Option{Name: "include-annotation", Value: i}) + } + return options +} + +// Files to write and where they come from: +// 1) test_module_config.manifest +// - Leave a trail of where we got files from in case other tools need it. +// +// 2) $Module.config +// - comes from base's module.config (AndroidTest.xml), and then we add our test_options. +// provider.TestConfig +// [rules via soong_app_prebuilt] +// +// 3) $ARCH/$Module.apk +// - comes from base +// provider.OutputFile +// [rules via soong_app_prebuilt] +// +// 4) [bases data] +// - We copy all of bases data (like helper apks) to our install directory too. +// Since we call AndroidMkEntries on base, it will write out LOCAL_COMPATIBILITY_SUPPORT_FILES +// with this data and app_prebuilt.mk will generate the rules to copy it from base. +// We have no direct rules here to add to ninja. +// +// If we change to symlinks, this all needs to change. +func (m *testModuleConfigModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + + ctx.VisitDirectDepsWithTag(testModuleConfigTag, func(dep android.Module) { + if provider, ok := android.OtherModuleProvider(ctx, dep, tradefed.BaseTestProviderKey); ok { + m.base = dep + m.provider = provider + } else { + ctx.ModuleErrorf("The base module '%s' does not provide test BaseTestProviderData. Only 'android_test' modules are supported.", dep.Name()) + return + } + }) + + // 1) A manifest file listing the base. + installDir := android.PathForModuleInstall(ctx, ctx.ModuleName()) + out := android.PathForModuleOut(ctx, "test_module_config.manifest") + android.WriteFileRule(ctx, out, fmt.Sprintf("{%q: %q}", "base", *m.tradefedProperties.Base)) + ctx.InstallFile(installDir, out.Base(), out) + + // 2) Module.config / AndroidTest.xml + // Note, there is still a "test-tag" element with base's module name, but + // Tradefed team says its ignored anyway. + m.testConfig = m.fixTestConfig(ctx, m.provider.TestConfig) + + // 3) Write ARCH/Module.apk in testcases. + // Handled by soong_app_prebuilt and OutputFile in entries. + // Nothing to do here. + + // 4) Copy base's data files. + // Handled by soong_app_prebuilt and LOCAL_COMPATIBILITY_SUPPORT_FILES. + // Nothing to do here. +} + +func TestModuleConfigFactory() android.Module { + module := &testModuleConfigModule{} + + module.AddProperties(&module.tradefedProperties) + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) + android.InitDefaultableModule(module) + + return module +} + +// Implements android.AndroidMkEntriesProvider +var _ android.AndroidMkEntriesProvider = (*testModuleConfigModule)(nil) + +func (m *testModuleConfigModule) AndroidMkEntries() []android.AndroidMkEntries { + // We rely on base writing LOCAL_COMPATIBILITY_SUPPORT_FILES for its data files + entriesList := m.base.(android.AndroidMkEntriesProvider).AndroidMkEntries() + entries := &entriesList[0] + entries.OutputFile = android.OptionalPathForPath(m.provider.OutputFile) + entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { + entries.SetString("LOCAL_MODULE", m.Name()) // out module name, not base's + + // Out update config file with extra options. + entries.SetPath("LOCAL_FULL_TEST_CONFIG", m.testConfig) + entries.SetString("LOCAL_MODULE_TAGS", "tests") + // Required for atest to run additional tradefed testtypes + entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", m.provider.HostRequiredModuleNames...) + + // Clear the JNI symbols because they belong to base not us. Either transform the names in the string + // or clear the variable because we don't need it, we are copying bases libraries not generating + // new ones. + entries.SetString("LOCAL_SOONG_JNI_LIBS_SYMBOLS", "") + + // Don't append to base's test-suites, only use the ones we define, so clear it before + // appending to it. + entries.SetString("LOCAL_COMPATIBILITY_SUITE", "") + if len(m.tradefedProperties.Test_suites) > 0 { + entries.AddCompatibilityTestSuites(m.tradefedProperties.Test_suites...) + } else { + entries.AddCompatibilityTestSuites("null-suite") + } + }) + return entriesList +} diff --git a/tradefed_modules/test_module_config_test.go b/tradefed_modules/test_module_config_test.go new file mode 100644 index 000000000..ff5304373 --- /dev/null +++ b/tradefed_modules/test_module_config_test.go @@ -0,0 +1,204 @@ +// 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 tradefed_modules + +import ( + "android/soong/android" + "android/soong/java" + "strings" + "testing" +) + +const bp = ` + android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "current", + } + + android_test_helper_app { + name: "HelperApp", + srcs: ["helper.java"], + } + + android_test { + name: "base", + sdk_version: "current", + data: [":HelperApp", "data/testfile"], + } + + test_module_config { + name: "derived_test", + base: "base", + exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"], + include_annotations: ["android.platform.test.annotations.LargeTest"], + } + +` + +// Ensure we create files needed and set the AndroidMkEntries needed +func TestModuleConfigAndroidTest(t *testing.T) { + + ctx := android.GroupFixturePreparers( + java.PrepareForTestWithJavaDefaultModules, + android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents), + ).RunTestWithBp(t, bp) + + derived := ctx.ModuleForTests("derived_test", "android_common") + // Assert there are rules to create these files. + derived.Output("test_module_config.manifest") + derived.Output("test_config_fixer/derived_test.config") + + // Ensure some basic rules exist. + ctx.ModuleForTests("base", "android_common").Output("package-res.apk") + entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0] + + // Ensure some entries from base are there, specifically support files for data and helper apps. + assertEntryPairValues(t, entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{"HelperApp.apk", "data/testfile"}) + + // And some new derived entries are there. + android.AssertArrayString(t, "", entries.EntryMap["LOCAL_MODULE_TAGS"], []string{"tests"}) + + // And ones we override + android.AssertArrayString(t, "", entries.EntryMap["LOCAL_SOONG_JNI_LIBS_SYMBOLS"], []string{""}) + + android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], "derived_test/android_common/test_config_fixer/derived_test.config") +} + +// Make sure we call test-config-fixer with the right args. +func TestModuleConfigOptions(t *testing.T) { + + ctx := android.GroupFixturePreparers( + java.PrepareForTestWithJavaDefaultModules, + android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents), + ).RunTestWithBp(t, bp) + + // Check that we generate a rule to make a new AndroidTest.xml/Module.config file. + derived := ctx.ModuleForTests("derived_test", "android_common") + rule_cmd := derived.Rule("fix_test_config").RuleParams.Command + android.AssertStringDoesContain(t, "Bad FixConfig rule inputs", rule_cmd, + `--test-file-name=derived_test.apk --orig-test-file-name=base.apk --test-runner-options='[{"Name":"exclude-filter","Key":"","Value":"android.test.example.devcodelab.DevCodelabTest#testHelloFail"},{"Name":"include-annotation","Key":"","Value":"android.platform.test.annotations.LargeTest"}]'`) +} + +// Ensure we error for a base we don't support. +func TestModuleConfigBadBaseShouldFail(t *testing.T) { + badBp := ` + java_test_host { + name: "base", + srcs: ["a.java"], + } + + test_module_config { + name: "derived_test", + base: "base", + exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"], + include_annotations: ["android.platform.test.annotations.LargeTest"], + }` + + ctx := android.GroupFixturePreparers( + java.PrepareForTestWithJavaDefaultModules, + android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents), + ).ExtendWithErrorHandler( + android.FixtureExpectsAtLeastOneErrorMatchingPattern("does not provide test BaseTestProviderData")). + RunTestWithBp(t, badBp) + + ctx.ModuleForTests("derived_test", "android_common") +} + +// Ensure we error for a base we don't support. +func TestModuleConfigNoFiltersOrAnnotationsShouldFail(t *testing.T) { + badBp := ` + android_test { + name: "base", + sdk_version: "current", + srcs: ["a.java"], + } + + test_module_config { + name: "derived_test", + base: "base", + }` + + ctx := android.GroupFixturePreparers( + java.PrepareForTestWithJavaDefaultModules, + android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents), + ).ExtendWithErrorHandler( + android.FixtureExpectsAtLeastOneErrorMatchingPattern("Test options must be given")). + RunTestWithBp(t, badBp) + + ctx.ModuleForTests("derived_test", "android_common") +} + +func TestModuleConfigMultipleDerivedTestsWriteDistinctMakeEntries(t *testing.T) { + multiBp := ` + android_test { + name: "base", + sdk_version: "current", + srcs: ["a.java"], + } + + test_module_config { + name: "derived_test", + base: "base", + include_annotations: ["android.platform.test.annotations.LargeTest"], + } + + test_module_config { + name: "another_derived_test", + base: "base", + include_annotations: ["android.platform.test.annotations.LargeTest"], + }` + + ctx := android.GroupFixturePreparers( + java.PrepareForTestWithJavaDefaultModules, + android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents), + ).RunTestWithBp(t, multiBp) + + { + derived := ctx.ModuleForTests("derived_test", "android_common") + entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0] + // All these should be the same in both derived tests + assertEntryPairValues(t, entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{"HelperApp.apk", "data/testfile"}) + android.AssertArrayString(t, "", entries.EntryMap["LOCAL_SOONG_JNI_LIBS_SYMBOLS"], []string{""}) + // Except this one, which points to the updated tradefed xml file. + android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], "derived_test/android_common/test_config_fixer/derived_test.config") + // And this one, the module name. + android.AssertArrayString(t, "", entries.EntryMap["LOCAL_MODULE"], []string{"derived_test"}) + } + + { + derived := ctx.ModuleForTests("another_derived_test", "android_common") + entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0] + // All these should be the same in both derived tests + assertEntryPairValues(t, entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{"HelperApp.apk", "data/testfile"}) + android.AssertArrayString(t, "", entries.EntryMap["LOCAL_SOONG_JNI_LIBS_SYMBOLS"], []string{""}) + // Except this one, which points to the updated tradefed xml file. + android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], "another_derived_test/android_common/test_config_fixer/another_derived_test.config") + // And this one, the module name. + android.AssertArrayString(t, "", entries.EntryMap["LOCAL_MODULE"], []string{"another_derived_test"}) + } +} + +// Use for situations where the entries map contains pairs: [srcPath:installedPath1, srcPath2:installedPath2] +// and we want to compare the RHS of the pairs, i.e. installedPath1, installedPath2 +func assertEntryPairValues(t *testing.T, actual []string, expected []string) { + for i, e := range actual { + parts := strings.Split(e, ":") + if len(parts) != 2 { + t.Errorf("Expected entry to have a value delimited by :, received: %s", e) + return + } + android.AssertStringEquals(t, "", parts[1], expected[i]) + } +} diff --git a/ui/build/config.go b/ui/build/config.go index 1a2539793..7426a78ee 100644 --- a/ui/build/config.go +++ b/ui/build/config.go @@ -386,22 +386,21 @@ func NewConfig(ctx Context, args ...string) Config { // Configure Java-related variables, including adding it to $PATH java8Home := filepath.Join("prebuilts/jdk/jdk8", ret.HostPrebuiltTag()) - java17Home := filepath.Join("prebuilts/jdk/jdk17", ret.HostPrebuiltTag()) java21Home := filepath.Join("prebuilts/jdk/jdk21", ret.HostPrebuiltTag()) javaHome := func() string { if override, ok := ret.environ.Get("OVERRIDE_ANDROID_JAVA_HOME"); ok { return override } - if ret.environ.IsEnvTrue("EXPERIMENTAL_USE_OPENJDK21_TOOLCHAIN") { - return java21Home - } if toolchain11, ok := ret.environ.Get("EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN"); ok && toolchain11 != "true" { - ctx.Fatalln("The environment variable EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN is no longer supported. An OpenJDK 11 toolchain is now the global default.") + ctx.Fatalln("The environment variable EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN is no longer supported. An OpenJDK 21 toolchain is now the global default.") } if toolchain17, ok := ret.environ.Get("EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN"); ok && toolchain17 != "true" { - ctx.Fatalln("The environment variable EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN is no longer supported. An OpenJDK 17 toolchain is now the global default.") + ctx.Fatalln("The environment variable EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN is no longer supported. An OpenJDK 21 toolchain is now the global default.") + } + if toolchain21, ok := ret.environ.Get("EXPERIMENTAL_USE_OPENJDK21_TOOLCHAIN"); ok && toolchain21 != "true" { + ctx.Fatalln("The environment variable EXPERIMENTAL_USE_OPENJDK21_TOOLCHAIN is no longer supported. An OpenJDK 21 toolchain is now the global default.") } - return java17Home + return java21Home }() absJavaHome := absPath(ctx, javaHome) @@ -1387,7 +1386,9 @@ func (c *configImpl) rbeAuth() (string, string) { } func (c *configImpl) rbeSockAddr(dir string) (string, error) { - maxNameLen := len(syscall.RawSockaddrUnix{}.Path) + // Absolute path socket addresses have a prefix of //. This should + // be included in the length limit. + maxNameLen := len(syscall.RawSockaddrUnix{}.Path) - 2 base := fmt.Sprintf("reproxy_%v.sock", rbeRandPrefix) name := filepath.Join(dir, base) diff --git a/ui/build/rbe.go b/ui/build/rbe.go index fa04207e0..5142a416f 100644 --- a/ui/build/rbe.go +++ b/ui/build/rbe.go @@ -163,7 +163,7 @@ func CheckProdCreds(ctx Context, config Config) { return } fmt.Fprintln(ctx.Writer, "") - fmt.Fprintln(ctx.Writer, "\033[33mWARNING: Missing LOAS credentials, please run `gcert`. This will result in failing builds in the future, see go/rbe-android-default-announcement.\033[0m") + fmt.Fprintln(ctx.Writer, "\033[33mWARNING: Missing LOAS credentials, please run `gcert`. This is required for a successful build execution. See go/rbe-android-default-announcement for more information.\033[0m") fmt.Fprintln(ctx.Writer, "") } diff --git a/ui/status/ninja.go b/ui/status/ninja.go index f4e3fb806..8c3ff291e 100644 --- a/ui/status/ninja.go +++ b/ui/status/ninja.go @@ -206,10 +206,11 @@ func (n *NinjaReader) run() { } if msg.EdgeStarted != nil { action := &Action{ - Description: msg.EdgeStarted.GetDesc(), - Outputs: msg.EdgeStarted.Outputs, - Inputs: msg.EdgeStarted.Inputs, - Command: msg.EdgeStarted.GetCommand(), + Description: msg.EdgeStarted.GetDesc(), + Outputs: msg.EdgeStarted.Outputs, + Inputs: msg.EdgeStarted.Inputs, + Command: msg.EdgeStarted.GetCommand(), + ChangedInputs: msg.EdgeStarted.ChangedInputs, } n.status.StartAction(action) running[msg.EdgeStarted.GetId()] = action diff --git a/ui/status/ninja_frontend/frontend.pb.go b/ui/status/ninja_frontend/frontend.pb.go index d8344c8a9..fce7ca284 100644 --- a/ui/status/ninja_frontend/frontend.pb.go +++ b/ui/status/ninja_frontend/frontend.pb.go @@ -363,6 +363,8 @@ type Status_EdgeStarted struct { Command *string `protobuf:"bytes,6,opt,name=command" json:"command,omitempty"` // Edge uses console. Console *bool `protobuf:"varint,7,opt,name=console" json:"console,omitempty"` + // Changed inputs. + ChangedInputs []string `protobuf:"bytes,8,rep,name=changed_inputs,json=changedInputs" json:"changed_inputs,omitempty"` } func (x *Status_EdgeStarted) Reset() { @@ -446,6 +448,13 @@ func (x *Status_EdgeStarted) GetConsole() bool { return false } +func (x *Status_EdgeStarted) GetChangedInputs() []string { + if x != nil { + return x.ChangedInputs + } + return nil +} + type Status_EdgeFinished struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -678,7 +687,7 @@ var File_frontend_proto protoreflect.FileDescriptor var file_frontend_proto_rawDesc = []byte{ 0x0a, 0x0e, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x12, 0x05, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x22, 0xa9, 0x0b, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, + 0x12, 0x05, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x22, 0xdb, 0x0b, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x39, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x64, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x45, 0x64, 0x67, 0x65, @@ -717,7 +726,7 @@ var file_frontend_proto_rawDesc = []byte{ 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x1a, 0x0f, 0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x1a, - 0xb6, 0x01, 0x0a, 0x0b, 0x45, 0x64, 0x67, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, + 0xe8, 0x01, 0x0a, 0x0b, 0x45, 0x64, 0x67, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, @@ -728,50 +737,54 @@ var file_frontend_proto_rawDesc = []byte{ 0x64, 0x65, 0x73, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x07, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x1a, 0xf3, 0x03, 0x0a, 0x0c, 0x45, 0x64, 0x67, - 0x65, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x65, 0x6e, 0x64, - 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x11, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, - 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x54, 0x69, 0x6d, - 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x69, - 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x73, 0x73, 0x5f, 0x6b, 0x62, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x52, 0x73, 0x73, 0x4b, 0x62, - 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, - 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x69, 0x6e, - 0x6f, 0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, - 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74, - 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x50, 0x61, - 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x69, 0x6f, 0x5f, 0x69, - 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x69, - 0x6f, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x6f, 0x5f, 0x6f, - 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, - 0x69, 0x6f, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x3c, 0x0a, 0x1a, 0x76, 0x6f, - 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, - 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x18, - 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, - 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x69, 0x6e, 0x76, 0x6f, - 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, - 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1a, - 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, - 0x67, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x1a, 0x92, - 0x01, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x05, 0x6c, 0x65, - 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6e, 0x69, 0x6e, 0x6a, - 0x61, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x3a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x52, 0x05, 0x6c, 0x65, - 0x76, 0x65, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x34, 0x0a, - 0x05, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x00, - 0x12, 0x0b, 0x0a, 0x07, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, - 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, - 0x47, 0x10, 0x03, 0x42, 0x2a, 0x48, 0x03, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, - 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x2f, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x5f, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64, + 0x07, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x64, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x1a, 0xf3, 0x03, 0x0a, 0x0c, 0x45, + 0x64, 0x67, 0x65, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x65, + 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x65, + 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x11, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, + 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x54, + 0x69, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x73, 0x73, 0x5f, + 0x6b, 0x62, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x52, 0x73, 0x73, + 0x4b, 0x62, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, + 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, + 0x69, 0x6e, 0x6f, 0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2a, + 0x0a, 0x11, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, + 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x69, 0x6f, + 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x09, 0x69, 0x6f, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x6f, + 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0a, 0x69, 0x6f, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x3c, 0x0a, 0x1a, + 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x18, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x69, 0x6e, + 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x1a, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, + 0x74, 0x61, 0x67, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, + 0x1a, 0x92, 0x01, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x05, + 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6e, 0x69, + 0x6e, 0x6a, 0x61, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x3a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x52, 0x05, + 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, + 0x34, 0x0a, 0x05, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, + 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, + 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, + 0x42, 0x55, 0x47, 0x10, 0x03, 0x42, 0x2a, 0x48, 0x03, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, + 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x2f, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x5f, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x64, } var ( diff --git a/ui/status/ninja_frontend/frontend.proto b/ui/status/ninja_frontend/frontend.proto index 42b251ba5..faa0d3467 100644 --- a/ui/status/ninja_frontend/frontend.proto +++ b/ui/status/ninja_frontend/frontend.proto @@ -54,6 +54,8 @@ message Status { optional string command = 6; // Edge uses console. optional bool console = 7; + // Changed inputs since last build. + repeated string changed_inputs = 8; } message EdgeFinished { diff --git a/ui/status/status.go b/ui/status/status.go index da78994ef..52ed56a13 100644 --- a/ui/status/status.go +++ b/ui/status/status.go @@ -41,6 +41,10 @@ type Action struct { // It's optional, but one of either Description or Command should be // set. Command string + + // ChangedInputs is the (optional) list of inputs that have changed + // since last time this action was run. + ChangedInputs []string } // ActionResult describes the result of running an Action. diff --git a/ui/tracer/status.go b/ui/tracer/status.go index f973613d4..8acf561c9 100644 --- a/ui/tracer/status.go +++ b/ui/tracer/status.go @@ -15,9 +15,10 @@ package tracer import ( - "android/soong/ui/status" "strings" "time" + + "android/soong/ui/status" ) func (t *tracerImpl) StatusTracer() status.StatusOutput { @@ -110,6 +111,7 @@ func (s *statusOutput) FinishAction(result status.ActionResult, counts status.Co VoluntaryContextSwitches: result.Stats.VoluntaryContextSwitches, InvoluntaryContextSwitches: result.Stats.InvoluntaryContextSwitches, Tags: s.parseTags(result.Stats.Tags), + ChangedInputs: result.Action.ChangedInputs, }, }) } @@ -125,6 +127,7 @@ type statsArg struct { VoluntaryContextSwitches uint64 `json:"voluntary_context_switches"` InvoluntaryContextSwitches uint64 `json:"involuntary_context_switches"` Tags map[string]string `json:"tags"` + ChangedInputs []string `json:"changed_inputs"` } func (s *statusOutput) Flush() {} |