diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go index dac0ae36d..162079281 100644 --- a/aconfig/aconfig_declarations.go +++ b/aconfig/aconfig_declarations.go @@ -15,6 +15,8 @@ package aconfig import ( + "path/filepath" + "slices" "strings" "android/soong/android" @@ -22,6 +24,11 @@ import ( "github.com/google/blueprint" ) +type AconfigReleaseConfigValue struct { + ReleaseConfig string + Values []string `blueprint:"mutated"` +} + type DeclarationsModule struct { android.ModuleBase android.DefaultableModuleBase @@ -34,8 +41,10 @@ type DeclarationsModule struct { // Release config flag package Package string - // Values from TARGET_RELEASE / RELEASE_ACONFIG_VALUE_SETS - Values []string `blueprint:"mutated"` + // Values for release configs / RELEASE_ACONFIG_VALUE_SETS + // The current release config is `ReleaseConfig: ""`, others + // are from RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS. + ReleaseConfigValues []AconfigReleaseConfigValue // Container(system/vendor/apex) that this module belongs to Container string @@ -57,6 +66,10 @@ func DeclarationsFactory() android.Module { type implicitValuesTagType struct { blueprint.BaseDependencyTag + + // The release config name for these values. + // Empty string for the actual current release config. + ReleaseConfig string } var implicitValuesTag = implicitValuesTagType{} @@ -81,6 +94,11 @@ func (module *DeclarationsModule) DepsMutator(ctx android.BottomUpMutatorContext if len(valuesFromConfig) > 0 { ctx.AddDependency(ctx.Module(), implicitValuesTag, valuesFromConfig...) } + for rcName, valueSets := range ctx.Config().ReleaseAconfigExtraReleaseConfigsValueSets() { + if len(valueSets) > 0 { + ctx.AddDependency(ctx.Module(), implicitValuesTagType{ReleaseConfig: rcName}, valueSets...) + } + } } func joinAndPrefix(prefix string, values []string) string { @@ -101,59 +119,101 @@ func optionalVariable(prefix string, value string) string { return sb.String() } +// Assemble the actual filename. +// If `rcName` is not empty, then insert "-{rcName}" into the path before the +// file extension. +func assembleFileName(rcName, path string) string { + if rcName == "" { + return path + } + dir, file := filepath.Split(path) + rcName = "-" + rcName + ext := filepath.Ext(file) + base := file[:len(file)-len(ext)] + return dir + base + rcName + ext +} + func (module *DeclarationsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { - // Get the values that came from the global RELEASE_ACONFIG_VALUE_SETS flag - valuesFiles := make([]android.Path, 0) + // Determine which release configs we are processing. + // + // We always process the current release config (empty string). + // We may have been told to also create artifacts for some others. + configs := append([]string{""}, ctx.Config().ReleaseAconfigExtraReleaseConfigs()...) + slices.Sort(configs) + + values := make(map[string][]string) + valuesFiles := make(map[string][]android.Path, 0) + providerData := android.AconfigReleaseDeclarationsProviderData{} ctx.VisitDirectDeps(func(dep android.Module) { if depData, ok := android.OtherModuleProvider(ctx, dep, valueSetProviderKey); ok { - paths, ok := depData.AvailablePackages[module.properties.Package] - if ok { - valuesFiles = append(valuesFiles, paths...) - for _, path := range paths { - module.properties.Values = append(module.properties.Values, path.String()) + depTag := ctx.OtherModuleDependencyTag(dep) + for _, config := range configs { + tag := implicitValuesTagType{ReleaseConfig: config} + if depTag == tag { + paths, ok := depData.AvailablePackages[module.properties.Package] + if ok { + valuesFiles[config] = append(valuesFiles[config], paths...) + for _, path := range paths { + values[config] = append(values[config], path.String()) + } + } } } } }) + for _, config := range configs { + module.properties.ReleaseConfigValues = append(module.properties.ReleaseConfigValues, AconfigReleaseConfigValue{ + ReleaseConfig: config, + Values: values[config], + }) - // Intermediate format - declarationFiles := android.PathsForModuleSrc(ctx, module.properties.Srcs) - intermediateCacheFilePath := android.PathForModuleOut(ctx, "intermediate.pb") - defaultPermission := ctx.Config().ReleaseAconfigFlagDefaultPermission() - inputFiles := make([]android.Path, len(declarationFiles)) - copy(inputFiles, declarationFiles) - inputFiles = append(inputFiles, valuesFiles...) - args := map[string]string{ - "release_version": ctx.Config().ReleaseVersion(), - "package": module.properties.Package, - "declarations": android.JoinPathsWithPrefix(declarationFiles, "--declarations "), - "values": joinAndPrefix(" --values ", module.properties.Values), - "default-permission": optionalVariable(" --default-permission ", defaultPermission), + // Intermediate format + declarationFiles := android.PathsForModuleSrc(ctx, module.properties.Srcs) + intermediateCacheFilePath := android.PathForModuleOut(ctx, assembleFileName(config, "intermediate.pb")) + var defaultPermission string + defaultPermission = ctx.Config().ReleaseAconfigFlagDefaultPermission() + if config != "" { + if confPerm, ok := ctx.Config().GetBuildFlag("RELEASE_ACONFIG_FLAG_DEFAULT_PERMISSION_" + config); ok { + defaultPermission = confPerm + } + } + inputFiles := make([]android.Path, len(declarationFiles)) + copy(inputFiles, declarationFiles) + inputFiles = append(inputFiles, valuesFiles[config]...) + args := map[string]string{ + "release_version": ctx.Config().ReleaseVersion(), + "package": module.properties.Package, + "declarations": android.JoinPathsWithPrefix(declarationFiles, "--declarations "), + "values": joinAndPrefix(" --values ", values[config]), + "default-permission": optionalVariable(" --default-permission ", defaultPermission), + } + if len(module.properties.Container) > 0 { + args["container"] = "--container " + module.properties.Container + } + ctx.Build(pctx, android.BuildParams{ + Rule: aconfigRule, + Output: intermediateCacheFilePath, + Inputs: inputFiles, + Description: "aconfig_declarations", + Args: args, + }) + + intermediateDumpFilePath := android.PathForModuleOut(ctx, assembleFileName(config, "intermediate.txt")) + ctx.Build(pctx, android.BuildParams{ + Rule: aconfigTextRule, + Output: intermediateDumpFilePath, + Inputs: android.Paths{intermediateCacheFilePath}, + Description: "aconfig_text", + }) + + providerData[config] = android.AconfigDeclarationsProviderData{ + Package: module.properties.Package, + Container: module.properties.Container, + Exportable: module.properties.Exportable, + IntermediateCacheOutputPath: intermediateCacheFilePath, + IntermediateDumpOutputPath: intermediateDumpFilePath, + } } - if len(module.properties.Container) > 0 { - args["container"] = "--container " + module.properties.Container - } - ctx.Build(pctx, android.BuildParams{ - Rule: aconfigRule, - Output: intermediateCacheFilePath, - Inputs: inputFiles, - Description: "aconfig_declarations", - Args: args, - }) - - intermediateDumpFilePath := android.PathForModuleOut(ctx, "intermediate.txt") - ctx.Build(pctx, android.BuildParams{ - Rule: aconfigTextRule, - Output: intermediateDumpFilePath, - Inputs: android.Paths{intermediateCacheFilePath}, - Description: "aconfig_text", - }) - - android.SetProvider(ctx, android.AconfigDeclarationsProviderKey, android.AconfigDeclarationsProviderData{ - Package: module.properties.Package, - Container: module.properties.Container, - Exportable: module.properties.Exportable, - IntermediateCacheOutputPath: intermediateCacheFilePath, - IntermediateDumpOutputPath: intermediateDumpFilePath, - }) + android.SetProvider(ctx, android.AconfigDeclarationsProviderKey, providerData[""]) + android.SetProvider(ctx, android.AconfigReleaseDeclarationsProviderKey, providerData) } diff --git a/aconfig/aconfig_declarations_test.go b/aconfig/aconfig_declarations_test.go index c37274c71..548329523 100644 --- a/aconfig/aconfig_declarations_test.go +++ b/aconfig/aconfig_declarations_test.go @@ -15,6 +15,7 @@ package aconfig import ( + "slices" "strings" "testing" @@ -134,3 +135,95 @@ func TestMandatoryProperties(t *testing.T) { }) } } + +func TestAssembleFileName(t *testing.T) { + testCases := []struct { + name string + releaseConfig string + path string + expectedValue string + }{ + { + name: "active release config", + path: "file.path", + expectedValue: "file.path", + }, + { + name: "release config FOO", + releaseConfig: "FOO", + path: "file.path", + expectedValue: "file-FOO.path", + }, + } + for _, test := range testCases { + actualValue := assembleFileName(test.releaseConfig, test.path) + if actualValue != test.expectedValue { + t.Errorf("Expected %q found %q", test.expectedValue, actualValue) + } + } +} + +func TestGenerateAndroidBuildActions(t *testing.T) { + testCases := []struct { + name string + buildFlags map[string]string + bp string + errorHandler android.FixtureErrorHandler + }{ + { + name: "generate extra", + buildFlags: map[string]string{ + "RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS": "config2", + "RELEASE_ACONFIG_VALUE_SETS": "aconfig_value_set-config1", + "RELEASE_ACONFIG_VALUE_SETS_config2": "aconfig_value_set-config2", + }, + bp: ` + aconfig_declarations { + name: "module_name", + package: "com.example.package", + container: "com.android.foo", + srcs: [ + "foo.aconfig", + "bar.aconfig", + ], + } + aconfig_value_set { + name: "aconfig_value_set-config1", + values: [] + } + aconfig_value_set { + name: "aconfig_value_set-config2", + values: [] + } + `, + }, + } + for _, test := range testCases { + fixture := PrepareForTest(t, addBuildFlagsForTest(test.buildFlags)) + if test.errorHandler != nil { + fixture = fixture.ExtendWithErrorHandler(test.errorHandler) + } + result := fixture.RunTestWithBp(t, test.bp) + module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule) + depData, _ := android.SingletonModuleProvider(result, module, android.AconfigReleaseDeclarationsProviderKey) + expectedKeys := []string{""} + for _, rc := range strings.Split(test.buildFlags["RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS"], " ") { + expectedKeys = append(expectedKeys, rc) + } + slices.Sort(expectedKeys) + actualKeys := []string{} + for rc := range depData { + actualKeys = append(actualKeys, rc) + } + slices.Sort(actualKeys) + android.AssertStringEquals(t, "provider keys", strings.Join(expectedKeys, " "), strings.Join(actualKeys, " ")) + for _, rc := range actualKeys { + if !strings.HasSuffix(depData[rc].IntermediateCacheOutputPath.String(), assembleFileName(rc, "/intermediate.pb")) { + t.Errorf("Incorrect intermediates proto path in provider for release config %s: %s", rc, depData[rc].IntermediateCacheOutputPath.String()) + } + if !strings.HasSuffix(depData[rc].IntermediateDumpOutputPath.String(), assembleFileName(rc, "/intermediate.txt")) { + t.Errorf("Incorrect intermediates text path in provider for release config %s: %s", rc, depData[rc].IntermediateDumpOutputPath.String()) + } + } + } +} diff --git a/aconfig/all_aconfig_declarations.go b/aconfig/all_aconfig_declarations.go index e771d0597..0437c266f 100644 --- a/aconfig/all_aconfig_declarations.go +++ b/aconfig/all_aconfig_declarations.go @@ -17,6 +17,7 @@ package aconfig import ( "android/soong/android" "fmt" + "slices" ) // A singleton module that collects all of the aconfig flags declared in the @@ -27,70 +28,90 @@ import ( // ones that are relevant to the product currently being built, so that that infra // doesn't need to pull from multiple builds and merge them. func AllAconfigDeclarationsFactory() android.Singleton { - return &allAconfigDeclarationsSingleton{} + return &allAconfigDeclarationsSingleton{releaseMap: make(map[string]allAconfigReleaseDeclarationsSingleton)} } -type allAconfigDeclarationsSingleton struct { +type allAconfigReleaseDeclarationsSingleton struct { intermediateBinaryProtoPath android.OutputPath intermediateTextProtoPath android.OutputPath } +type allAconfigDeclarationsSingleton struct { + releaseMap map[string]allAconfigReleaseDeclarationsSingleton +} + +func (this *allAconfigDeclarationsSingleton) sortedConfigNames() []string { + var names []string + for k := range this.releaseMap { + names = append(names, k) + } + slices.Sort(names) + return names +} + func (this *allAconfigDeclarationsSingleton) GenerateBuildActions(ctx android.SingletonContext) { - // Find all of the aconfig_declarations modules - var packages = make(map[string]int) - var cacheFiles android.Paths - ctx.VisitAllModules(func(module android.Module) { - decl, ok := android.SingletonModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey) - if !ok { - return + for _, rcName := range append([]string{""}, ctx.Config().ReleaseAconfigExtraReleaseConfigs()...) { + // Find all of the aconfig_declarations modules + var packages = make(map[string]int) + var cacheFiles android.Paths + ctx.VisitAllModules(func(module android.Module) { + decl, ok := android.SingletonModuleProvider(ctx, module, android.AconfigReleaseDeclarationsProviderKey) + if !ok { + return + } + cacheFiles = append(cacheFiles, decl[rcName].IntermediateCacheOutputPath) + packages[decl[rcName].Package]++ + }) + + var numOffendingPkg = 0 + for pkg, cnt := range packages { + if cnt > 1 { + fmt.Printf("%d aconfig_declarations found for package %s\n", cnt, pkg) + numOffendingPkg++ + } } - cacheFiles = append(cacheFiles, decl.IntermediateCacheOutputPath) - packages[decl.Package]++ - }) - var numOffendingPkg = 0 - for pkg, cnt := range packages { - if cnt > 1 { - fmt.Printf("%d aconfig_declarations found for package %s\n", cnt, pkg) - numOffendingPkg++ + if numOffendingPkg > 0 { + panic(fmt.Errorf("Only one aconfig_declarations allowed for each package.")) } + + // Generate build action for aconfig (binary proto output) + paths := allAconfigReleaseDeclarationsSingleton{ + intermediateBinaryProtoPath: android.PathForIntermediates(ctx, assembleFileName(rcName, "all_aconfig_declarations.pb")), + intermediateTextProtoPath: android.PathForIntermediates(ctx, assembleFileName(rcName, "all_aconfig_declarations.textproto")), + } + this.releaseMap[rcName] = paths + ctx.Build(pctx, android.BuildParams{ + Rule: AllDeclarationsRule, + Inputs: cacheFiles, + Output: this.releaseMap[rcName].intermediateBinaryProtoPath, + Description: "all_aconfig_declarations", + Args: map[string]string{ + "cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "), + }, + }) + ctx.Phony("all_aconfig_declarations", this.releaseMap[rcName].intermediateBinaryProtoPath) + + // Generate build action for aconfig (text proto output) + ctx.Build(pctx, android.BuildParams{ + Rule: AllDeclarationsRuleTextProto, + Inputs: cacheFiles, + Output: this.releaseMap[rcName].intermediateTextProtoPath, + Description: "all_aconfig_declarations_textproto", + Args: map[string]string{ + "cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "), + }, + }) + ctx.Phony("all_aconfig_declarations_textproto", this.releaseMap[rcName].intermediateTextProtoPath) } - - if numOffendingPkg > 0 { - panic(fmt.Errorf("Only one aconfig_declarations allowed for each package.")) - } - - // Generate build action for aconfig (binary proto output) - this.intermediateBinaryProtoPath = android.PathForIntermediates(ctx, "all_aconfig_declarations.pb") - ctx.Build(pctx, android.BuildParams{ - Rule: AllDeclarationsRule, - Inputs: cacheFiles, - Output: this.intermediateBinaryProtoPath, - Description: "all_aconfig_declarations", - Args: map[string]string{ - "cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "), - }, - }) - ctx.Phony("all_aconfig_declarations", this.intermediateBinaryProtoPath) - - // Generate build action for aconfig (text proto output) - this.intermediateTextProtoPath = android.PathForIntermediates(ctx, "all_aconfig_declarations.textproto") - ctx.Build(pctx, android.BuildParams{ - Rule: AllDeclarationsRuleTextProto, - Inputs: cacheFiles, - Output: this.intermediateTextProtoPath, - Description: "all_aconfig_declarations_textproto", - Args: map[string]string{ - "cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "), - }, - }) - ctx.Phony("all_aconfig_declarations_textproto", this.intermediateTextProtoPath) } func (this *allAconfigDeclarationsSingleton) MakeVars(ctx android.MakeVarsContext) { - ctx.DistForGoal("droid", this.intermediateBinaryProtoPath) - for _, goal := range []string{"docs", "droid", "sdk"} { - ctx.DistForGoalWithFilename(goal, this.intermediateBinaryProtoPath, "flags.pb") - ctx.DistForGoalWithFilename(goal, this.intermediateTextProtoPath, "flags.textproto") + for _, rcName := range this.sortedConfigNames() { + ctx.DistForGoal("droid", this.releaseMap[rcName].intermediateBinaryProtoPath) + for _, goal := range []string{"docs", "droid", "sdk"} { + ctx.DistForGoalWithFilename(goal, this.releaseMap[rcName].intermediateBinaryProtoPath, assembleFileName(rcName, "flags.pb")) + ctx.DistForGoalWithFilename(goal, this.releaseMap[rcName].intermediateTextProtoPath, assembleFileName(rcName, "flags.textproto")) + } } } diff --git a/aconfig/testing.go b/aconfig/testing.go index f6489ec3f..4ceb6b3e5 100644 --- a/aconfig/testing.go +++ b/aconfig/testing.go @@ -23,7 +23,25 @@ import ( var PrepareForTestWithAconfigBuildComponents = android.FixtureRegisterWithContext(RegisterBuildComponents) func runTest(t *testing.T, errorHandler android.FixtureErrorHandler, bp string) *android.TestResult { - return android.GroupFixturePreparers(PrepareForTestWithAconfigBuildComponents). + return PrepareForTest(t). ExtendWithErrorHandler(errorHandler). RunTestWithBp(t, bp) } + +func PrepareForTest(t *testing.T, preparers ...android.FixturePreparer) android.FixturePreparer { + preparers = append([]android.FixturePreparer{PrepareForTestWithAconfigBuildComponents}, preparers...) + return android.GroupFixturePreparers(preparers...) +} + +func addBuildFlagsForTest(buildFlags map[string]string) android.FixturePreparer { + return android.GroupFixturePreparers( + android.FixtureModifyProductVariables(func(vars android.FixtureProductVariables) { + if vars.BuildFlags == nil { + vars.BuildFlags = make(map[string]string) + } + for k, v := range buildFlags { + vars.BuildFlags[k] = v + } + }), + ) +} diff --git a/android/aconfig_providers.go b/android/aconfig_providers.go index ee9891df1..a47e80f03 100644 --- a/android/aconfig_providers.go +++ b/android/aconfig_providers.go @@ -43,6 +43,10 @@ type AconfigDeclarationsProviderData struct { var AconfigDeclarationsProviderKey = blueprint.NewProvider[AconfigDeclarationsProviderData]() +type AconfigReleaseDeclarationsProviderData map[string]AconfigDeclarationsProviderData + +var AconfigReleaseDeclarationsProviderKey = blueprint.NewProvider[AconfigReleaseDeclarationsProviderData]() + type ModeInfo struct { Container string Mode string @@ -112,6 +116,8 @@ func aconfigUpdateAndroidBuildActions(ctx ModuleContext) { if dep, ok := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); ok { mergedAconfigFiles[dep.Container] = append(mergedAconfigFiles[dep.Container], dep.IntermediateCacheOutputPath) } + // If we were generating on-device artifacts for other release configs, we would need to add code here to propagate + // those artifacts as well. See also b/298444886. if dep, ok := OtherModuleProvider(ctx, module, AconfigPropagatingProviderKey); ok { for container, v := range dep.AconfigFiles { mergedAconfigFiles[container] = append(mergedAconfigFiles[container], v...) diff --git a/android/config.go b/android/config.go index 112448877..e122d25af 100644 --- a/android/config.go +++ b/android/config.go @@ -198,6 +198,33 @@ func (c Config) ReleaseAconfigValueSets() []string { return c.config.productVariables.ReleaseAconfigValueSets } +func (c Config) ReleaseAconfigExtraReleaseConfigs() []string { + result := []string{} + if val, ok := c.config.productVariables.BuildFlags["RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS"]; ok { + if len(val) > 0 { + // Remove any duplicates from the list. + found := make(map[string]bool) + for _, k := range strings.Split(val, " ") { + if !found[k] { + found[k] = true + result = append(result, k) + } + } + } + } + return result +} + +func (c Config) ReleaseAconfigExtraReleaseConfigsValueSets() map[string][]string { + result := make(map[string][]string) + for _, rcName := range c.ReleaseAconfigExtraReleaseConfigs() { + if value, ok := c.config.productVariables.BuildFlags["RELEASE_ACONFIG_VALUE_SETS_"+rcName]; ok { + result[rcName] = strings.Split(value, " ") + } + } + return result +} + // The flag default permission value passed to aconfig // derived from RELEASE_ACONFIG_FLAG_DEFAULT_PERMISSION func (c Config) ReleaseAconfigFlagDefaultPermission() string { diff --git a/android/config_test.go b/android/config_test.go index 7d327a27e..ca7c7f8b4 100644 --- a/android/config_test.go +++ b/android/config_test.go @@ -125,6 +125,43 @@ func assertStringEquals(t *testing.T, expected, actual string) { } } +func TestReleaseAconfigExtraReleaseConfigs(t *testing.T) { + testCases := []struct { + name string + flag string + expected []string + }{ + { + name: "empty", + flag: "", + expected: []string{}, + }, + { + name: "specified", + flag: "bar foo", + expected: []string{"bar", "foo"}, + }, + { + name: "duplicates", + flag: "foo bar foo", + expected: []string{"foo", "bar"}, + }, + } + + for _, tc := range testCases { + fixture := GroupFixturePreparers( + FixtureModifyProductVariables(func(vars FixtureProductVariables) { + if vars.BuildFlags == nil { + vars.BuildFlags = make(map[string]string) + } + vars.BuildFlags["RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS"] = tc.flag + }), + ) + actual := fixture.RunTest(t).Config.ReleaseAconfigExtraReleaseConfigs() + AssertArrayString(t, tc.name, tc.expected, actual) + } +} + func TestConfiguredJarList(t *testing.T) { list1 := CreateTestConfiguredJarList([]string{"apex1:jarA"}) diff --git a/cmd/release_config/release_config_lib/release_config.go b/cmd/release_config/release_config_lib/release_config.go index 6911e545e..6d71d9343 100644 --- a/cmd/release_config/release_config_lib/release_config.go +++ b/cmd/release_config/release_config_lib/release_config.go @@ -226,8 +226,16 @@ func (config *ReleaseConfig) GenerateReleaseConfig(configs *ReleaseConfigs) erro config.PriorStagesMap[priorStage] = true } myDirsMap[contrib.DeclarationIndex] = true - if config.AconfigFlagsOnly && len(contrib.FlagValues) > 0 { - return fmt.Errorf("%s does not allow build flag overrides", config.Name) + if config.AconfigFlagsOnly { + // AconfigFlagsOnly allows very very few build flag values, all of them are part of aconfig flags. + allowedFlags := map[string]bool{ + "RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS": true, + } + for _, fv := range contrib.FlagValues { + if !allowedFlags[*fv.proto.Name] { + return fmt.Errorf("%s does not allow build flag overrides", config.Name) + } + } } for _, value := range contrib.FlagValues { name := *value.proto.Name @@ -256,7 +264,7 @@ func (config *ReleaseConfig) GenerateReleaseConfig(configs *ReleaseConfigs) erro myAconfigValueSets := []string{} myAconfigValueSetsMap := map[string]bool{} for _, v := range strings.Split(releaseAconfigValueSets.Value.GetStringValue(), " ") { - if myAconfigValueSetsMap[v] { + if v == "" || myAconfigValueSetsMap[v] { continue } myAconfigValueSetsMap[v] = true @@ -320,6 +328,23 @@ func (config *ReleaseConfig) WriteMakefile(outFile, targetRelease string, config makeVars := make(map[string]string) myFlagArtifacts := config.FlagArtifacts.Clone() + + // Add any RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS variables. + var extraAconfigReleaseConfigs []string + if extraAconfigValueSetsValue, ok := config.FlagArtifacts["RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS"]; ok { + if val := MarshalValue(extraAconfigValueSetsValue.Value); len(val) > 0 { + extraAconfigReleaseConfigs = strings.Split(val, " ") + } + } + for _, rcName := range extraAconfigReleaseConfigs { + rc, err := configs.GetReleaseConfig(rcName) + if err != nil { + return err + } + myFlagArtifacts["RELEASE_ACONFIG_VALUE_SETS_"+rcName] = rc.FlagArtifacts["RELEASE_ACONFIG_VALUE_SETS"] + myFlagArtifacts["RELEASE_ACONFIG_FLAG_DEFAULT_PERMISSION_"+rcName] = rc.FlagArtifacts["RELEASE_ACONFIG_FLAG_DEFAULT_PERMISSION"] + } + // Sort the flags by name first. names := myFlagArtifacts.SortedFlagNames() partitions := make(map[string][]string)