diff options
63 files changed, 1871 insertions, 322 deletions
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 3d15ac9a9..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" @@ -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..4625128e4 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") 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/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 1bb1a2296..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 { @@ -1938,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/module_context.go b/android/module_context.go index 3fc5d0158..1cab63022 100644 --- a/android/module_context.go +++ b/android/module_context.go @@ -21,7 +21,6 @@ import ( "strings" "github.com/google/blueprint" - "github.com/google/blueprint/parser" "github.com/google/blueprint/proptools" ) @@ -213,10 +212,6 @@ type ModuleContext interface { // GenerateAndroidBuildActions. If it is called then the struct will be written out and included in // the module-info.json generated by Make, and Make will not generate its own data for this module. ModuleInfoJSON() *ModuleInfoJSON - - // 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 moduleContext struct { @@ -719,32 +714,3 @@ func (m *moduleContext) HostRequiredModuleNames() []string { func (m *moduleContext) TargetRequiredModuleNames() []string { return m.module.TargetRequiredModuleNames() } - -func (m *moduleContext) 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: 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/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 index dca378933..aa9c521a2 100644 --- a/android/selects_test.go +++ b/android/selects_test.go @@ -80,6 +80,36 @@ func TestSelects(t *testing.T) { }, }, { + 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 { @@ -233,6 +263,7 @@ type selectsTestProvider struct { my_bool *bool my_string *string my_string_list *[]string + my_paths *[]string } func (p *selectsTestProvider) String() string { @@ -248,7 +279,8 @@ func (p *selectsTestProvider) String() string { my_bool: %v, my_string: %s, my_string_list: %s, -}`, myBoolStr, myStringStr, p.my_string_list) + my_paths: %s, +}`, myBoolStr, myStringStr, p.my_string_list, p.my_paths) } var selectsTestProviderKey = blueprint.NewProvider[selectsTestProvider]() @@ -257,6 +289,7 @@ 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 { @@ -266,10 +299,11 @@ type selectsMockModule struct { } func (p *selectsMockModule) GenerateAndroidBuildActions(ctx ModuleContext) { - SetProvider[selectsTestProvider](ctx, selectsTestProviderKey, selectsTestProvider{ + 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), }) } 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..79a534f56 100644 --- a/android/visibility.go +++ b/android/visibility.go @@ -303,7 +303,10 @@ func checkRules(ctx BaseModuleContext, currentPkg, property string, visibility [ if pkg == "visibility" { switch name { - case "private", "public", "any_partition": + case "private", "public": + case "any_partition": + // any_partition can be used with another visibility fields + continue case "legacy_public": ctx.PropertyErrorf(property, "//visibility:legacy_public must not be used") continue diff --git a/android/visibility_test.go b/android/visibility_test.go index d4add7d32..bb43b1fa1 100644 --- a/android/visibility_test.go +++ b/android/visibility_test.go @@ -1921,6 +1921,26 @@ var visibilityTests = []struct { }, }, { + name: "any_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_partition" + ], + }`), + }, + }, + { name: "any_partition visibility doesn't work for non-partitions", fs: MockFS{ "top/Android.bp": []byte(` 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 c6884387a..32a3638ed 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -2321,9 +2321,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") } } @@ -2428,29 +2434,14 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.providePrebuiltInfo(ctx) } -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"` -} - // 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 := prebuiltInfo{ + info := android.PrebuiltInfo{ Name: a.Name(), Is_prebuilt: false, } - android.SetProvider(ctx, prebuiltInfoProvider, info) + 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 8aaddbe25..e6ebff2c1 100644 --- a/apex/apex_singleton.go +++ b/apex/apex_singleton.go @@ -149,10 +149,10 @@ type apexPrebuiltInfo struct { } func (a *apexPrebuiltInfo) GenerateBuildActions(ctx android.SingletonContext) { - prebuiltInfos := []prebuiltInfo{} + prebuiltInfos := []android.PrebuiltInfo{} ctx.VisitAllModules(func(m android.Module) { - prebuiltInfo, exists := android.SingletonModuleProvider(ctx, m, prebuiltInfoProvider) + 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() { 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 34dfc467b..ea847e15c 100644 --- a/apex/prebuilt.go +++ b/apex/prebuilt.go @@ -827,15 +827,15 @@ 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 := prebuiltInfo{ - Name: p.BaseModuleName(), // BaseModuleName ensures that this will not contain the prebuilt_ prefix. + 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, prebuiltInfoProvider, info) + android.SetProvider(ctx, android.PrebuiltInfoProvider, info) } func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -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..0de9e055f 100644 --- a/cc/config/arm64_device.go +++ b/cc/config/arm64_device.go @@ -51,6 +51,7 @@ var ( arm64Ldflags = []string{ "-Wl,--hash-style=gnu", "-Wl,-z,separate-code", + "-Wl,-z,separate-loadable-segments", } arm64Lldflags = arm64Ldflags diff --git a/cc/config/riscv64_device.go b/cc/config/riscv64_device.go index 6a84fee9f..a5dea55bb 100644 --- a/cc/config/riscv64_device.go +++ b/cc/config/riscv64_device.go @@ -23,24 +23,36 @@ 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{ + // TODO: sysv hashes are the default for other architectures because gnu + // hashes weren't supported until api level 23, but riscv64 didn't exist + // back then, and could move today... + // https://android.googlesource.com/platform/bionic/+/main/android-changes-for-ndk-developers.md#gnu-hashes-availible-in-api-level-23 "-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: move to driver (https://github.com/google/android-riscv64/issues/111) + "-mno-strict-align", + // 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..12119a712 100644 --- a/cc/config/x86_64_device.go +++ b/cc/config/x86_64_device.go @@ -31,6 +31,7 @@ var ( x86_64Ldflags = []string{ "-Wl,--hash-style=gnu", + "-Wl,-z,separate-loadable-segments", } X86_64Lldflags = x86_64Ldflags diff --git a/cc/library.go b/cc/library.go index bbff49a68..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( @@ -1917,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 { @@ -2294,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/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/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/elf/Android.bp b/elf/Android.bp new file mode 100644 index 000000000..6450be137 --- /dev/null +++ b/elf/Android.bp @@ -0,0 +1,28 @@ +// 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +bootstrap_go_package { + name: "soong-elf", + pkgPath: "android/soong/elf", + srcs: [ + "elf.go", + ], + testSrcs: [ + "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/filesystem/filesystem.go b/filesystem/filesystem.go index 795a0aa02..64a2e235f 100644 --- a/filesystem/filesystem.go +++ b/filesystem/filesystem.go @@ -19,6 +19,7 @@ import ( "fmt" "io" "path/filepath" + "slices" "strings" "android/soong/android" @@ -109,6 +110,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 @@ -183,13 +190,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 +215,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. @@ -388,25 +369,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(). @@ -429,6 +406,41 @@ 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()) +} + 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/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 17e97b644..cf2a96673 100644 --- a/genrule/genrule.go +++ b/genrule/genrule.go @@ -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/aar.go b/java/aar.go index 27dd38b3f..283579280 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 { @@ -804,6 +807,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) { @@ -817,13 +824,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) 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 8209d4c61..4656888b4 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,18 +519,6 @@ 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(), - ) - } - }) - a.aapt.buildActions(ctx, aaptBuildActionOptions{ sdkContext: android.SdkContext(a), @@ -526,7 +526,7 @@ func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) { excludedLibs: a.usesLibraryProperties.Exclude_uses_libs, enforceDefaultTargetSdkVersion: a.enforceDefaultTargetSdkVersion(), extraLinkFlags: aaptLinkFlags, - aconfigTextFiles: aconfigTextFilePaths, + aconfigTextFiles: getAconfigFilePaths(ctx), }, ) @@ -755,7 +755,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 +763,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 +912,13 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { } a.buildAppDependencyInfo(ctx) + + providePrebuiltInfo(ctx, + prebuiltInfoProps{ + baseModuleName: a.BaseModuleName(), + isPrebuilt: false, + }, + ) } type appDepsInterface interface { @@ -1500,7 +1507,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 5d7b048b3..b75cb1678 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -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 d8ccec6c6..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" @@ -306,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 @@ -435,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 @@ -617,6 +616,7 @@ func (j *Module) checkHeadersOnly(ctx android.ModuleContext) { func (j *Module) addHostProperties() { j.AddProperties( &j.properties, + &j.overridableProperties, &j.protoProperties, &j.usesLibraryProperties, ) @@ -626,7 +626,6 @@ func (j *Module) addHostAndDeviceProperties() { j.addHostProperties() j.AddProperties( &j.deviceProperties, - &j.overridableDeviceProperties, &j.dexer.dexProperties, &j.dexpreoptProperties, &j.linter.properties, @@ -1220,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 @@ -1730,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 @@ -2296,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 { @@ -2354,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: @@ -2551,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/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/droiddoc.go b/java/droiddoc.go index 6a66f45ec..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" ) @@ -414,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 76c8d8890..9556e956a 100644 --- a/java/droidstubs.go +++ b/java/droidstubs.go @@ -368,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 } @@ -478,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() { diff --git a/java/java.go b/java/java.go index 6423eebff..ad10cbd77 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" @@ -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} @@ -890,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) @@ -1687,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 @@ -2173,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 "+ @@ -2998,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/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/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..668dd8f08 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 ( 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/sysprop/sysprop_library.go b/sysprop/sysprop_library.go index 82abba4cb..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 := android.GenPathWithExt(ctx, "sysprop", syspropFile, "srcrust") - outputDir := syspropDir.Join(ctx, "src") - libPath := syspropDir.Join(ctx, "src", "lib.rs") - parsersPath := syspropDir.Join(ctx, "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( |