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/config.go b/android/config.go index 1bb1a2296..567ebd83f 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" @@ -1938,6 +1939,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/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/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/apex/aconfig_test.go b/apex/aconfig_test.go new file mode 100644 index 000000000..be98d457b --- /dev/null +++ b/apex/aconfig_test.go @@ -0,0 +1,550 @@ +// 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", + }`, + }, + } + 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: "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..7c6fb899d 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") } } diff --git a/cc/cc.go b/cc/cc.go index 2770fb23b..0fa34571a 100644 --- a/cc/cc.go +++ b/cc/cc.go @@ -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/java/base.go b/java/base.go index d8ccec6c6..69f88be1e 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" @@ -2551,7 +2550,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/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/java.go b/java/java.go index 6423eebff..e606993f2 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" @@ -346,6 +345,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 +360,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 +407,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} @@ -2173,7 +2180,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 "+