Merge "Unify the behaviors of Shard*(...) utility functions" into main
diff --git a/android/apex.go b/android/apex.go
index 8759905..c0acada 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "slices"
"sort"
"strconv"
"strings"
@@ -108,7 +109,7 @@
// are configured to have the same alias variation named apex29. Whether platform APIs is allowed
// or not also matters; if two APEXes don't have the same allowance, they get different names and
// thus wouldn't be merged.
-func (i ApexInfo) mergedName(ctx PathContext) string {
+func (i ApexInfo) mergedName() string {
name := "apex" + strconv.Itoa(i.MinSdkVersion.FinalOrFutureInt())
return name
}
@@ -543,17 +544,10 @@
return true
}
-type byApexName []ApexInfo
-
-func (a byApexName) Len() int { return len(a) }
-func (a byApexName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-func (a byApexName) Less(i, j int) bool { return a[i].ApexVariationName < a[j].ApexVariationName }
-
// mergeApexVariations deduplicates apex variations that would build identically into a common
// variation. It returns the reduced list of variations and a list of aliases from the original
// variation names to the new variation names.
-func mergeApexVariations(ctx PathContext, apexInfos []ApexInfo) (merged []ApexInfo, aliases [][2]string) {
- sort.Sort(byApexName(apexInfos))
+func mergeApexVariations(apexInfos []ApexInfo) (merged []ApexInfo, aliases [][2]string) {
seen := make(map[string]int)
for _, apexInfo := range apexInfos {
// If this is for a prebuilt apex then use the actual name of the apex variation to prevent this
@@ -567,7 +561,7 @@
// this one into it, otherwise create a new merged ApexInfo from this one and save it away so
// other ApexInfo instances can be merged into it.
variantName := apexInfo.ApexVariationName
- mergedName := apexInfo.mergedName(ctx)
+ mergedName := apexInfo.mergedName()
if index, exists := seen[mergedName]; exists {
// Variants having the same mergedName are deduped
merged[index].InApexVariants = append(merged[index].InApexVariants, variantName)
@@ -606,18 +600,20 @@
// TODO(jiyong): is this the right place?
base.checkApexAvailableProperty(mctx)
- var apexInfos []ApexInfo
- var aliases [][2]string
- if !mctx.Module().(ApexModule).UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps {
- apexInfos, aliases = mergeApexVariations(mctx, base.apexInfos)
- } else {
- apexInfos = base.apexInfos
- }
+ apexInfos := base.apexInfos
// base.apexInfos is only needed to propagate the list of apexes from apexInfoMutator to
// apexMutator. It is no longer accurate after mergeApexVariations, and won't be copied to
// all but the first created variant. Clear it so it doesn't accidentally get used later.
base.apexInfos = nil
- sort.Sort(byApexName(apexInfos))
+
+ slices.SortFunc(apexInfos, func(a, b ApexInfo) int {
+ return strings.Compare(a.ApexVariationName, b.ApexVariationName)
+ })
+
+ var aliases [][2]string
+ if !mctx.Module().(ApexModule).UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps {
+ apexInfos, aliases = mergeApexVariations(apexInfos)
+ }
var inApex ApexMembership
for _, a := range apexInfos {
diff --git a/android/apex_test.go b/android/apex_test.go
index ddc730d..347bf7d 100644
--- a/android/apex_test.go
+++ b/android/apex_test.go
@@ -33,10 +33,22 @@
{
name: "single",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
+ {
+ ApexVariationName: "foo",
+ MinSdkVersion: FutureApiLevel,
+ InApexVariants: []string{"foo"},
+ InApexModules: []string{"foo"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
},
wantMerged: []ApexInfo{
- {"apex10000", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
+ {
+ ApexVariationName: "apex10000",
+ MinSdkVersion: FutureApiLevel,
+ InApexVariants: []string{"foo"},
+ InApexModules: []string{"foo"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
},
wantAliases: [][2]string{
{"foo", "apex10000"},
@@ -45,98 +57,231 @@
{
name: "merge",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
- {"bar", FutureApiLevel, false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
+ {
+ ApexVariationName: "foo",
+ MinSdkVersion: FutureApiLevel,
+ InApexVariants: []string{"foo"},
+ InApexModules: []string{"foo"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
+ {
+ ApexVariationName: "bar",
+ MinSdkVersion: FutureApiLevel,
+ InApexVariants: []string{"bar"},
+ InApexModules: []string{"bar"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
},
wantMerged: []ApexInfo{
- {"apex10000", FutureApiLevel, false, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, false, nil}},
+ {
+ ApexVariationName: "apex10000",
+ MinSdkVersion: FutureApiLevel,
+ InApexVariants: []string{"foo", "bar"},
+ InApexModules: []string{"foo", "bar"},
+ }},
wantAliases: [][2]string{
- {"bar", "apex10000"},
{"foo", "apex10000"},
+ {"bar", "apex10000"},
},
},
{
name: "don't merge version",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
- {"bar", uncheckedFinalApiLevel(30), false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
+ {
+ ApexVariationName: "foo",
+ MinSdkVersion: FutureApiLevel,
+ InApexVariants: []string{"foo"},
+ InApexModules: []string{"foo"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
+ {
+ ApexVariationName: "bar",
+ MinSdkVersion: uncheckedFinalApiLevel(30),
+ InApexVariants: []string{"bar"},
+ InApexModules: []string{"bar"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
},
wantMerged: []ApexInfo{
- {"apex30", uncheckedFinalApiLevel(30), false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
- {"apex10000", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
+ {
+ ApexVariationName: "apex10000",
+ MinSdkVersion: FutureApiLevel,
+ InApexVariants: []string{"foo"},
+ InApexModules: []string{"foo"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
+ {
+ ApexVariationName: "apex30",
+ MinSdkVersion: uncheckedFinalApiLevel(30),
+ InApexVariants: []string{"bar"},
+ InApexModules: []string{"bar"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
},
wantAliases: [][2]string{
- {"bar", "apex30"},
{"foo", "apex10000"},
+ {"bar", "apex30"},
},
},
{
name: "merge updatable",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
- {"bar", FutureApiLevel, true, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
+ {
+ ApexVariationName: "foo",
+ MinSdkVersion: FutureApiLevel,
+ InApexVariants: []string{"foo"},
+ InApexModules: []string{"foo"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
+ {
+ ApexVariationName: "bar",
+ MinSdkVersion: FutureApiLevel,
+ Updatable: true,
+ InApexVariants: []string{"bar"},
+ InApexModules: []string{"bar"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
},
wantMerged: []ApexInfo{
- {"apex10000", FutureApiLevel, true, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex, nil},
+ {
+ ApexVariationName: "apex10000",
+ MinSdkVersion: FutureApiLevel,
+ Updatable: true,
+ InApexVariants: []string{"foo", "bar"},
+ InApexModules: []string{"foo", "bar"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
},
wantAliases: [][2]string{
- {"bar", "apex10000"},
{"foo", "apex10000"},
+ {"bar", "apex10000"},
},
},
{
name: "don't merge when for prebuilt_apex",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
- {"bar", FutureApiLevel, true, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
+ {
+ ApexVariationName: "foo",
+ MinSdkVersion: FutureApiLevel,
+ InApexVariants: []string{"foo"},
+ InApexModules: []string{"foo"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
+ {
+ ApexVariationName: "bar",
+ MinSdkVersion: FutureApiLevel,
+ Updatable: true,
+ InApexVariants: []string{"bar"},
+ InApexModules: []string{"bar"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
// This one should not be merged in with the others because it is for
// a prebuilt_apex.
- {"baz", FutureApiLevel, true, false, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex, nil},
+ {
+ ApexVariationName: "baz",
+ MinSdkVersion: FutureApiLevel,
+ Updatable: true,
+ InApexVariants: []string{"baz"},
+ InApexModules: []string{"baz"},
+ ForPrebuiltApex: ForPrebuiltApex,
+ },
},
wantMerged: []ApexInfo{
- {"apex10000", FutureApiLevel, true, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex, nil},
- {"baz", FutureApiLevel, true, false, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex, nil},
+ {
+ ApexVariationName: "apex10000",
+ MinSdkVersion: FutureApiLevel,
+ Updatable: true,
+ InApexVariants: []string{"foo", "bar"},
+ InApexModules: []string{"foo", "bar"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
+ {
+ ApexVariationName: "baz",
+ MinSdkVersion: FutureApiLevel,
+ Updatable: true,
+ InApexVariants: []string{"baz"},
+ InApexModules: []string{"baz"},
+ ForPrebuiltApex: ForPrebuiltApex,
+ },
},
wantAliases: [][2]string{
- {"bar", "apex10000"},
{"foo", "apex10000"},
+ {"bar", "apex10000"},
},
},
{
name: "merge different UsePlatformApis but don't allow using platform api",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
- {"bar", FutureApiLevel, false, true, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
+ {
+ ApexVariationName: "foo",
+ MinSdkVersion: FutureApiLevel,
+ InApexVariants: []string{"foo"},
+ InApexModules: []string{"foo"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
+ {
+ ApexVariationName: "bar",
+ MinSdkVersion: FutureApiLevel,
+ UsePlatformApis: true,
+ InApexVariants: []string{"bar"},
+ InApexModules: []string{"bar"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
},
wantMerged: []ApexInfo{
- {"apex10000", FutureApiLevel, false, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex, nil},
+ {
+ ApexVariationName: "apex10000",
+ MinSdkVersion: FutureApiLevel,
+ InApexVariants: []string{"foo", "bar"},
+ InApexModules: []string{"foo", "bar"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
},
wantAliases: [][2]string{
- {"bar", "apex10000"},
{"foo", "apex10000"},
+ {"bar", "apex10000"},
},
},
{
name: "merge same UsePlatformApis and allow using platform api",
in: []ApexInfo{
- {"foo", FutureApiLevel, false, true, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
- {"bar", FutureApiLevel, false, true, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
+ {
+ ApexVariationName: "foo",
+ MinSdkVersion: FutureApiLevel,
+ UsePlatformApis: true,
+ InApexVariants: []string{"foo"},
+ InApexModules: []string{"foo"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
+ {
+ ApexVariationName: "bar",
+ MinSdkVersion: FutureApiLevel,
+ UsePlatformApis: true,
+ InApexVariants: []string{"bar"},
+ InApexModules: []string{"bar"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
},
wantMerged: []ApexInfo{
- {"apex10000", FutureApiLevel, false, true, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex, nil},
+ {
+ ApexVariationName: "apex10000",
+ MinSdkVersion: FutureApiLevel,
+ UsePlatformApis: true,
+ InApexVariants: []string{"foo", "bar"},
+ InApexModules: []string{"foo", "bar"},
+ ForPrebuiltApex: NotForPrebuiltApex,
+ },
},
wantAliases: [][2]string{
- {"bar", "apex10000"},
{"foo", "apex10000"},
+ {"bar", "apex10000"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- config := TestConfig(t.TempDir(), nil, "", nil)
- ctx := &configErrorWrapper{config: config}
- gotMerged, gotAliases := mergeApexVariations(ctx, tt.in)
+ gotMerged, gotAliases := mergeApexVariations(tt.in)
if !reflect.DeepEqual(gotMerged, tt.wantMerged) {
t.Errorf("mergeApexVariations() gotMerged = %v, want %v", gotMerged, tt.wantMerged)
}
diff --git a/android/base_module_context.go b/android/base_module_context.go
index c4922f4..c5fe585 100644
--- a/android/base_module_context.go
+++ b/android/base_module_context.go
@@ -20,7 +20,7 @@
"strings"
"github.com/google/blueprint"
- "github.com/google/blueprint/parser"
+ "github.com/google/blueprint/proptools"
)
// BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns
@@ -219,7 +219,7 @@
// 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) (string, bool)
+ EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue
}
type baseModuleContext struct {
@@ -577,6 +577,6 @@
return sb.String()
}
-func (m *baseModuleContext) EvaluateConfiguration(ty parser.SelectType, property, condition string) (string, bool) {
- return m.Module().ConfigurableEvaluator(m).EvaluateConfiguration(ty, property, condition)
+func (m *baseModuleContext) EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue {
+ return m.Module().ConfigurableEvaluator(m).EvaluateConfiguration(condition, property)
}
diff --git a/android/config.go b/android/config.go
index 11bd81b..75d135f 100644
--- a/android/config.go
+++ b/android/config.go
@@ -949,7 +949,11 @@
}
func (c *config) PlatformMinSupportedTargetSdkVersion() string {
- return String(c.productVariables.Platform_min_supported_target_sdk_version)
+ var val, ok = c.productVariables.BuildFlags["RELEASE_PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION"]
+ if !ok {
+ return ""
+ }
+ return val
}
func (c *config) PlatformBaseOS() string {
diff --git a/android/module.go b/android/module.go
index 89c4ddd..26261cc 100644
--- a/android/module.go
+++ b/android/module.go
@@ -29,7 +29,6 @@
"android/soong/bazel"
"github.com/google/blueprint"
- "github.com/google/blueprint/parser"
"github.com/google/blueprint/proptools"
)
@@ -2140,41 +2139,82 @@
e.ctx.OtherModulePropertyErrorf(e.m, property, fmt, args)
}
-func (e configurationEvalutor) EvaluateConfiguration(ty parser.SelectType, property, condition string) (string, bool) {
+func (e configurationEvalutor) EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue {
ctx := e.ctx
m := e.m
- switch ty {
- case parser.SelectTypeReleaseVariable:
- if v, ok := ctx.Config().productVariables.BuildFlags[condition]; ok {
- return v, true
+ switch condition.FunctionName {
+ case "release_variable":
+ if len(condition.Args) != 1 {
+ ctx.OtherModulePropertyErrorf(m, property, "release_variable requires 1 argument, found %d", len(condition.Args))
+ return proptools.ConfigurableValueUndefined()
}
- return "", false
- case parser.SelectTypeProductVariable:
+ if v, ok := ctx.Config().productVariables.BuildFlags[condition.Args[0]]; ok {
+ return proptools.ConfigurableValueString(v)
+ }
+ return proptools.ConfigurableValueUndefined()
+ case "product_variable":
// TODO(b/323382414): Might add these on a case-by-case basis
ctx.OtherModulePropertyErrorf(m, property, "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]
+ return proptools.ConfigurableValueUndefined()
+ case "soong_config_variable":
+ if len(condition.Args) != 2 {
+ ctx.OtherModulePropertyErrorf(m, property, "soong_config_variable requires 2 arguments, found %d", len(condition.Args))
+ return proptools.ConfigurableValueUndefined()
+ }
+ namespace := condition.Args[0]
+ variable := condition.Args[1]
if n, ok := ctx.Config().productVariables.VendorVars[namespace]; ok {
if v, ok := n[variable]; ok {
- return v, true
+ return proptools.ConfigurableValueString(v)
}
}
- return "", false
- case parser.SelectTypeVariant:
- if condition == "arch" {
- if !m.base().ArchReady() {
- ctx.OtherModulePropertyErrorf(m, property, "A select on arch was attempted before the arch mutator ran")
- return "", false
- }
- return m.base().Arch().ArchType.Name, true
+ return proptools.ConfigurableValueUndefined()
+ case "arch":
+ if len(condition.Args) != 0 {
+ ctx.OtherModulePropertyErrorf(m, property, "arch requires no arguments, found %d", len(condition.Args))
+ return proptools.ConfigurableValueUndefined()
}
- ctx.OtherModulePropertyErrorf(m, property, "Unknown variant %s", condition)
- return "", false
+ if !m.base().ArchReady() {
+ ctx.OtherModulePropertyErrorf(m, property, "A select on arch was attempted before the arch mutator ran")
+ return proptools.ConfigurableValueUndefined()
+ }
+ return proptools.ConfigurableValueString(m.base().Arch().ArchType.Name)
+ case "os":
+ if len(condition.Args) != 0 {
+ ctx.OtherModulePropertyErrorf(m, property, "os requires no arguments, found %d", len(condition.Args))
+ return proptools.ConfigurableValueUndefined()
+ }
+ // the arch mutator runs after the os mutator, we can just use this to enforce that os is ready.
+ if !m.base().ArchReady() {
+ ctx.OtherModulePropertyErrorf(m, property, "A select on os was attempted before the arch mutator ran (arch runs after os, we use it to lazily detect that os is ready)")
+ return proptools.ConfigurableValueUndefined()
+ }
+ return proptools.ConfigurableValueString(m.base().Os().Name)
+ case "boolean_var_for_testing":
+ // We currently don't have any other boolean variables (we should add support for typing
+ // the soong config variables), so add this fake one for testing the boolean select
+ // functionality.
+ if len(condition.Args) != 0 {
+ ctx.OtherModulePropertyErrorf(m, property, "boolean_var_for_testing requires 0 arguments, found %d", len(condition.Args))
+ return proptools.ConfigurableValueUndefined()
+ }
+
+ if n, ok := ctx.Config().productVariables.VendorVars["boolean_var"]; ok {
+ if v, ok := n["for_testing"]; ok {
+ switch v {
+ case "true":
+ return proptools.ConfigurableValueBool(true)
+ case "false":
+ return proptools.ConfigurableValueBool(false)
+ default:
+ ctx.OtherModulePropertyErrorf(m, property, "testing:my_boolean_var can only be true or false, found %q", v)
+ }
+ }
+ }
+ return proptools.ConfigurableValueUndefined()
default:
- panic("Should be unreachable")
+ ctx.OtherModulePropertyErrorf(m, property, "Unknown select condition %s", condition.FunctionName)
+ return proptools.ConfigurableValueUndefined()
}
}
diff --git a/android/mutator.go b/android/mutator.go
index 0ff4f48..75ba650 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -391,6 +391,7 @@
type IncomingTransitionContext interface {
ArchModuleContext
+ ModuleProviderContext
// Module returns the target of the dependency edge for which the transition
// is being computed
@@ -404,6 +405,7 @@
type OutgoingTransitionContext interface {
ArchModuleContext
+ ModuleProviderContext
// Module returns the target of the dependency edge for which the transition
// is being computed
@@ -505,6 +507,7 @@
type androidTransitionMutator struct {
finalPhase bool
mutator TransitionMutator
+ name string
}
func (a *androidTransitionMutator) Split(ctx blueprint.BaseModuleContext) []string {
@@ -537,6 +540,10 @@
return DeviceConfig{c.bp.Config().(Config).deviceConfig}
}
+func (c *outgoingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) {
+ return c.bp.Provider(provider)
+}
+
func (a *androidTransitionMutator) OutgoingTransition(bpctx blueprint.OutgoingTransitionContext, sourceVariation string) string {
if m, ok := bpctx.Module().(Module); ok {
ctx := outgoingTransitionContextPool.Get().(*outgoingTransitionContextImpl)
@@ -568,6 +575,10 @@
return DeviceConfig{c.bp.Config().(Config).deviceConfig}
}
+func (c *incomingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) {
+ return c.bp.Provider(provider)
+}
+
func (a *androidTransitionMutator) IncomingTransition(bpctx blueprint.IncomingTransitionContext, incomingVariation string) string {
if m, ok := bpctx.Module().(Module); ok {
ctx := incomingTransitionContextPool.Get().(*incomingTransitionContextImpl)
@@ -586,6 +597,9 @@
if am, ok := ctx.Module().(Module); ok {
mctx := bottomUpMutatorContextFactory(ctx, am, a.finalPhase)
defer bottomUpMutatorContextPool.Put(mctx)
+ base := am.base()
+ base.commonProperties.DebugMutators = append(base.commonProperties.DebugMutators, a.name)
+ base.commonProperties.DebugVariations = append(base.commonProperties.DebugVariations, variation)
a.mutator.Mutate(mctx, variation)
}
}
@@ -594,6 +608,7 @@
atm := &androidTransitionMutator{
finalPhase: x.finalPhase,
mutator: m,
+ name: name,
}
mutator := &mutator{
name: name,
diff --git a/android/selects_test.go b/android/selects_test.go
index e59b3e6..f912ce6 100644
--- a/android/selects_test.go
+++ b/android/selects_test.go
@@ -269,11 +269,11 @@
},
},
{
- name: "Select on variant",
+ name: "Select on arch",
bp: `
my_module_type {
name: "foo",
- my_string: select(variant("arch"), {
+ my_string: select(arch(), {
"x86": "my_x86",
"x86_64": "my_x86_64",
"arm": "my_arm",
@@ -287,6 +287,22 @@
},
},
{
+ name: "Select on os",
+ bp: `
+ my_module_type {
+ name: "foo",
+ my_string: select(os(), {
+ "android": "my_android",
+ "linux": "my_linux",
+ default: "my_default",
+ }),
+ }
+ `,
+ provider: selectsTestProvider{
+ my_string: proptools.StringPtr("my_android"),
+ },
+ },
+ {
name: "Unset value",
bp: `
my_module_type {
@@ -327,8 +343,10 @@
my_module_type {
name: "foo",
my_string: select(soong_config_variable("my_namespace", "my_variable"), {
+ "foo": "bar",
default: unset,
}) + select(soong_config_variable("my_namespace", "my_variable2"), {
+ "baz": "qux",
default: unset,
})
}
@@ -341,6 +359,7 @@
my_module_type {
name: "foo",
my_string: select(soong_config_variable("my_namespace", "my_variable"), {
+ "foo": "bar",
default: unset,
}) + select(soong_config_variable("my_namespace", "my_variable2"), {
default: "a",
@@ -414,6 +433,169 @@
replacing_string_list: &[]string{"b1"},
},
},
+ {
+ name: "Multi-condition string 1",
+ bp: `
+ my_module_type {
+ name: "foo",
+ my_string: select((
+ soong_config_variable("my_namespace", "my_variable"),
+ soong_config_variable("my_namespace", "my_variable2"),
+ ), {
+ ("a", "b"): "a+b",
+ ("a", default): "a+default",
+ (default, default): "default",
+ }),
+ }
+ `,
+ vendorVars: map[string]map[string]string{
+ "my_namespace": {
+ "my_variable": "a",
+ "my_variable2": "b",
+ },
+ },
+ provider: selectsTestProvider{
+ my_string: proptools.StringPtr("a+b"),
+ },
+ },
+ {
+ name: "Multi-condition string 2",
+ bp: `
+ my_module_type {
+ name: "foo",
+ my_string: select((
+ soong_config_variable("my_namespace", "my_variable"),
+ soong_config_variable("my_namespace", "my_variable2"),
+ ), {
+ ("a", "b"): "a+b",
+ ("a", default): "a+default",
+ (default, default): "default",
+ }),
+ }
+ `,
+ vendorVars: map[string]map[string]string{
+ "my_namespace": {
+ "my_variable": "a",
+ "my_variable2": "c",
+ },
+ },
+ provider: selectsTestProvider{
+ my_string: proptools.StringPtr("a+default"),
+ },
+ },
+ {
+ name: "Multi-condition string 3",
+ bp: `
+ my_module_type {
+ name: "foo",
+ my_string: select((
+ soong_config_variable("my_namespace", "my_variable"),
+ soong_config_variable("my_namespace", "my_variable2"),
+ ), {
+ ("a", "b"): "a+b",
+ ("a", default): "a+default",
+ (default, default): "default",
+ }),
+ }
+ `,
+ vendorVars: map[string]map[string]string{
+ "my_namespace": {
+ "my_variable": "c",
+ "my_variable2": "b",
+ },
+ },
+ provider: selectsTestProvider{
+ my_string: proptools.StringPtr("default"),
+ },
+ },
+ {
+ name: "Select on boolean",
+ bp: `
+ my_module_type {
+ name: "foo",
+ my_string: select(boolean_var_for_testing(), {
+ true: "t",
+ false: "f",
+ }),
+ }
+ `,
+ vendorVars: map[string]map[string]string{
+ "boolean_var": {
+ "for_testing": "true",
+ },
+ },
+ provider: selectsTestProvider{
+ my_string: proptools.StringPtr("t"),
+ },
+ },
+ {
+ name: "Select on boolean false",
+ bp: `
+ my_module_type {
+ name: "foo",
+ my_string: select(boolean_var_for_testing(), {
+ true: "t",
+ false: "f",
+ }),
+ }
+ `,
+ vendorVars: map[string]map[string]string{
+ "boolean_var": {
+ "for_testing": "false",
+ },
+ },
+ provider: selectsTestProvider{
+ my_string: proptools.StringPtr("f"),
+ },
+ },
+ {
+ name: "Select on boolean undefined",
+ bp: `
+ my_module_type {
+ name: "foo",
+ my_string: select(boolean_var_for_testing(), {
+ true: "t",
+ false: "f",
+ }),
+ }
+ `,
+ expectedError: "foo",
+ },
+ {
+ name: "Select on boolean undefined with default",
+ bp: `
+ my_module_type {
+ name: "foo",
+ my_string: select(boolean_var_for_testing(), {
+ true: "t",
+ false: "f",
+ default: "default",
+ }),
+ }
+ `,
+ provider: selectsTestProvider{
+ my_string: proptools.StringPtr("default"),
+ },
+ },
+ {
+ name: "Mismatched condition types",
+ bp: `
+ my_module_type {
+ name: "foo",
+ my_string: select(boolean_var_for_testing(), {
+ "true": "t",
+ "false": "f",
+ default: "default",
+ }),
+ }
+ `,
+ vendorVars: map[string]map[string]string{
+ "boolean_var": {
+ "for_testing": "false",
+ },
+ },
+ expectedError: "Expected all branches of a select on condition boolean_var_for_testing\\(\\) to have type bool, found string",
+ },
}
for _, tc := range testCases {
diff --git a/android/variable.go b/android/variable.go
index 5a079db..0040d83 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -201,23 +201,22 @@
BuildThumbprintFile *string `json:",omitempty"`
DisplayBuildNumber *bool `json:",omitempty"`
- Platform_display_version_name *string `json:",omitempty"`
- Platform_version_name *string `json:",omitempty"`
- Platform_sdk_version *int `json:",omitempty"`
- Platform_sdk_codename *string `json:",omitempty"`
- Platform_sdk_version_or_codename *string `json:",omitempty"`
- Platform_sdk_final *bool `json:",omitempty"`
- Platform_sdk_extension_version *int `json:",omitempty"`
- Platform_base_sdk_extension_version *int `json:",omitempty"`
- Platform_version_active_codenames []string `json:",omitempty"`
- Platform_version_all_preview_codenames []string `json:",omitempty"`
- Platform_systemsdk_versions []string `json:",omitempty"`
- Platform_security_patch *string `json:",omitempty"`
- Platform_preview_sdk_version *string `json:",omitempty"`
- Platform_min_supported_target_sdk_version *string `json:",omitempty"`
- Platform_base_os *string `json:",omitempty"`
- Platform_version_last_stable *string `json:",omitempty"`
- Platform_version_known_codenames *string `json:",omitempty"`
+ Platform_display_version_name *string `json:",omitempty"`
+ Platform_version_name *string `json:",omitempty"`
+ Platform_sdk_version *int `json:",omitempty"`
+ Platform_sdk_codename *string `json:",omitempty"`
+ Platform_sdk_version_or_codename *string `json:",omitempty"`
+ Platform_sdk_final *bool `json:",omitempty"`
+ Platform_sdk_extension_version *int `json:",omitempty"`
+ Platform_base_sdk_extension_version *int `json:",omitempty"`
+ Platform_version_active_codenames []string `json:",omitempty"`
+ Platform_version_all_preview_codenames []string `json:",omitempty"`
+ Platform_systemsdk_versions []string `json:",omitempty"`
+ Platform_security_patch *string `json:",omitempty"`
+ Platform_preview_sdk_version *string `json:",omitempty"`
+ Platform_base_os *string `json:",omitempty"`
+ Platform_version_last_stable *string `json:",omitempty"`
+ Platform_version_known_codenames *string `json:",omitempty"`
DeviceName *string `json:",omitempty"`
DeviceProduct *string `json:",omitempty"`
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index 672e852..b5b49b1 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -19,6 +19,7 @@
import (
"bytes"
"fmt"
+ "os"
"reflect"
"strings"
"testing"
@@ -2215,3 +2216,9 @@
})
}
}
+
+func TestMain(m *testing.M) {
+ // Skip checking Android.mk path with cleaning "ANDROID_BUILD_TOP"
+ os.Setenv("ANDROID_BUILD_TOP", "")
+ os.Exit(m.Run())
+}
diff --git a/cc/cc.go b/cc/cc.go
index a1cae39..e3954d7 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1995,6 +1995,20 @@
}
func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
+ ctx := moduleContextFromAndroidModuleContext(actx, c)
+
+ // If Test_only is set on a module in bp file, respect the setting, otherwise
+ // see if is a known test module type.
+ testOnly := c.testModule || c.testLibrary()
+ if c.sourceProperties.Test_only != nil {
+ testOnly = Bool(c.sourceProperties.Test_only)
+ }
+ // Keep before any early returns.
+ android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
+ TestOnly: testOnly,
+ TopLevelTarget: c.testModule,
+ })
+
// Handle the case of a test module split by `test_per_src` mutator.
//
// The `test_per_src` mutator adds an extra variation named "", depending on all the other
@@ -2013,8 +2027,6 @@
c.makeLinkType = GetMakeLinkType(actx, c)
- ctx := moduleContextFromAndroidModuleContext(actx, c)
-
deps := c.depsToPaths(ctx)
if ctx.Failed() {
return
@@ -2141,17 +2153,6 @@
android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
}
- // If Test_only is set on a module in bp file, respect the setting, otherwise
- // see if is a known test module type.
- testOnly := c.testModule || c.testLibrary()
- if c.sourceProperties.Test_only != nil {
- testOnly = Bool(c.sourceProperties.Test_only)
- }
- android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
- TestOnly: testOnly,
- TopLevelTarget: c.testModule,
- })
-
android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: deps.GeneratedSources.Strings()})
android.CollectDependencyAconfigFiles(ctx, &c.mergedAconfigFiles)
diff --git a/cc/cc_test_only_property_test.go b/cc/cc_test_only_property_test.go
index 972e86b..c14f34e 100644
--- a/cc/cc_test_only_property_test.go
+++ b/cc/cc_test_only_property_test.go
@@ -78,6 +78,38 @@
}
}
+func TestTestOnlyValueWithTestPerSrcProp(t *testing.T) {
+ t.Parallel()
+ ctx := android.GroupFixturePreparers(
+ prepareForCcTest,
+ ).RunTestWithBp(t, `
+ // These should be test-only
+ cc_test { name: "cc-test",
+ gtest: false,
+ test_per_src: true,
+ srcs: ["foo_test.cpp"],
+ test_options: { unit_test: false, },
+ }
+ `)
+
+ // Ensure all variation of test-per-src tests are marked test-only.
+ ctx.VisitAllModules(func(m blueprint.Module) {
+ testOnly := false
+ if provider, ok := android.OtherModuleProvider(ctx.TestContext.OtherModuleProviderAdaptor(), m, android.TestOnlyProviderKey); ok {
+ if provider.TestOnly {
+ testOnly = true
+ }
+ }
+ if module, ok := m.(*Module); ok {
+ if testModule, ok := module.installer.(*testBinary); ok {
+ if !testOnly && *testModule.Properties.Test_per_src {
+ t.Errorf("%v is not test-only but should be", m)
+ }
+ }
+ }
+ })
+}
+
func TestTestOnlyInTeamsProto(t *testing.T) {
t.Parallel()
ctx := android.GroupFixturePreparers(
diff --git a/cmd/release_config/Android.bp b/cmd/release_config/Android.bp
deleted file mode 100644
index 7f627ff..0000000
--- a/cmd/release_config/Android.bp
+++ /dev/null
@@ -1,30 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-bootstrap_go_package {
- name: "release-config",
- pkgPath: "android/soong/cmd/release_config",
- deps: [
- "golang-protobuf-encoding-prototext",
- "golang-protobuf-reflect-protoreflect",
- "golang-protobuf-runtime-protoimpl",
- "soong-cmd-release-config-proto",
- ],
- srcs: [
- "main.go",
- ],
-}
-
-bootstrap_go_package {
- name: "soong-cmd-release-config-proto",
- pkgPath: "android/soong/cmd/release_config/release_config_proto",
- deps: [
- "golang-protobuf-reflect-protoreflect",
- "golang-protobuf-runtime-protoimpl",
- ],
- srcs: [
- "release_config_proto/build_flags_out.pb.go",
- "release_config_proto/build_flags_src.pb.go",
- ],
-}
diff --git a/cmd/release_config/main.go b/cmd/release_config/main.go
deleted file mode 100644
index 3bb6b3d..0000000
--- a/cmd/release_config/main.go
+++ /dev/null
@@ -1,691 +0,0 @@
-// 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 main
-
-import (
- "cmp"
- "encoding/json"
- "flag"
- "fmt"
- "io/fs"
- "os"
- "path/filepath"
- "slices"
- "strings"
-
- "android/soong/cmd/release_config/release_config_proto"
-
- "google.golang.org/protobuf/encoding/prototext"
- "google.golang.org/protobuf/proto"
-)
-
-var verboseFlag bool
-
-type StringList []string
-
-func (l *StringList) Set(v string) error {
- *l = append(*l, v)
- return nil
-}
-
-func (l *StringList) String() string {
- return fmt.Sprintf("%v", *l)
-}
-
-var releaseConfigMapPaths StringList
-
-func DumpProtos(outDir string, message proto.Message) error {
- basePath := filepath.Join(outDir, "all_release_configs")
- writer := func(suffix string, marshal func() ([]byte, error)) error {
- data, err := marshal()
- if err != nil {
- return err
- }
- return os.WriteFile(fmt.Sprintf("%s.%s", basePath, suffix), data, 0644)
- }
- err := writer("textproto", func() ([]byte, error) { return prototext.MarshalOptions{Multiline: true}.Marshal(message) })
- if err != nil {
- return err
- }
-
- err = writer("pb", func() ([]byte, error) { return proto.Marshal(message) })
- if err != nil {
- return err
- }
-
- return writer("json", func() ([]byte, error) { return json.MarshalIndent(message, "", " ") })
-}
-
-func LoadTextproto(path string, message proto.Message) error {
- data, err := os.ReadFile(path)
- if err != nil {
- return err
- }
- ret := prototext.Unmarshal(data, message)
- if verboseFlag {
- debug, _ := prototext.Marshal(message)
- fmt.Printf("%s: %s\n", path, debug)
- }
- return ret
-}
-
-func WalkTextprotoFiles(root string, subdir string, Func fs.WalkDirFunc) error {
- path := filepath.Join(root, subdir)
- if _, err := os.Stat(path); err != nil {
- // Missing subdirs are not an error.
- return nil
- }
- return filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
- if err != nil {
- return err
- }
- if strings.HasSuffix(d.Name(), ".textproto") && d.Type().IsRegular() {
- return Func(path, d, err)
- }
- return nil
- })
-}
-
-type FlagValue struct {
- // The path providing this value.
- path string
-
- // Protobuf
- proto release_config_proto.FlagValue
-}
-
-func FlagValueFactory(protoPath string) (fv *FlagValue) {
- fv = &FlagValue{path: protoPath}
- if protoPath != "" {
- LoadTextproto(protoPath, &fv.proto)
- }
- return fv
-}
-
-// One directory's contribution to the a release config.
-type ReleaseConfigContribution struct {
- // Paths to files providing this config.
- path string
-
- // The index of the config directory where this release config
- // contribution was declared.
- // Flag values cannot be set in a location with a lower index.
- DeclarationIndex int
-
- // Protobufs relevant to the config.
- proto release_config_proto.ReleaseConfig
-
- FlagValues []*FlagValue
-}
-
-// A single release_config_map.textproto and its associated data.
-// Used primarily for debugging.
-type ReleaseConfigMap struct {
- // The path to this release_config_map file.
- path string
-
- // Data received
- proto release_config_proto.ReleaseConfigMap
-
- ReleaseConfigContributions map[string]*ReleaseConfigContribution
- FlagDeclarations []release_config_proto.FlagDeclaration
-}
-
-// A generated release config.
-type ReleaseConfig struct {
- // the Name of the release config
- Name string
-
- // The index of the config directory where this release config was
- // first declared.
- // Flag values cannot be set in a location with a lower index.
- DeclarationIndex int
-
- // What contributes to this config.
- Contributions []*ReleaseConfigContribution
-
- // Aliases for this release
- OtherNames []string
-
- // The names of release configs that we inherit
- InheritNames []string
-
- // Unmarshalled flag artifacts
- FlagArtifacts FlagArtifacts
-
- // Generated release config
- ReleaseConfigArtifact *release_config_proto.ReleaseConfigArtifact
-
- // We have begun compiling this release config.
- compileInProgress bool
-}
-
-type FlagArtifact struct {
- FlagDeclaration *release_config_proto.FlagDeclaration
-
- // The index of the config directory where this flag was declared.
- // Flag values cannot be set in a location with a lower index.
- DeclarationIndex int
-
- Traces []*release_config_proto.Tracepoint
-
- // Assigned value
- Value *release_config_proto.Value
-}
-
-// Key is flag name.
-type FlagArtifacts map[string]*FlagArtifact
-
-type ReleaseConfigDirMap map[string]int
-
-// The generated release configs.
-type ReleaseConfigs struct {
- // Ordered list of release config maps processed.
- ReleaseConfigMaps []*ReleaseConfigMap
-
- // Aliases
- Aliases map[string]*string
-
- // Dictionary of flag_name:FlagDeclaration, with no overrides applied.
- FlagArtifacts FlagArtifacts
-
- // Dictionary of name:ReleaseConfig
- ReleaseConfigs map[string]*ReleaseConfig
-
- // Generated release configs
- Artifact release_config_proto.ReleaseConfigsArtifact
-
- // The list of config directories used.
- ConfigDirs []string
-
- // A map from the config directory to its order in the list of config
- // directories.
- ConfigDirIndexes ReleaseConfigDirMap
-}
-
-func (src *FlagArtifact) Clone() *FlagArtifact {
- value := &release_config_proto.Value{}
- proto.Merge(value, src.Value)
- return &FlagArtifact{
- FlagDeclaration: src.FlagDeclaration,
- Traces: src.Traces,
- Value: value,
- }
-}
-
-func (src FlagArtifacts) Clone() (dst FlagArtifacts) {
- if dst == nil {
- dst = make(FlagArtifacts)
- }
- for k, v := range src {
- dst[k] = v.Clone()
- }
- return
-}
-
-func ReleaseConfigFactory(name string, index int) (c *ReleaseConfig) {
- return &ReleaseConfig{Name: name, DeclarationIndex: index}
-}
-
-func ReleaseConfigsFactory() (c *ReleaseConfigs) {
- return &ReleaseConfigs{
- Aliases: make(map[string]*string),
- FlagArtifacts: make(map[string]*FlagArtifact),
- ReleaseConfigs: make(map[string]*ReleaseConfig),
- ConfigDirs: []string{},
- ConfigDirIndexes: make(ReleaseConfigDirMap),
- }
-}
-
-func ReleaseConfigMapFactory(protoPath string) (m *ReleaseConfigMap) {
- m = &ReleaseConfigMap{
- path: protoPath,
- ReleaseConfigContributions: make(map[string]*ReleaseConfigContribution),
- }
- if protoPath != "" {
- LoadTextproto(protoPath, &m.proto)
- }
- return m
-}
-
-func FlagDeclarationFactory(protoPath string) (fd *release_config_proto.FlagDeclaration) {
- fd = &release_config_proto.FlagDeclaration{}
- if protoPath != "" {
- LoadTextproto(protoPath, fd)
- }
- return fd
-}
-
-func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex int) error {
- m := ReleaseConfigMapFactory(path)
- if m.proto.Origin == nil || *m.proto.Origin == "" {
- return fmt.Errorf("Release config map %s lacks origin", path)
- }
- if m.proto.DefaultContainer == nil {
- return fmt.Errorf("Release config map %s lacks default_container", path)
- }
- dir := filepath.Dir(path)
- // Record any aliases, checking for duplicates.
- for _, alias := range m.proto.Aliases {
- name := *alias.Name
- oldTarget, ok := configs.Aliases[name]
- if ok {
- if *oldTarget != *alias.Target {
- return fmt.Errorf("Conflicting alias declarations: %s vs %s",
- *oldTarget, *alias.Target)
- }
- }
- configs.Aliases[name] = alias.Target
- }
- var err error
- err = WalkTextprotoFiles(dir, "flag_declarations", func(path string, d fs.DirEntry, err error) error {
- flagDeclaration := FlagDeclarationFactory(path)
- // Container must be specified.
- if flagDeclaration.Container == nil {
- flagDeclaration.Container = m.proto.DefaultContainer
- }
- // TODO: drop flag_declaration.origin from the proto.
- if flagDeclaration.Origin == nil {
- flagDeclaration.Origin = m.proto.Origin
- }
- // There is always a default value.
- if flagDeclaration.Value == nil {
- flagDeclaration.Value = &release_config_proto.Value{Val: &release_config_proto.Value_UnspecifiedValue{true}}
- }
- m.FlagDeclarations = append(m.FlagDeclarations, *flagDeclaration)
- name := *flagDeclaration.Name
- if def, ok := configs.FlagArtifacts[name]; !ok {
- configs.FlagArtifacts[name] = &FlagArtifact{FlagDeclaration: flagDeclaration, DeclarationIndex: ConfigDirIndex}
- } else if !proto.Equal(def.FlagDeclaration, flagDeclaration) {
- return fmt.Errorf("Duplicate definition of %s", *flagDeclaration.Name)
- }
- // Set the initial value in the flag artifact.
- configs.FlagArtifacts[name].UpdateValue(
- FlagValue{path: path, proto: release_config_proto.FlagValue{
- Name: proto.String(name), Value: flagDeclaration.Value}})
- return nil
- })
- if err != nil {
- return err
- }
-
- err = WalkTextprotoFiles(dir, "release_configs", func(path string, d fs.DirEntry, err error) error {
- releaseConfigContribution := &ReleaseConfigContribution{path: path, DeclarationIndex: ConfigDirIndex}
- LoadTextproto(path, &releaseConfigContribution.proto)
- name := *releaseConfigContribution.proto.Name
- if fmt.Sprintf("%s.textproto", name) != filepath.Base(path) {
- return fmt.Errorf("%s incorrectly declares release config %s", path, name)
- }
- if _, ok := configs.ReleaseConfigs[name]; !ok {
- configs.ReleaseConfigs[name] = ReleaseConfigFactory(name, ConfigDirIndex)
- }
- config := configs.ReleaseConfigs[name]
- config.InheritNames = append(config.InheritNames, releaseConfigContribution.proto.Inherits...)
-
- // Only walk flag_values/{RELEASE} for defined releases.
- err2 := WalkTextprotoFiles(dir, filepath.Join("flag_values", name), func(path string, d fs.DirEntry, err error) error {
- flagValue := FlagValueFactory(path)
- if fmt.Sprintf("%s.textproto", *flagValue.proto.Name) != filepath.Base(path) {
- return fmt.Errorf("%s incorrectly sets value for flag %s", path, *flagValue.proto.Name)
- }
- releaseConfigContribution.FlagValues = append(releaseConfigContribution.FlagValues, flagValue)
- return nil
- })
- if err2 != nil {
- return err2
- }
- m.ReleaseConfigContributions[name] = releaseConfigContribution
- config.Contributions = append(config.Contributions, releaseConfigContribution)
- return nil
- })
- if err != nil {
- return err
- }
- configs.ReleaseConfigMaps = append(configs.ReleaseConfigMaps, m)
- return nil
-}
-
-func (configs *ReleaseConfigs) GetReleaseConfig(name string) (*ReleaseConfig, error) {
- trace := []string{name}
- for target, ok := configs.Aliases[name]; ok; target, ok = configs.Aliases[name] {
- name = *target
- trace = append(trace, name)
- }
- if config, ok := configs.ReleaseConfigs[name]; ok {
- return config, nil
- }
- return nil, fmt.Errorf("Missing config %s. Trace=%v", name, trace)
-}
-
-func (configs *ReleaseConfigs) DumpMakefile(outDir, targetRelease string) error {
- outFile := filepath.Join(outDir, "release_config.mk")
- makeVars := make(map[string]string)
- config, err := configs.GetReleaseConfig(targetRelease)
- if err != nil {
- return err
- }
- // Sort the flags by name first.
- names := []string{}
- for k, _ := range config.FlagArtifacts {
- names = append(names, k)
- }
- slices.SortFunc(names, func(a, b string) int {
- return cmp.Compare(a, b)
- })
- partitions := make(map[string][]string)
-
- vNames := []string{}
- addVar := func(name, suffix, value string) {
- fullName := fmt.Sprintf("_ALL_RELEASE_FLAGS.%s.%s", name, suffix)
- vNames = append(vNames, fullName)
- makeVars[fullName] = value
- }
-
- for _, name := range names {
- flag := config.FlagArtifacts[name]
- decl := flag.FlagDeclaration
-
- // cName := strings.ToLower(release_config_proto.Container_name[decl.GetContainer()])
- cName := strings.ToLower(decl.Container.String())
- if cName == strings.ToLower(release_config_proto.Container_ALL.String()) {
- partitions["product"] = append(partitions["product"], name)
- partitions["system"] = append(partitions["system"], name)
- partitions["system_ext"] = append(partitions["system_ext"], name)
- partitions["vendor"] = append(partitions["vendor"], name)
- } else {
- partitions[cName] = append(partitions[cName], name)
- }
- value := MarshalValue(flag.Value)
- makeVars[name] = value
- addVar(name, "PARTITIONS", cName)
- addVar(name, "DEFAULT", MarshalValue(decl.Value))
- addVar(name, "VALUE", value)
- addVar(name, "DECLARED_IN", *flag.Traces[0].Source)
- addVar(name, "SET_IN", *flag.Traces[len(flag.Traces)-1].Source)
- addVar(name, "ORIGIN", *decl.Origin)
- }
- pNames := []string{}
- for k, _ := range partitions {
- pNames = append(pNames, k)
- }
- slices.SortFunc(pNames, func(a, b string) int {
- return cmp.Compare(a, b)
- })
-
- // Now sort the make variables, and output them.
- slices.SortFunc(vNames, func(a, b string) int {
- return cmp.Compare(a, b)
- })
-
- // Write the flags as:
- // _ALL_RELELASE_FLAGS
- // _ALL_RELEASE_FLAGS.PARTITIONS.*
- // all _ALL_RELEASE_FLAGS.*, sorted by name
- // Final flag values, sorted by name.
- data := fmt.Sprintf("_ALL_RELEASE_FLAGS :=$= %s\n", strings.Join(names, " "))
- for _, pName := range pNames {
- data += fmt.Sprintf("_ALL_RELEASE_FLAGS.PARTITIONS.%s :=$= %s\n", pName, strings.Join(partitions[pName], " "))
- }
- for _, vName := range vNames {
- data += fmt.Sprintf("%s :=$= %s\n", vName, makeVars[vName])
- }
- data += "\n\n# Values for all build flags\n"
- data += fmt.Sprintf("RELEASE_ACONFIG_VALUE_SETS :=$= %s\n",
- strings.Join(config.ReleaseConfigArtifact.AconfigValueSets, " "))
- for _, name := range names {
- data += fmt.Sprintf("%s :=$= %s\n", name, makeVars[name])
- }
- return os.WriteFile(outFile, []byte(data), 0644)
-}
-
-func (configs *ReleaseConfigs) GenerateReleaseConfigs(targetRelease string) error {
- otherNames := make(map[string][]string)
- for aliasName, aliasTarget := range configs.Aliases {
- if _, ok := configs.ReleaseConfigs[aliasName]; ok {
- return fmt.Errorf("Alias %s is a declared release config", aliasName)
- }
- if _, ok := configs.ReleaseConfigs[*aliasTarget]; !ok {
- if _, ok2 := configs.Aliases[*aliasTarget]; !ok2 {
- return fmt.Errorf("Alias %s points to non-existing config %s", aliasName, *aliasTarget)
- }
- }
- otherNames[*aliasTarget] = append(otherNames[*aliasTarget], aliasName)
- }
- for name, aliases := range otherNames {
- configs.ReleaseConfigs[name].OtherNames = aliases
- }
-
- for _, config := range configs.ReleaseConfigs {
- err := config.GenerateReleaseConfig(configs)
- if err != nil {
- return err
- }
- }
-
- releaseConfig, err := configs.GetReleaseConfig(targetRelease)
- if err != nil {
- return err
- }
- configs.Artifact = release_config_proto.ReleaseConfigsArtifact{
- ReleaseConfig: releaseConfig.ReleaseConfigArtifact,
- OtherReleaseConfigs: func() []*release_config_proto.ReleaseConfigArtifact {
- orc := []*release_config_proto.ReleaseConfigArtifact{}
- for name, config := range configs.ReleaseConfigs {
- if name != releaseConfig.Name {
- orc = append(orc, config.ReleaseConfigArtifact)
- }
- }
- return orc
- }(),
- }
- return nil
-}
-
-func MarshalValue(value *release_config_proto.Value) string {
- switch val := value.Val.(type) {
- case *release_config_proto.Value_UnspecifiedValue:
- // Value was never set.
- return ""
- case *release_config_proto.Value_StringValue:
- return val.StringValue
- case *release_config_proto.Value_BoolValue:
- if val.BoolValue {
- return "true"
- }
- // False ==> empty string
- return ""
- case *release_config_proto.Value_Obsolete:
- return " #OBSOLETE"
- default:
- // Flagged as error elsewhere, so return empty string here.
- return ""
- }
-}
-
-func (fa *FlagArtifact) UpdateValue(flagValue FlagValue) error {
- name := *flagValue.proto.Name
- fa.Traces = append(fa.Traces, &release_config_proto.Tracepoint{Source: proto.String(flagValue.path), Value: flagValue.proto.Value})
- if fa.Value.GetObsolete() {
- return fmt.Errorf("Attempting to set obsolete flag %s. Trace=%v", name, fa.Traces)
- }
- switch val := flagValue.proto.Value.Val.(type) {
- case *release_config_proto.Value_StringValue:
- fa.Value = &release_config_proto.Value{Val: &release_config_proto.Value_StringValue{val.StringValue}}
- case *release_config_proto.Value_BoolValue:
- fa.Value = &release_config_proto.Value{Val: &release_config_proto.Value_BoolValue{val.BoolValue}}
- case *release_config_proto.Value_Obsolete:
- if !val.Obsolete {
- return fmt.Errorf("%s: Cannot set obsolete=false. Trace=%v", name, fa.Traces)
- }
- fa.Value = &release_config_proto.Value{Val: &release_config_proto.Value_Obsolete{true}}
- default:
- return fmt.Errorf("Invalid type for flag_value: %T. Trace=%v", val, fa.Traces)
- }
- return nil
-}
-
-func (fa *FlagArtifact) Marshal() (*release_config_proto.FlagArtifact, error) {
- return &release_config_proto.FlagArtifact{
- FlagDeclaration: fa.FlagDeclaration,
- Value: fa.Value,
- Traces: fa.Traces,
- }, nil
-}
-
-func (config *ReleaseConfig) GenerateReleaseConfig(configs *ReleaseConfigs) error {
- if config.ReleaseConfigArtifact != nil {
- return nil
- }
- if config.compileInProgress {
- return fmt.Errorf("Loop detected for release config %s", config.Name)
- }
- config.compileInProgress = true
-
- // Generate any configs we need to inherit. This will detect loops in
- // the config.
- contributionsToApply := []*ReleaseConfigContribution{}
- myInherits := []string{}
- myInheritsSet := make(map[string]bool)
- for _, inherit := range config.InheritNames {
- if _, ok := myInheritsSet[inherit]; ok {
- continue
- }
- myInherits = append(myInherits, inherit)
- myInheritsSet[inherit] = true
- iConfig, err := configs.GetReleaseConfig(inherit)
- if err != nil {
- return err
- }
- iConfig.GenerateReleaseConfig(configs)
- contributionsToApply = append(contributionsToApply, iConfig.Contributions...)
- }
- contributionsToApply = append(contributionsToApply, config.Contributions...)
-
- myAconfigValueSets := []string{}
- myFlags := configs.FlagArtifacts.Clone()
- myDirsMap := make(map[int]bool)
- for _, contrib := range contributionsToApply {
- myAconfigValueSets = append(myAconfigValueSets, contrib.proto.AconfigValueSets...)
- myDirsMap[contrib.DeclarationIndex] = true
- for _, value := range contrib.FlagValues {
- fa, ok := myFlags[*value.proto.Name]
- if !ok {
- return fmt.Errorf("Setting value for undefined flag %s in %s\n", *value.proto.Name, value.path)
- }
- myDirsMap[fa.DeclarationIndex] = true
- if fa.DeclarationIndex > contrib.DeclarationIndex {
- // Setting location is to the left of declaration.
- return fmt.Errorf("Setting value for flag %s not allowed in %s\n", *value.proto.Name, value.path)
- }
- if err := fa.UpdateValue(*value); err != nil {
- return err
- }
- }
- }
-
- directories := []string{}
- for idx, confDir := range configs.ConfigDirs {
- if _, ok := myDirsMap[idx]; ok {
- directories = append(directories, confDir)
- }
- }
-
- config.FlagArtifacts = myFlags
- config.ReleaseConfigArtifact = &release_config_proto.ReleaseConfigArtifact{
- Name: proto.String(config.Name),
- OtherNames: config.OtherNames,
- FlagArtifacts: func() []*release_config_proto.FlagArtifact {
- ret := []*release_config_proto.FlagArtifact{}
- for _, flag := range myFlags {
- ret = append(ret, &release_config_proto.FlagArtifact{
- FlagDeclaration: flag.FlagDeclaration,
- Traces: flag.Traces,
- Value: flag.Value,
- })
- }
- return ret
- }(),
- AconfigValueSets: myAconfigValueSets,
- Inherits: myInherits,
- Directories: directories,
- }
-
- config.compileInProgress = false
- return nil
-}
-
-func main() {
- var targetRelease string
- var outputDir string
-
- outEnv := os.Getenv("OUT_DIR")
- if outEnv == "" {
- outEnv = "out"
- }
- defaultOutputDir := filepath.Join(outEnv, "soong", "release-config")
- var defaultMapPaths StringList
- defaultLocations := StringList{
- "build/release/release_config_map.textproto",
- "vendor/google_shared/build/release/release_config_map.textproto",
- "vendor/google/release/release_config_map.textproto",
- }
- for _, path := range defaultLocations {
- if _, err := os.Stat(path); err == nil {
- defaultMapPaths = append(defaultMapPaths, path)
- }
- }
- prodMaps := os.Getenv("PRODUCT_RELEASE_CONFIG_MAPS")
- if prodMaps != "" {
- defaultMapPaths = append(defaultMapPaths, strings.Split(prodMaps, " ")...)
- }
-
- flag.BoolVar(&verboseFlag, "debug", false, "print debugging information")
- flag.Var(&releaseConfigMapPaths, "map", "path to a release_config_map.textproto. may be repeated")
- flag.StringVar(&targetRelease, "release", "trunk_staging", "TARGET_RELEASE for this build")
- flag.StringVar(&outputDir, "out_dir", defaultOutputDir, "basepath for the output. Multiple formats are created")
- flag.Parse()
-
- if len(releaseConfigMapPaths) == 0 {
- releaseConfigMapPaths = defaultMapPaths
- fmt.Printf("No --map argument provided. Using: --map %s\n", strings.Join(releaseConfigMapPaths, " --map "))
- }
-
- configs := ReleaseConfigsFactory()
- for idx, releaseConfigMapPath := range releaseConfigMapPaths {
- // Maintain an ordered list of release config directories.
- configDir := filepath.Dir(releaseConfigMapPath)
- configs.ConfigDirIndexes[configDir] = idx
- configs.ConfigDirs = append(configs.ConfigDirs, configDir)
- err := configs.LoadReleaseConfigMap(releaseConfigMapPath, idx)
- if err != nil {
- panic(err)
- }
- }
-
- // Now that we have all of the release config maps, can meld them and generate the artifacts.
- err := configs.GenerateReleaseConfigs(targetRelease)
- if err != nil {
- panic(err)
- }
- err = os.MkdirAll(outputDir, 0775)
- if err != nil {
- panic(err)
- }
- err = configs.DumpMakefile(outputDir, targetRelease)
- if err != nil {
- panic(err)
- }
- DumpProtos(outputDir, &configs.Artifact)
-}
diff --git a/cmd/release_config/release_config/Android.bp b/cmd/release_config/release_config/Android.bp
new file mode 100644
index 0000000..3c73826
--- /dev/null
+++ b/cmd/release_config/release_config/Android.bp
@@ -0,0 +1,18 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-cmd-release_config-release_config",
+ pkgPath: "android/soong/cmd/release_config/release_config",
+ deps: [
+ "golang-protobuf-encoding-prototext",
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ "soong-cmd-release_config-proto",
+ "soong-cmd-release_config-lib",
+ ],
+ srcs: [
+ "main.go",
+ ],
+}
diff --git a/cmd/release_config/release_config/main.go b/cmd/release_config/release_config/main.go
new file mode 100644
index 0000000..076abfa
--- /dev/null
+++ b/cmd/release_config/release_config/main.go
@@ -0,0 +1,57 @@
+// 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 main
+
+import (
+ "flag"
+ "os"
+
+ rc_lib "android/soong/cmd/release_config/release_config_lib"
+)
+
+func main() {
+ var top string
+ var releaseConfigMapPaths rc_lib.StringList
+ var targetRelease string
+ var outputDir string
+ var err error
+ var configs *rc_lib.ReleaseConfigs
+
+ flag.StringVar(&top, "top", ".", "path to top of workspace")
+ flag.Var(&releaseConfigMapPaths, "map", "path to a release_config_map.textproto. may be repeated")
+ flag.StringVar(&targetRelease, "release", "trunk_staging", "TARGET_RELEASE for this build")
+ flag.StringVar(&outputDir, "out_dir", rc_lib.GetDefaultOutDir(), "basepath for the output. Multiple formats are created")
+ flag.Parse()
+
+ if err = os.Chdir(top); err != nil {
+ panic(err)
+ }
+ configs, err = rc_lib.ReadReleaseConfigMaps(releaseConfigMapPaths, targetRelease)
+ if err != nil {
+ panic(err)
+ }
+ err = os.MkdirAll(outputDir, 0775)
+ if err != nil {
+ panic(err)
+ }
+ err = configs.DumpMakefile(outputDir, targetRelease)
+ if err != nil {
+ panic(err)
+ }
+ err = configs.DumpArtifact(outputDir)
+ if err != nil {
+ panic(err)
+ }
+}
diff --git a/cmd/release_config/release_config_lib/Android.bp b/cmd/release_config/release_config_lib/Android.bp
new file mode 100644
index 0000000..601194c
--- /dev/null
+++ b/cmd/release_config/release_config_lib/Android.bp
@@ -0,0 +1,36 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-cmd-release_config-lib",
+ pkgPath: "android/soong/release_config/release_config_lib",
+ deps: [
+ "golang-protobuf-encoding-prototext",
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ "soong-cmd-release_config-proto",
+ ],
+ srcs: [
+ "flag_artifact.go",
+ "flag_declaration.go",
+ "flag_value.go",
+ "release_config.go",
+ "release_configs.go",
+ "util.go",
+ ],
+}
diff --git a/cmd/release_config/release_config_lib/flag_artifact.go b/cmd/release_config/release_config_lib/flag_artifact.go
new file mode 100644
index 0000000..51673a5
--- /dev/null
+++ b/cmd/release_config/release_config_lib/flag_artifact.go
@@ -0,0 +1,89 @@
+// 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 release_config_lib
+
+import (
+ "fmt"
+
+ "android/soong/cmd/release_config/release_config_proto"
+
+ "google.golang.org/protobuf/proto"
+)
+
+type FlagArtifact struct {
+ FlagDeclaration *release_config_proto.FlagDeclaration
+
+ // The index of the config directory where this flag was declared.
+ // Flag values cannot be set in a location with a lower index.
+ DeclarationIndex int
+
+ Traces []*release_config_proto.Tracepoint
+
+ // Assigned value
+ Value *release_config_proto.Value
+}
+
+// Key is flag name.
+type FlagArtifacts map[string]*FlagArtifact
+
+func (src *FlagArtifact) Clone() *FlagArtifact {
+ value := &release_config_proto.Value{}
+ proto.Merge(value, src.Value)
+ return &FlagArtifact{
+ FlagDeclaration: src.FlagDeclaration,
+ Traces: src.Traces,
+ Value: value,
+ }
+}
+
+func (src FlagArtifacts) Clone() (dst FlagArtifacts) {
+ if dst == nil {
+ dst = make(FlagArtifacts)
+ }
+ for k, v := range src {
+ dst[k] = v.Clone()
+ }
+ return
+}
+
+func (fa *FlagArtifact) UpdateValue(flagValue FlagValue) error {
+ name := *flagValue.proto.Name
+ fa.Traces = append(fa.Traces, &release_config_proto.Tracepoint{Source: proto.String(flagValue.path), Value: flagValue.proto.Value})
+ if fa.Value.GetObsolete() {
+ return fmt.Errorf("Attempting to set obsolete flag %s. Trace=%v", name, fa.Traces)
+ }
+ switch val := flagValue.proto.Value.Val.(type) {
+ case *release_config_proto.Value_StringValue:
+ fa.Value = &release_config_proto.Value{Val: &release_config_proto.Value_StringValue{val.StringValue}}
+ case *release_config_proto.Value_BoolValue:
+ fa.Value = &release_config_proto.Value{Val: &release_config_proto.Value_BoolValue{val.BoolValue}}
+ case *release_config_proto.Value_Obsolete:
+ if !val.Obsolete {
+ return fmt.Errorf("%s: Cannot set obsolete=false. Trace=%v", name, fa.Traces)
+ }
+ fa.Value = &release_config_proto.Value{Val: &release_config_proto.Value_Obsolete{true}}
+ default:
+ return fmt.Errorf("Invalid type for flag_value: %T. Trace=%v", val, fa.Traces)
+ }
+ return nil
+}
+
+func (fa *FlagArtifact) Marshal() (*release_config_proto.FlagArtifact, error) {
+ return &release_config_proto.FlagArtifact{
+ FlagDeclaration: fa.FlagDeclaration,
+ Value: fa.Value,
+ Traces: fa.Traces,
+ }, nil
+}
diff --git a/cmd/release_config/release_config_lib/flag_declaration.go b/cmd/release_config/release_config_lib/flag_declaration.go
new file mode 100644
index 0000000..d5cc418
--- /dev/null
+++ b/cmd/release_config/release_config_lib/flag_declaration.go
@@ -0,0 +1,27 @@
+// 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 release_config_lib
+
+import (
+ "android/soong/cmd/release_config/release_config_proto"
+)
+
+func FlagDeclarationFactory(protoPath string) (fd *release_config_proto.FlagDeclaration) {
+ fd = &release_config_proto.FlagDeclaration{}
+ if protoPath != "" {
+ LoadTextproto(protoPath, fd)
+ }
+ return fd
+}
diff --git a/cmd/release_config/release_config_lib/flag_value.go b/cmd/release_config/release_config_lib/flag_value.go
new file mode 100644
index 0000000..138e8f8
--- /dev/null
+++ b/cmd/release_config/release_config_lib/flag_value.go
@@ -0,0 +1,56 @@
+// 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 release_config_lib
+
+import (
+ "android/soong/cmd/release_config/release_config_proto"
+)
+
+type FlagValue struct {
+ // The path providing this value.
+ path string
+
+ // Protobuf
+ proto release_config_proto.FlagValue
+}
+
+func FlagValueFactory(protoPath string) (fv *FlagValue) {
+ fv = &FlagValue{path: protoPath}
+ if protoPath != "" {
+ LoadTextproto(protoPath, &fv.proto)
+ }
+ return fv
+}
+
+func MarshalValue(value *release_config_proto.Value) string {
+ switch val := value.Val.(type) {
+ case *release_config_proto.Value_UnspecifiedValue:
+ // Value was never set.
+ return ""
+ case *release_config_proto.Value_StringValue:
+ return val.StringValue
+ case *release_config_proto.Value_BoolValue:
+ if val.BoolValue {
+ return "true"
+ }
+ // False ==> empty string
+ return ""
+ case *release_config_proto.Value_Obsolete:
+ return " #OBSOLETE"
+ default:
+ // Flagged as error elsewhere, so return empty string here.
+ return ""
+ }
+}
diff --git a/cmd/release_config/release_config_lib/flag_value_test.go b/cmd/release_config/release_config_lib/flag_value_test.go
new file mode 100644
index 0000000..aaa4caf
--- /dev/null
+++ b/cmd/release_config/release_config_lib/flag_value_test.go
@@ -0,0 +1,67 @@
+// 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 release_config_lib
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+
+ rc_proto "android/soong/cmd/release_config/release_config_proto"
+
+ "google.golang.org/protobuf/proto"
+)
+
+type testCaseFlagValue struct {
+ protoPath string
+ name string
+ data []byte
+ expected rc_proto.FlagValue
+ err error
+}
+
+func (tc testCaseFlagValue) assertProtoEqual(t *testing.T, expected, actual proto.Message) {
+ if !proto.Equal(expected, actual) {
+ t.Errorf("Expected %q found %q", expected, actual)
+ }
+}
+
+func TestFlagValue(t *testing.T) {
+ testCases := []testCaseFlagValue{
+ {
+ name: "stringVal",
+ protoPath: "build/release/flag_values/test/RELEASE_FOO.textproto",
+ data: []byte(`name: "RELEASE_FOO" value {string_value: "BAR"}`),
+ expected: rc_proto.FlagValue{
+ Name: proto.String("RELEASE_FOO"),
+ Value: &rc_proto.Value{Val: &rc_proto.Value_StringValue{"BAR"}},
+ },
+ err: nil,
+ },
+ }
+ for _, tc := range testCases {
+ var err error
+ tempdir := t.TempDir()
+ path := filepath.Join(tempdir, tc.protoPath)
+ if err = os.MkdirAll(filepath.Dir(path), 0755); err != nil {
+ t.Fatal(err)
+ }
+ if err = os.WriteFile(path, tc.data, 0644); err != nil {
+ t.Fatal(err)
+ }
+ actual := FlagValueFactory(path)
+ tc.assertProtoEqual(t, &tc.expected, &actual.proto)
+ }
+}
diff --git a/cmd/release_config/release_config_lib/release_config.go b/cmd/release_config/release_config_lib/release_config.go
new file mode 100644
index 0000000..3110dae
--- /dev/null
+++ b/cmd/release_config/release_config_lib/release_config.go
@@ -0,0 +1,154 @@
+// 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 release_config_lib
+
+import (
+ "fmt"
+
+ "android/soong/cmd/release_config/release_config_proto"
+
+ "google.golang.org/protobuf/proto"
+)
+
+// One directory's contribution to the a release config.
+type ReleaseConfigContribution struct {
+ // Paths to files providing this config.
+ path string
+
+ // The index of the config directory where this release config
+ // contribution was declared.
+ // Flag values cannot be set in a location with a lower index.
+ DeclarationIndex int
+
+ // Protobufs relevant to the config.
+ proto release_config_proto.ReleaseConfig
+
+ FlagValues []*FlagValue
+}
+
+// A generated release config.
+type ReleaseConfig struct {
+ // the Name of the release config
+ Name string
+
+ // The index of the config directory where this release config was
+ // first declared.
+ // Flag values cannot be set in a location with a lower index.
+ DeclarationIndex int
+
+ // What contributes to this config.
+ Contributions []*ReleaseConfigContribution
+
+ // Aliases for this release
+ OtherNames []string
+
+ // The names of release configs that we inherit
+ InheritNames []string
+
+ // Unmarshalled flag artifacts
+ FlagArtifacts FlagArtifacts
+
+ // Generated release config
+ ReleaseConfigArtifact *release_config_proto.ReleaseConfigArtifact
+
+ // We have begun compiling this release config.
+ compileInProgress bool
+}
+
+func ReleaseConfigFactory(name string, index int) (c *ReleaseConfig) {
+ return &ReleaseConfig{Name: name, DeclarationIndex: index}
+}
+
+func (config *ReleaseConfig) GenerateReleaseConfig(configs *ReleaseConfigs) error {
+ if config.ReleaseConfigArtifact != nil {
+ return nil
+ }
+ if config.compileInProgress {
+ return fmt.Errorf("Loop detected for release config %s", config.Name)
+ }
+ config.compileInProgress = true
+
+ // Generate any configs we need to inherit. This will detect loops in
+ // the config.
+ contributionsToApply := []*ReleaseConfigContribution{}
+ myInherits := []string{}
+ myInheritsSet := make(map[string]bool)
+ for _, inherit := range config.InheritNames {
+ if _, ok := myInheritsSet[inherit]; ok {
+ continue
+ }
+ myInherits = append(myInherits, inherit)
+ myInheritsSet[inherit] = true
+ iConfig, err := configs.GetReleaseConfig(inherit)
+ if err != nil {
+ return err
+ }
+ iConfig.GenerateReleaseConfig(configs)
+ contributionsToApply = append(contributionsToApply, iConfig.Contributions...)
+ }
+ contributionsToApply = append(contributionsToApply, config.Contributions...)
+
+ myAconfigValueSets := []string{}
+ myFlags := configs.FlagArtifacts.Clone()
+ myDirsMap := make(map[int]bool)
+ for _, contrib := range contributionsToApply {
+ myAconfigValueSets = append(myAconfigValueSets, contrib.proto.AconfigValueSets...)
+ myDirsMap[contrib.DeclarationIndex] = true
+ for _, value := range contrib.FlagValues {
+ fa, ok := myFlags[*value.proto.Name]
+ if !ok {
+ return fmt.Errorf("Setting value for undefined flag %s in %s\n", *value.proto.Name, value.path)
+ }
+ myDirsMap[fa.DeclarationIndex] = true
+ if fa.DeclarationIndex > contrib.DeclarationIndex {
+ // Setting location is to the left of declaration.
+ return fmt.Errorf("Setting value for flag %s not allowed in %s\n", *value.proto.Name, value.path)
+ }
+ if err := fa.UpdateValue(*value); err != nil {
+ return err
+ }
+ }
+ }
+
+ directories := []string{}
+ for idx, confDir := range configs.ConfigDirs {
+ if _, ok := myDirsMap[idx]; ok {
+ directories = append(directories, confDir)
+ }
+ }
+
+ config.FlagArtifacts = myFlags
+ config.ReleaseConfigArtifact = &release_config_proto.ReleaseConfigArtifact{
+ Name: proto.String(config.Name),
+ OtherNames: config.OtherNames,
+ FlagArtifacts: func() []*release_config_proto.FlagArtifact {
+ ret := []*release_config_proto.FlagArtifact{}
+ for _, flag := range myFlags {
+ ret = append(ret, &release_config_proto.FlagArtifact{
+ FlagDeclaration: flag.FlagDeclaration,
+ Traces: flag.Traces,
+ Value: flag.Value,
+ })
+ }
+ return ret
+ }(),
+ AconfigValueSets: myAconfigValueSets,
+ Inherits: myInherits,
+ Directories: directories,
+ }
+
+ config.compileInProgress = false
+ return nil
+}
diff --git a/cmd/release_config/release_config_lib/release_configs.go b/cmd/release_config/release_config_lib/release_configs.go
new file mode 100644
index 0000000..74fdc00
--- /dev/null
+++ b/cmd/release_config/release_config_lib/release_configs.go
@@ -0,0 +1,365 @@
+// 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 release_config_lib
+
+import (
+ "cmp"
+ "encoding/json"
+ "fmt"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "slices"
+ "strings"
+
+ "android/soong/cmd/release_config/release_config_proto"
+
+ "google.golang.org/protobuf/encoding/prototext"
+ "google.golang.org/protobuf/proto"
+)
+
+// A single release_config_map.textproto and its associated data.
+// Used primarily for debugging.
+type ReleaseConfigMap struct {
+ // The path to this release_config_map file.
+ path string
+
+ // Data received
+ proto release_config_proto.ReleaseConfigMap
+
+ ReleaseConfigContributions map[string]*ReleaseConfigContribution
+ FlagDeclarations []release_config_proto.FlagDeclaration
+}
+
+type ReleaseConfigDirMap map[string]int
+
+// The generated release configs.
+type ReleaseConfigs struct {
+ // Ordered list of release config maps processed.
+ ReleaseConfigMaps []*ReleaseConfigMap
+
+ // Aliases
+ Aliases map[string]*string
+
+ // Dictionary of flag_name:FlagDeclaration, with no overrides applied.
+ FlagArtifacts FlagArtifacts
+
+ // Dictionary of name:ReleaseConfig
+ ReleaseConfigs map[string]*ReleaseConfig
+
+ // Generated release configs
+ Artifact release_config_proto.ReleaseConfigsArtifact
+
+ // The list of config directories used.
+ ConfigDirs []string
+
+ // A map from the config directory to its order in the list of config
+ // directories.
+ ConfigDirIndexes ReleaseConfigDirMap
+}
+
+func (configs *ReleaseConfigs) DumpArtifact(outDir string) error {
+ message := &configs.Artifact
+ basePath := filepath.Join(outDir, "all_release_configs")
+ writer := func(suffix string, marshal func() ([]byte, error)) error {
+ data, err := marshal()
+ if err != nil {
+ return err
+ }
+ return os.WriteFile(fmt.Sprintf("%s.%s", basePath, suffix), data, 0644)
+ }
+ err := writer("textproto", func() ([]byte, error) { return prototext.MarshalOptions{Multiline: true}.Marshal(message) })
+ if err != nil {
+ return err
+ }
+
+ err = writer("pb", func() ([]byte, error) { return proto.Marshal(message) })
+ if err != nil {
+ return err
+ }
+
+ return writer("json", func() ([]byte, error) { return json.MarshalIndent(message, "", " ") })
+}
+
+func ReleaseConfigsFactory() (c *ReleaseConfigs) {
+ return &ReleaseConfigs{
+ Aliases: make(map[string]*string),
+ FlagArtifacts: make(map[string]*FlagArtifact),
+ ReleaseConfigs: make(map[string]*ReleaseConfig),
+ ConfigDirs: []string{},
+ ConfigDirIndexes: make(ReleaseConfigDirMap),
+ }
+}
+
+func ReleaseConfigMapFactory(protoPath string) (m *ReleaseConfigMap) {
+ m = &ReleaseConfigMap{
+ path: protoPath,
+ ReleaseConfigContributions: make(map[string]*ReleaseConfigContribution),
+ }
+ if protoPath != "" {
+ LoadTextproto(protoPath, &m.proto)
+ }
+ return m
+}
+
+func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex int) error {
+ m := ReleaseConfigMapFactory(path)
+ if m.proto.DefaultContainer == nil {
+ return fmt.Errorf("Release config map %s lacks default_container", path)
+ }
+ dir := filepath.Dir(path)
+ // Record any aliases, checking for duplicates.
+ for _, alias := range m.proto.Aliases {
+ name := *alias.Name
+ oldTarget, ok := configs.Aliases[name]
+ if ok {
+ if *oldTarget != *alias.Target {
+ return fmt.Errorf("Conflicting alias declarations: %s vs %s",
+ *oldTarget, *alias.Target)
+ }
+ }
+ configs.Aliases[name] = alias.Target
+ }
+ var err error
+ err = WalkTextprotoFiles(dir, "flag_declarations", func(path string, d fs.DirEntry, err error) error {
+ flagDeclaration := FlagDeclarationFactory(path)
+ // Container must be specified.
+ if flagDeclaration.Container == nil {
+ flagDeclaration.Container = m.proto.DefaultContainer
+ }
+ // TODO: once we have namespaces initialized, we can throw an error here.
+ if flagDeclaration.Namespace == nil {
+ flagDeclaration.Namespace = proto.String("android_UNKNOWN")
+ }
+ // If the input didn't specify a value, create one (== UnspecifiedValue).
+ if flagDeclaration.Value == nil {
+ flagDeclaration.Value = &release_config_proto.Value{Val: &release_config_proto.Value_UnspecifiedValue{false}}
+ }
+ m.FlagDeclarations = append(m.FlagDeclarations, *flagDeclaration)
+ name := *flagDeclaration.Name
+ if def, ok := configs.FlagArtifacts[name]; !ok {
+ configs.FlagArtifacts[name] = &FlagArtifact{FlagDeclaration: flagDeclaration, DeclarationIndex: ConfigDirIndex}
+ } else if !proto.Equal(def.FlagDeclaration, flagDeclaration) {
+ return fmt.Errorf("Duplicate definition of %s", *flagDeclaration.Name)
+ }
+ // Set the initial value in the flag artifact.
+ configs.FlagArtifacts[name].UpdateValue(
+ FlagValue{path: path, proto: release_config_proto.FlagValue{
+ Name: proto.String(name), Value: flagDeclaration.Value}})
+ return nil
+ })
+ if err != nil {
+ return err
+ }
+
+ err = WalkTextprotoFiles(dir, "release_configs", func(path string, d fs.DirEntry, err error) error {
+ releaseConfigContribution := &ReleaseConfigContribution{path: path, DeclarationIndex: ConfigDirIndex}
+ LoadTextproto(path, &releaseConfigContribution.proto)
+ name := *releaseConfigContribution.proto.Name
+ if fmt.Sprintf("%s.textproto", name) != filepath.Base(path) {
+ return fmt.Errorf("%s incorrectly declares release config %s", path, name)
+ }
+ if _, ok := configs.ReleaseConfigs[name]; !ok {
+ configs.ReleaseConfigs[name] = ReleaseConfigFactory(name, ConfigDirIndex)
+ }
+ config := configs.ReleaseConfigs[name]
+ config.InheritNames = append(config.InheritNames, releaseConfigContribution.proto.Inherits...)
+
+ // Only walk flag_values/{RELEASE} for defined releases.
+ err2 := WalkTextprotoFiles(dir, filepath.Join("flag_values", name), func(path string, d fs.DirEntry, err error) error {
+ flagValue := FlagValueFactory(path)
+ if fmt.Sprintf("%s.textproto", *flagValue.proto.Name) != filepath.Base(path) {
+ return fmt.Errorf("%s incorrectly sets value for flag %s", path, *flagValue.proto.Name)
+ }
+ releaseConfigContribution.FlagValues = append(releaseConfigContribution.FlagValues, flagValue)
+ return nil
+ })
+ if err2 != nil {
+ return err2
+ }
+ m.ReleaseConfigContributions[name] = releaseConfigContribution
+ config.Contributions = append(config.Contributions, releaseConfigContribution)
+ return nil
+ })
+ if err != nil {
+ return err
+ }
+ configs.ReleaseConfigMaps = append(configs.ReleaseConfigMaps, m)
+ return nil
+}
+
+func (configs *ReleaseConfigs) GetReleaseConfig(name string) (*ReleaseConfig, error) {
+ trace := []string{name}
+ for target, ok := configs.Aliases[name]; ok; target, ok = configs.Aliases[name] {
+ name = *target
+ trace = append(trace, name)
+ }
+ if config, ok := configs.ReleaseConfigs[name]; ok {
+ return config, nil
+ }
+ return nil, fmt.Errorf("Missing config %s. Trace=%v", name, trace)
+}
+
+func (configs *ReleaseConfigs) DumpMakefile(outDir, targetRelease string) error {
+ outFile := filepath.Join(outDir, "release_config.mk")
+ makeVars := make(map[string]string)
+ config, err := configs.GetReleaseConfig(targetRelease)
+ if err != nil {
+ return err
+ }
+ // Sort the flags by name first.
+ names := []string{}
+ for k, _ := range config.FlagArtifacts {
+ names = append(names, k)
+ }
+ slices.SortFunc(names, func(a, b string) int {
+ return cmp.Compare(a, b)
+ })
+ partitions := make(map[string][]string)
+
+ vNames := []string{}
+ addVar := func(name, suffix, value string) {
+ fullName := fmt.Sprintf("_ALL_RELEASE_FLAGS.%s.%s", name, suffix)
+ vNames = append(vNames, fullName)
+ makeVars[fullName] = value
+ }
+
+ for _, name := range names {
+ flag := config.FlagArtifacts[name]
+ decl := flag.FlagDeclaration
+
+ // cName := strings.ToLower(release_config_proto.Container_name[decl.GetContainer()])
+ cName := strings.ToLower(decl.Container.String())
+ if cName == strings.ToLower(release_config_proto.Container_ALL.String()) {
+ partitions["product"] = append(partitions["product"], name)
+ partitions["system"] = append(partitions["system"], name)
+ partitions["system_ext"] = append(partitions["system_ext"], name)
+ partitions["vendor"] = append(partitions["vendor"], name)
+ } else {
+ partitions[cName] = append(partitions[cName], name)
+ }
+ value := MarshalValue(flag.Value)
+ makeVars[name] = value
+ addVar(name, "PARTITIONS", cName)
+ addVar(name, "DEFAULT", MarshalValue(decl.Value))
+ addVar(name, "VALUE", value)
+ addVar(name, "DECLARED_IN", *flag.Traces[0].Source)
+ addVar(name, "SET_IN", *flag.Traces[len(flag.Traces)-1].Source)
+ addVar(name, "NAMESPACE", *decl.Namespace)
+ }
+ pNames := []string{}
+ for k, _ := range partitions {
+ pNames = append(pNames, k)
+ }
+ slices.SortFunc(pNames, func(a, b string) int {
+ return cmp.Compare(a, b)
+ })
+
+ // Now sort the make variables, and output them.
+ slices.SortFunc(vNames, func(a, b string) int {
+ return cmp.Compare(a, b)
+ })
+
+ // Write the flags as:
+ // _ALL_RELELASE_FLAGS
+ // _ALL_RELEASE_FLAGS.PARTITIONS.*
+ // all _ALL_RELEASE_FLAGS.*, sorted by name
+ // Final flag values, sorted by name.
+ data := fmt.Sprintf("_ALL_RELEASE_FLAGS :=$= %s\n", strings.Join(names, " "))
+ for _, pName := range pNames {
+ data += fmt.Sprintf("_ALL_RELEASE_FLAGS.PARTITIONS.%s :=$= %s\n", pName, strings.Join(partitions[pName], " "))
+ }
+ for _, vName := range vNames {
+ data += fmt.Sprintf("%s :=$= %s\n", vName, makeVars[vName])
+ }
+ data += "\n\n# Values for all build flags\n"
+ data += fmt.Sprintf("RELEASE_ACONFIG_VALUE_SETS :=$= %s\n",
+ strings.Join(config.ReleaseConfigArtifact.AconfigValueSets, " "))
+ for _, name := range names {
+ data += fmt.Sprintf("%s :=$= %s\n", name, makeVars[name])
+ }
+ return os.WriteFile(outFile, []byte(data), 0644)
+}
+
+func (configs *ReleaseConfigs) GenerateReleaseConfigs(targetRelease string) error {
+ otherNames := make(map[string][]string)
+ for aliasName, aliasTarget := range configs.Aliases {
+ if _, ok := configs.ReleaseConfigs[aliasName]; ok {
+ return fmt.Errorf("Alias %s is a declared release config", aliasName)
+ }
+ if _, ok := configs.ReleaseConfigs[*aliasTarget]; !ok {
+ if _, ok2 := configs.Aliases[*aliasTarget]; !ok2 {
+ return fmt.Errorf("Alias %s points to non-existing config %s", aliasName, *aliasTarget)
+ }
+ }
+ otherNames[*aliasTarget] = append(otherNames[*aliasTarget], aliasName)
+ }
+ for name, aliases := range otherNames {
+ configs.ReleaseConfigs[name].OtherNames = aliases
+ }
+
+ for _, config := range configs.ReleaseConfigs {
+ err := config.GenerateReleaseConfig(configs)
+ if err != nil {
+ return err
+ }
+ }
+
+ releaseConfig, err := configs.GetReleaseConfig(targetRelease)
+ if err != nil {
+ return err
+ }
+ configs.Artifact = release_config_proto.ReleaseConfigsArtifact{
+ ReleaseConfig: releaseConfig.ReleaseConfigArtifact,
+ OtherReleaseConfigs: func() []*release_config_proto.ReleaseConfigArtifact {
+ orc := []*release_config_proto.ReleaseConfigArtifact{}
+ for name, config := range configs.ReleaseConfigs {
+ if name != releaseConfig.Name {
+ orc = append(orc, config.ReleaseConfigArtifact)
+ }
+ }
+ return orc
+ }(),
+ }
+ return nil
+}
+
+func ReadReleaseConfigMaps(releaseConfigMapPaths StringList, targetRelease string) (*ReleaseConfigs, error) {
+ var err error
+
+ if len(releaseConfigMapPaths) == 0 {
+ releaseConfigMapPaths = GetDefaultMapPaths()
+ if len(releaseConfigMapPaths) == 0 {
+ return nil, fmt.Errorf("No maps found")
+ }
+ fmt.Printf("No --map argument provided. Using: --map %s\n", strings.Join(releaseConfigMapPaths, " --map "))
+ }
+
+ configs := ReleaseConfigsFactory()
+ for idx, releaseConfigMapPath := range releaseConfigMapPaths {
+ // Maintain an ordered list of release config directories.
+ configDir := filepath.Dir(releaseConfigMapPath)
+ configs.ConfigDirIndexes[configDir] = idx
+ configs.ConfigDirs = append(configs.ConfigDirs, configDir)
+ err = configs.LoadReleaseConfigMap(releaseConfigMapPath, idx)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // Now that we have all of the release config maps, can meld them and generate the artifacts.
+ err = configs.GenerateReleaseConfigs(targetRelease)
+ return configs, err
+}
diff --git a/cmd/release_config/release_config_lib/util.go b/cmd/release_config/release_config_lib/util.go
new file mode 100644
index 0000000..c59deb3
--- /dev/null
+++ b/cmd/release_config/release_config_lib/util.go
@@ -0,0 +1,90 @@
+// 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 release_config_lib
+
+import (
+ "fmt"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "google.golang.org/protobuf/encoding/prototext"
+ "google.golang.org/protobuf/proto"
+)
+
+type StringList []string
+
+func (l *StringList) Set(v string) error {
+ *l = append(*l, v)
+ return nil
+}
+
+func (l *StringList) String() string {
+ return fmt.Sprintf("%v", *l)
+}
+
+func LoadTextproto(path string, message proto.Message) error {
+ data, err := os.ReadFile(path)
+ if err != nil {
+ return err
+ }
+ ret := prototext.Unmarshal(data, message)
+ return ret
+}
+
+func WalkTextprotoFiles(root string, subdir string, Func fs.WalkDirFunc) error {
+ path := filepath.Join(root, subdir)
+ if _, err := os.Stat(path); err != nil {
+ // Missing subdirs are not an error.
+ return nil
+ }
+ return filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ if strings.HasSuffix(d.Name(), ".textproto") && d.Type().IsRegular() {
+ return Func(path, d, err)
+ }
+ return nil
+ })
+}
+
+func GetDefaultOutDir() string {
+ outEnv := os.Getenv("OUT_DIR")
+ if outEnv == "" {
+ outEnv = "out"
+ }
+ return filepath.Join(outEnv, "soong", "release-config")
+}
+
+func GetDefaultMapPaths() StringList {
+ var defaultMapPaths StringList
+ defaultLocations := StringList{
+ "build/release/release_config_map.textproto",
+ "vendor/google_shared/build/release/release_config_map.textproto",
+ "vendor/google/release/release_config_map.textproto",
+ }
+ for _, path := range defaultLocations {
+ if _, err := os.Stat(path); err == nil {
+ defaultMapPaths = append(defaultMapPaths, path)
+ }
+ }
+ prodMaps := os.Getenv("PRODUCT_RELEASE_CONFIG_MAPS")
+ if prodMaps != "" {
+ defaultMapPaths = append(defaultMapPaths, strings.Split(prodMaps, " ")...)
+ }
+ return defaultMapPaths
+}
diff --git a/cmd/release_config/release_config_proto/Android.bp b/cmd/release_config/release_config_proto/Android.bp
index a8660c7..5a6aeab 100644
--- a/cmd/release_config/release_config_proto/Android.bp
+++ b/cmd/release_config/release_config_proto/Android.bp
@@ -17,7 +17,7 @@
}
bootstrap_go_package {
- name: "soong-release_config_proto",
+ name: "soong-cmd-release_config-proto",
pkgPath: "android/soong/release_config/release_config_proto",
deps: [
"golang-protobuf-reflect-protoreflect",
diff --git a/cmd/release_config/release_config_proto/build_flags_src.pb.go b/cmd/release_config/release_config_proto/build_flags_src.pb.go
index 0f2c30b..ccf3b3f 100644
--- a/cmd/release_config/release_config_proto/build_flags_src.pb.go
+++ b/cmd/release_config/release_config_proto/build_flags_src.pb.go
@@ -287,6 +287,9 @@
// The name of the flag.
// See # name for format detail
Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ // Namespace the flag belongs to (required)
+ // See # namespace for format detail
+ Namespace *string `protobuf:"bytes,2,opt,name=namespace" json:"namespace,omitempty"`
// Text description of the flag's purpose.
Description *string `protobuf:"bytes,3,opt,name=description" json:"description,omitempty"`
// Value for the flag
@@ -296,9 +299,6 @@
// The container for this flag. This overrides any default container given
// in the release_config_map message.
Container *Container `protobuf:"varint,206,opt,name=container,enum=android.release_config_proto.Container" json:"container,omitempty"`
- // Temporarily allow origin at the flag declaration level while we
- // move flags to their own locations.
- Origin *string `protobuf:"bytes,208,opt,name=origin" json:"origin,omitempty"`
}
func (x *FlagDeclaration) Reset() {
@@ -340,6 +340,13 @@
return ""
}
+func (x *FlagDeclaration) GetNamespace() string {
+ if x != nil && x.Namespace != nil {
+ return *x.Namespace
+ }
+ return ""
+}
+
func (x *FlagDeclaration) GetDescription() string {
if x != nil && x.Description != nil {
return *x.Description
@@ -368,13 +375,6 @@
return Container_UNSPECIFIED_container
}
-func (x *FlagDeclaration) GetOrigin() string {
- if x != nil && x.Origin != nil {
- return *x.Origin
- }
- return ""
-}
-
type FlagValue struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -568,8 +568,6 @@
// Any aliases.
Aliases []*ReleaseAlias `protobuf:"bytes,1,rep,name=aliases" json:"aliases,omitempty"`
- // The origin for flags declared here.
- Origin *string `protobuf:"bytes,2,opt,name=origin" json:"origin,omitempty"`
// The default container for flags declared here.
DefaultContainer *Container `protobuf:"varint,3,opt,name=default_container,json=defaultContainer,enum=android.release_config_proto.Container" json:"default_container,omitempty"`
}
@@ -613,13 +611,6 @@
return nil
}
-func (x *ReleaseConfigMap) GetOrigin() string {
- if x != nil && x.Origin != nil {
- return *x.Origin
- }
- return ""
-}
-
func (x *ReleaseConfigMap) GetDefaultContainer() Container {
if x != nil && x.DefaultContainer != nil {
return *x.DefaultContainer
@@ -643,71 +634,70 @@
0x6c, 0x75, 0x65, 0x18, 0xca, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x09, 0x62, 0x6f,
0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1d, 0x0a, 0x08, 0x6f, 0x62, 0x73, 0x6f, 0x6c,
0x65, 0x74, 0x65, 0x18, 0xcb, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x08, 0x6f, 0x62,
- 0x73, 0x6f, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x05, 0x0a, 0x03, 0x76, 0x61, 0x6c, 0x22, 0xb8, 0x02,
+ 0x73, 0x6f, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x05, 0x0a, 0x03, 0x76, 0x61, 0x6c, 0x22, 0xbd, 0x02,
0x0a, 0x10, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
- 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73,
- 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
- 0x65, 0x18, 0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
- 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
- 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76,
- 0x61, 0x6c, 0x75, 0x65, 0x12, 0x43, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
- 0x18, 0xcd, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
- 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
- 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52,
- 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x46, 0x0a, 0x09, 0x63, 0x6f, 0x6e,
- 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0xce, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e,
- 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f,
- 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e,
- 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65,
- 0x72, 0x12, 0x17, 0x0a, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x18, 0xd0, 0x01, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05,
- 0x4a, 0x06, 0x08, 0xcf, 0x01, 0x10, 0xd0, 0x01, 0x22, 0x5c, 0x0a, 0x0a, 0x66, 0x6c, 0x61, 0x67,
- 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02,
- 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x05, 0x76, 0x61,
- 0x6c, 0x75, 0x65, 0x18, 0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x6e, 0x64,
- 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e,
- 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x52,
- 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x6e, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
- 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
- 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08,
- 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08,
- 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x61, 0x63, 0x6f, 0x6e,
- 0x66, 0x69, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x73, 0x18, 0x03,
- 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x61, 0x6c,
- 0x75, 0x65, 0x53, 0x65, 0x74, 0x73, 0x22, 0x3b, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
- 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
- 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74,
- 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72,
- 0x67, 0x65, 0x74, 0x22, 0xc9, 0x01, 0x0a, 0x12, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f,
- 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6d, 0x61, 0x70, 0x12, 0x45, 0x0a, 0x07, 0x61, 0x6c,
- 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x61, 0x6e,
+ 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70,
+ 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73,
+ 0x70, 0x61, 0x63, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
+ 0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+ 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x12, 0x43, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0xcd,
+ 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
+ 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x08, 0x77,
+ 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x46, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61,
+ 0x69, 0x6e, 0x65, 0x72, 0x18, 0xce, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x61, 0x6e,
0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f,
- 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61,
- 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65,
- 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x12, 0x54, 0x0a, 0x11, 0x64, 0x65, 0x66,
- 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x03,
- 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72,
+ 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61,
+ 0x69, 0x6e, 0x65, 0x72, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4a,
+ 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x06, 0x08, 0xcf, 0x01, 0x10, 0xd0, 0x01, 0x22, 0x5c, 0x0a,
+ 0x0a, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e,
+ 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
+ 0x3a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x23, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
+ 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x6e, 0x0a, 0x0e, 0x72,
+ 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a,
+ 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
+ 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20,
+ 0x03, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x73, 0x12, 0x2c, 0x0a,
+ 0x12, 0x61, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x73,
+ 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x61, 0x63, 0x6f, 0x6e, 0x66,
+ 0x69, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x65, 0x74, 0x73, 0x22, 0x3b, 0x0a, 0x0d, 0x72,
+ 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x12, 0x0a, 0x04,
+ 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+ 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xb1, 0x01, 0x0a, 0x12, 0x72, 0x65, 0x6c,
+ 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6d, 0x61, 0x70, 0x12,
+ 0x45, 0x0a, 0x07, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
+ 0x32, 0x2b, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65, 0x61,
+ 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
+ 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x07, 0x61,
+ 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x54, 0x0a, 0x11, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c,
+ 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x0e, 0x32, 0x27, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x72, 0x65, 0x6c, 0x65,
+ 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x10, 0x64, 0x65, 0x66, 0x61,
+ 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x2a, 0x4a, 0x0a, 0x08,
+ 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x18, 0x0a, 0x14, 0x55, 0x4e, 0x53, 0x50,
+ 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
+ 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x41, 0x55, 0x4e, 0x43, 0x48, 0x10, 0x01, 0x12, 0x0c,
+ 0x0a, 0x08, 0x50, 0x52, 0x45, 0x42, 0x55, 0x49, 0x4c, 0x54, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06,
+ 0x4d, 0x41, 0x4e, 0x55, 0x41, 0x4c, 0x10, 0x03, 0x2a, 0x64, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74,
+ 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
+ 0x46, 0x49, 0x45, 0x44, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x10, 0x00,
+ 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x52, 0x4f,
+ 0x44, 0x55, 0x43, 0x54, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d,
+ 0x10, 0x03, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x45, 0x58, 0x54,
+ 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x56, 0x45, 0x4e, 0x44, 0x4f, 0x52, 0x10, 0x05, 0x42, 0x33,
+ 0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f,
+ 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72,
0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72,
- 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x10, 0x64,
- 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x2a,
- 0x4a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x18, 0x0a, 0x14, 0x55,
- 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x66,
- 0x6c, 0x6f, 0x77, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x41, 0x55, 0x4e, 0x43, 0x48, 0x10,
- 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x52, 0x45, 0x42, 0x55, 0x49, 0x4c, 0x54, 0x10, 0x02, 0x12,
- 0x0a, 0x0a, 0x06, 0x4d, 0x41, 0x4e, 0x55, 0x41, 0x4c, 0x10, 0x03, 0x2a, 0x64, 0x0a, 0x09, 0x63,
- 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x4e, 0x53, 0x50,
- 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65,
- 0x72, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07,
- 0x50, 0x52, 0x4f, 0x44, 0x55, 0x43, 0x54, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x59, 0x53,
- 0x54, 0x45, 0x4d, 0x10, 0x03, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f,
- 0x45, 0x58, 0x54, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x56, 0x45, 0x4e, 0x44, 0x4f, 0x52, 0x10,
- 0x05, 0x42, 0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f,
- 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
- 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
- 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x6f, 0x74, 0x6f,
}
var (
diff --git a/cmd/release_config/release_config_proto/build_flags_src.proto b/cmd/release_config/release_config_proto/build_flags_src.proto
index 0662716..8501524 100644
--- a/cmd/release_config/release_config_proto/build_flags_src.proto
+++ b/cmd/release_config/release_config_proto/build_flags_src.proto
@@ -26,6 +26,11 @@
// RELEASE_MY_PACKAGE_FLAG is a valid name, while MY_PACKAGE_FLAG, and
// RELEASE_MY_PACKAGE__FLAG are invalid.
//
+// # namespace: namespace the flag belongs to
+//
+// format: a lowercase string in snake_case format, no consecutive underscores, and no leading
+// digit. For example android_bar_system
+//
// # package: package to which the flag belongs
//
// format: lowercase strings in snake_case format, delimited by dots, no
@@ -77,6 +82,10 @@
// See # name for format detail
optional string name = 1;
+ // Namespace the flag belongs to (required)
+ // See # namespace for format detail
+ optional string namespace = 2;
+
// Text description of the flag's purpose.
optional string description = 3;
@@ -96,12 +105,6 @@
// The package associated with this flag.
// (when Gantry is ready for it) optional string package = 207;
reserved 207;
-
- // Temporarily allow origin at the flag declaration level while we
- // move flags to their own locations.
- optional string origin = 208;
-
- // TODO: do we want to include "first used in" (= ap2a)?
}
message flag_value {
@@ -141,9 +144,6 @@
// Any aliases.
repeated release_alias aliases = 1;
- // The origin for flags declared here.
- optional string origin = 2;
-
// The default container for flags declared here.
optional container default_container = 3;
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 57c7ae8..af1d33d 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -323,6 +323,7 @@
} else if clc.Host == hostPath && clc.Device == devicePath {
// Ok, the same library with the same paths. Don't re-add it, but don't raise an error
// either, as the same library may be reachable via different transitional dependencies.
+ clc.Optional = clc.Optional && optional
return nil
} else {
// Fail, as someone is trying to add the same library with different paths. This likely
diff --git a/java/aar.go b/java/aar.go
index a366267..f8955ce 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -356,12 +356,13 @@
forceNonFinalResourceIDs bool
extraLinkFlags []string
aconfigTextFiles android.Paths
+ usesLibrary *usesLibrary
}
func (a *aapt) buildActions(ctx android.ModuleContext, opts aaptBuildActionOptions) {
staticResourcesNodesDepSet, sharedResourcesNodesDepSet, staticRRODirsDepSet, staticManifestsDepSet, sharedExportPackages, libFlags :=
- aaptLibs(ctx, opts.sdkContext, opts.classLoaderContexts)
+ aaptLibs(ctx, opts.sdkContext, opts.classLoaderContexts, opts.usesLibrary)
// Exclude any libraries from the supplied list.
opts.classLoaderContexts = opts.classLoaderContexts.ExcludeLibs(opts.excludedLibs)
@@ -703,7 +704,8 @@
}
// aaptLibs collects libraries from dependencies and sdk_version and converts them into paths
-func aaptLibs(ctx android.ModuleContext, sdkContext android.SdkContext, classLoaderContexts dexpreopt.ClassLoaderContextMap) (
+func aaptLibs(ctx android.ModuleContext, sdkContext android.SdkContext,
+ classLoaderContexts dexpreopt.ClassLoaderContextMap, usesLibrary *usesLibrary) (
staticResourcesNodes, sharedResourcesNodes *android.DepSet[*resourcesNode], staticRRODirs *android.DepSet[rroDir],
staticManifests *android.DepSet[android.Path], sharedLibs android.Paths, flags []string) {
@@ -753,6 +755,9 @@
}
addCLCFromDep(ctx, module, classLoaderContexts)
+ if usesLibrary != nil {
+ addMissingOptionalUsesLibsFromDep(ctx, module, usesLibrary)
+ }
})
// AAPT2 overlays are in lowest to highest priority order, the topological order will be reversed later.
@@ -805,12 +810,12 @@
var _ AndroidLibraryDependency = (*AndroidLibrary)(nil)
func (a *AndroidLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
+ a.usesLibrary.deps(ctx, false)
a.Module.deps(ctx)
sdkDep := decodeSdkDep(ctx, android.SdkContext(a))
if sdkDep.hasFrameworkLibs() {
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)
@@ -829,6 +834,7 @@
classLoaderContexts: a.classLoaderContexts,
enforceDefaultTargetSdkVersion: false,
aconfigTextFiles: getAconfigFilePaths(ctx),
+ usesLibrary: &a.usesLibrary,
},
)
@@ -1215,7 +1221,7 @@
linkDeps = append(linkDeps, a.manifest)
staticResourcesNodesDepSet, sharedResourcesNodesDepSet, staticRRODirsDepSet, staticManifestsDepSet, sharedLibs, libFlags :=
- aaptLibs(ctx, android.SdkContext(a), nil)
+ aaptLibs(ctx, android.SdkContext(a), nil, nil)
_ = sharedResourcesNodesDepSet
_ = staticRRODirsDepSet
@@ -1287,6 +1293,7 @@
}
}
addCLCFromDep(ctx, module, a.classLoaderContexts)
+ addMissingOptionalUsesLibsFromDep(ctx, module, &a.usesLibrary)
})
var implementationJarFile android.OutputPath
@@ -1405,6 +1412,12 @@
var _ android.PrebuiltInterface = (*AARImport)(nil)
+func (a *AARImport) UsesLibrary() *usesLibrary {
+ return &a.usesLibrary
+}
+
+var _ ModuleWithUsesLibrary = (*AARImport)(nil)
+
// android_library_import imports an `.aar` file into the build graph as if it was built with android_library.
//
// This module is not suitable for installing on a device, but can be used as a `static_libs` dependency of
diff --git a/java/app.go b/java/app.go
index 1aa3afe..50d1a2f 100644
--- a/java/app.go
+++ b/java/app.go
@@ -249,13 +249,13 @@
}
func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) {
- a.Module.deps(ctx)
-
if String(a.appProperties.Stl) == "c++_shared" && !a.SdkVersion(ctx).Specified() {
ctx.PropertyErrorf("stl", "sdk_version must be set in order to use c++_shared")
}
sdkDep := decodeSdkDep(ctx, android.SdkContext(a))
+ a.usesLibrary.deps(ctx, sdkDep.hasFrameworkLibs())
+ a.Module.deps(ctx)
if sdkDep.hasFrameworkLibs() {
a.aapt.deps(ctx, sdkDep)
}
@@ -285,9 +285,6 @@
}
ctx.AddFarVariationDependencies(variation, jniLibTag, a.appProperties.Jni_libs...)
}
-
- a.usesLibrary.deps(ctx, sdkDep.hasFrameworkLibs())
-
for _, aconfig_declaration := range a.aaptProperties.Flags_packages {
ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration)
}
@@ -534,6 +531,7 @@
forceNonFinalResourceIDs: nonFinalIds,
extraLinkFlags: aaptLinkFlags,
aconfigTextFiles: getAconfigFilePaths(ctx),
+ usesLibrary: &a.usesLibrary,
},
)
@@ -815,18 +813,10 @@
// The decision to enforce <uses-library> checks is made before adding implicit SDK libraries.
a.usesLibrary.freezeEnforceUsesLibraries()
- // Add implicit SDK libraries to <uses-library> list.
- requiredUsesLibs, optionalUsesLibs := a.classLoaderContexts.UsesLibs()
- for _, usesLib := range requiredUsesLibs {
- a.usesLibrary.addLib(usesLib, false)
- }
- for _, usesLib := range optionalUsesLibs {
- a.usesLibrary.addLib(usesLib, true)
- }
-
// Check that the <uses-library> list is coherent with the manifest.
if a.usesLibrary.enforceUsesLibraries() {
- manifestCheckFile := a.usesLibrary.verifyUsesLibrariesManifest(ctx, a.mergedManifestFile)
+ manifestCheckFile := a.usesLibrary.verifyUsesLibrariesManifest(
+ ctx, a.mergedManifestFile, &a.classLoaderContexts)
apkDeps = append(apkDeps, manifestCheckFile)
}
@@ -1596,6 +1586,9 @@
// provide the android.test.base statically and use jarjar to rename them so they do not collide
// with the classes provided by the android.test.base library.
Exclude_uses_libs []string
+
+ // The module names of optional uses-library libraries that are missing from the source tree.
+ Missing_optional_uses_libs []string `blueprint:"mutated"`
}
// usesLibrary provides properties and helper functions for AndroidApp and AndroidAppImport to verify that the
@@ -1612,20 +1605,11 @@
shouldDisableDexpreopt bool
}
-func (u *usesLibrary) addLib(lib string, optional bool) {
- if !android.InList(lib, u.usesLibraryProperties.Uses_libs) && !android.InList(lib, u.usesLibraryProperties.Optional_uses_libs) {
- if optional {
- u.usesLibraryProperties.Optional_uses_libs = append(u.usesLibraryProperties.Optional_uses_libs, lib)
- } else {
- u.usesLibraryProperties.Uses_libs = append(u.usesLibraryProperties.Uses_libs, lib)
- }
- }
-}
-
func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, addCompatDeps bool) {
if !ctx.Config().UnbundledBuild() || ctx.Config().UnbundledBuildImage() {
ctx.AddVariationDependencies(nil, usesLibReqTag, u.usesLibraryProperties.Uses_libs...)
- ctx.AddVariationDependencies(nil, usesLibOptTag, u.presentOptionalUsesLibs(ctx)...)
+ presentOptionalUsesLibs := u.presentOptionalUsesLibs(ctx)
+ ctx.AddVariationDependencies(nil, usesLibOptTag, presentOptionalUsesLibs...)
// Only add these extra dependencies if the module is an app that depends on framework
// libs. This avoids creating a cyclic dependency:
// e.g. framework-res -> org.apache.http.legacy -> ... -> framework-res.
@@ -1636,6 +1620,8 @@
ctx.AddVariationDependencies(nil, usesLibCompat28OptTag, dexpreopt.OptionalCompatUsesLibs28...)
ctx.AddVariationDependencies(nil, usesLibCompat30OptTag, dexpreopt.OptionalCompatUsesLibs30...)
}
+ _, diff, _ := android.ListSetDifference(u.usesLibraryProperties.Optional_uses_libs, presentOptionalUsesLibs)
+ u.usesLibraryProperties.Missing_optional_uses_libs = diff
} else {
ctx.AddVariationDependencies(nil, r8LibraryJarTag, u.usesLibraryProperties.Uses_libs...)
ctx.AddVariationDependencies(nil, r8LibraryJarTag, u.presentOptionalUsesLibs(ctx)...)
@@ -1654,15 +1640,6 @@
return optionalUsesLibs
}
-// Helper function to replace string in a list.
-func replaceInList(list []string, oldstr, newstr string) {
- for i, str := range list {
- if str == oldstr {
- list[i] = newstr
- }
- }
-}
-
// Returns a map of module names of shared library dependencies to the paths to their dex jars on
// host and on device.
func (u *usesLibrary) classLoaderContextForUsesLibDeps(ctx android.ModuleContext) dexpreopt.ClassLoaderContextMap {
@@ -1704,11 +1681,6 @@
libName := dep
if ulib, ok := m.(ProvidesUsesLib); ok && ulib.ProvidesUsesLib() != nil {
libName = *ulib.ProvidesUsesLib()
- // Replace module name with library name in `uses_libs`/`optional_uses_libs` in
- // order to pass verify_uses_libraries check (which compares these properties
- // against library names written in the manifest).
- replaceInList(u.usesLibraryProperties.Uses_libs, dep, libName)
- replaceInList(u.usesLibraryProperties.Optional_uses_libs, dep, libName)
}
clcMap.AddContext(ctx, tag.sdkVersion, libName, tag.optional,
lib.DexJarBuildPath(ctx).PathOrNil(), lib.DexJarInstallPath(),
@@ -1742,7 +1714,7 @@
// an APK with the manifest embedded in it (manifest_check will know which one it is by the file
// extension: APKs are supposed to end with '.apk').
func (u *usesLibrary) verifyUsesLibraries(ctx android.ModuleContext, inputFile android.Path,
- outputFile android.WritablePath) android.Path {
+ outputFile android.WritablePath, classLoaderContexts *dexpreopt.ClassLoaderContextMap) android.Path {
statusFile := dexpreopt.UsesLibrariesStatusFile(ctx)
@@ -1770,27 +1742,37 @@
cmd.Flag("--enforce-uses-libraries-relax")
}
- for _, lib := range u.usesLibraryProperties.Uses_libs {
+ requiredUsesLibs, optionalUsesLibs := classLoaderContexts.UsesLibs()
+ for _, lib := range requiredUsesLibs {
cmd.FlagWithArg("--uses-library ", lib)
}
-
- for _, lib := range u.usesLibraryProperties.Optional_uses_libs {
+ for _, lib := range optionalUsesLibs {
cmd.FlagWithArg("--optional-uses-library ", lib)
}
+ // Also add missing optional uses libs, as the manifest check expects them.
+ // Note that what we add here are the module names of those missing libs, not library names, while
+ // the manifest check actually expects library names. However, the case where a library is missing
+ // and the module name != the library name is too rare for us to handle.
+ for _, lib := range u.usesLibraryProperties.Missing_optional_uses_libs {
+ cmd.FlagWithArg("--missing-optional-uses-library ", lib)
+ }
+
rule.Build("verify_uses_libraries", "verify <uses-library>")
return outputFile
}
// verifyUsesLibrariesManifest checks the <uses-library> tags in an AndroidManifest.xml against
// the build system and returns the path to a copy of the manifest.
-func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path) android.Path {
+func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path,
+ classLoaderContexts *dexpreopt.ClassLoaderContextMap) android.Path {
outputFile := android.PathForModuleOut(ctx, "manifest_check", "AndroidManifest.xml")
- return u.verifyUsesLibraries(ctx, manifest, outputFile)
+ return u.verifyUsesLibraries(ctx, manifest, outputFile, classLoaderContexts)
}
// verifyUsesLibrariesAPK checks the <uses-library> tags in the manifest of an APK against the build
// system and returns the path to a copy of the APK.
-func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk android.Path) {
- u.verifyUsesLibraries(ctx, apk, nil) // for APKs manifest_check does not write output file
+func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk android.Path,
+ classLoaderContexts *dexpreopt.ClassLoaderContextMap) {
+ u.verifyUsesLibraries(ctx, apk, nil, classLoaderContexts) // for APKs manifest_check does not write output file
}
diff --git a/java/app_import.go b/java/app_import.go
index 7387e16..bb07c42 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -355,7 +355,7 @@
}
if a.usesLibrary.enforceUsesLibraries() {
- a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk)
+ a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk, &a.dexpreopter.classLoaderContexts)
}
a.dexpreopter.dexpreopt(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), jnisUncompressed)
@@ -611,6 +611,12 @@
return return_struct
}
+func (a *AndroidAppImport) UsesLibrary() *usesLibrary {
+ return &a.usesLibrary
+}
+
+var _ ModuleWithUsesLibrary = (*AndroidAppImport)(nil)
+
// android_app_import imports a prebuilt apk with additional processing specified in the module.
// DPI-specific apk source files can be specified using dpi_variants. Example:
//
diff --git a/java/app_test.go b/java/app_test.go
index 0c28000..eab40e7 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -3244,7 +3244,10 @@
name: "static-y",
srcs: ["a.java"],
uses_libs: ["runtime-required-y"],
- optional_uses_libs: ["runtime-optional-y"],
+ optional_uses_libs: [
+ "runtime-optional-y",
+ "missing-lib-a",
+ ],
sdk_version: "current",
}
@@ -3280,7 +3283,7 @@
sdk_version: "current",
optional_uses_libs: [
"bar",
- "baz",
+ "missing-lib-b",
],
}
@@ -3295,7 +3298,7 @@
],
optional_uses_libs: [
"bar",
- "baz",
+ "missing-lib-b",
],
}
`
@@ -3317,10 +3320,10 @@
// propagated from dependencies.
actualManifestFixerArgs := app.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
expectManifestFixerArgs := `--extract-native-libs=true ` +
- `--uses-library qux ` +
- `--uses-library quuz ` +
`--uses-library foo ` +
`--uses-library com.non.sdk.lib ` +
+ `--uses-library qux ` +
+ `--uses-library quuz ` +
`--uses-library runtime-library ` +
`--uses-library runtime-required-x ` +
`--uses-library runtime-required-y ` +
@@ -3339,9 +3342,10 @@
`--uses-library runtime-required-x ` +
`--uses-library runtime-required-y ` +
`--optional-uses-library bar ` +
- `--optional-uses-library baz ` +
`--optional-uses-library runtime-optional-x ` +
- `--optional-uses-library runtime-optional-y `
+ `--optional-uses-library runtime-optional-y ` +
+ `--missing-optional-uses-library missing-lib-b ` +
+ `--missing-optional-uses-library missing-lib-a`
android.AssertStringDoesContain(t, "verify cmd args", verifyCmd, verifyArgs)
// Test that all libraries are verified for an APK (library order matters).
@@ -3350,7 +3354,7 @@
`--uses-library com.non.sdk.lib ` +
`--uses-library android.test.runner ` +
`--optional-uses-library bar ` +
- `--optional-uses-library baz `
+ `--missing-optional-uses-library missing-lib-b `
android.AssertStringDoesContain(t, "verify apk cmd args", verifyApkCmd, verifyApkArgs)
// Test that necessary args are passed for constructing CLC in Ninja phase.
diff --git a/java/base.go b/java/base.go
index ef61f1c..938ac5e 100644
--- a/java/base.go
+++ b/java/base.go
@@ -843,9 +843,11 @@
if dep != nil {
if component, ok := dep.(SdkLibraryComponentDependency); ok {
if lib := component.OptionalSdkLibraryImplementation(); lib != nil {
- // Add library as optional if it's one of the optional compatibility libs.
+ // Add library as optional if it's one of the optional compatibility libs or it's
+ // explicitly listed in the optional_uses_libs property.
tag := usesLibReqTag
- if android.InList(*lib, dexpreopt.OptionalCompatUsesLibs) {
+ if android.InList(*lib, dexpreopt.OptionalCompatUsesLibs) ||
+ android.InList(*lib, j.usesLibrary.usesLibraryProperties.Optional_uses_libs) {
tag = usesLibOptTag
}
ctx.AddVariationDependencies(nil, tag, *lib)
@@ -2387,6 +2389,7 @@
}
addCLCFromDep(ctx, module, j.classLoaderContexts)
+ addMissingOptionalUsesLibsFromDep(ctx, module, &j.usesLibrary)
})
return deps
@@ -2720,3 +2723,13 @@
}
var _ ModuleWithStem = (*Module)(nil)
+
+type ModuleWithUsesLibrary interface {
+ UsesLibrary() *usesLibrary
+}
+
+func (j *Module) UsesLibrary() *usesLibrary {
+ return &j.usesLibrary
+}
+
+var _ ModuleWithUsesLibrary = (*Module)(nil)
diff --git a/java/java.go b/java/java.go
index ca99e69..fb35a9a 100644
--- a/java/java.go
+++ b/java/java.go
@@ -966,8 +966,8 @@
}
func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) {
- j.deps(ctx)
j.usesLibrary.deps(ctx, false)
+ j.deps(ctx)
}
const (
@@ -3162,13 +3162,35 @@
// <uses_library> and should not be added to CLC, but the transitive <uses-library> dependencies
// from its CLC should be added to the current CLC.
if sdkLib != nil {
- clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, false,
+ optional := false
+ if module, ok := ctx.Module().(ModuleWithUsesLibrary); ok {
+ if android.InList(*sdkLib, module.UsesLibrary().usesLibraryProperties.Optional_uses_libs) {
+ optional = true
+ }
+ }
+ clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, optional,
dep.DexJarBuildPath(ctx).PathOrNil(), dep.DexJarInstallPath(), dep.ClassLoaderContexts())
} else {
clcMap.AddContextMap(dep.ClassLoaderContexts(), depName)
}
}
+func addMissingOptionalUsesLibsFromDep(ctx android.ModuleContext, depModule android.Module,
+ usesLibrary *usesLibrary) {
+
+ dep, ok := depModule.(ModuleWithUsesLibrary)
+ if !ok {
+ return
+ }
+
+ for _, lib := range dep.UsesLibrary().usesLibraryProperties.Missing_optional_uses_libs {
+ if !android.InList(lib, usesLibrary.usesLibraryProperties.Missing_optional_uses_libs) {
+ usesLibrary.usesLibraryProperties.Missing_optional_uses_libs =
+ append(usesLibrary.usesLibraryProperties.Missing_optional_uses_libs, lib)
+ }
+ }
+}
+
type JavaApiContributionImport struct {
JavaApiContribution
diff --git a/partner/androidmk/androidmk_test.go b/partner/androidmk/androidmk_test.go
index 6bae836..3ace750 100644
--- a/partner/androidmk/androidmk_test.go
+++ b/partner/androidmk/androidmk_test.go
@@ -54,6 +54,9 @@
}
func TestEndToEnd(t *testing.T) {
+ // Skip checking Android.mk path with cleaning "ANDROID_BUILD_TOP"
+ t.Setenv("ANDROID_BUILD_TOP", "")
+
for i, test := range testCases {
expected, err := bpfix.Reformat(test.expected)
if err != nil {
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py
index c33b104..b101259 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -52,6 +52,14 @@
'required:false'
)
parser.add_argument(
+ '--missing-optional-uses-library',
+ dest='missing_optional_uses_libraries',
+ action='append',
+ help='specify uses-library entries missing from the build system with '
+ 'required:false',
+ default=[]
+ )
+ parser.add_argument(
'--enforce-uses-libraries',
dest='enforce_uses_libraries',
action='store_true',
@@ -91,7 +99,7 @@
C_BOLD = "\033[1m"
-def enforce_uses_libraries(manifest, required, optional, relax, is_apk, path):
+def enforce_uses_libraries(manifest, required, optional, missing_optional, relax, is_apk, path):
"""Verify that the <uses-library> tags in the manifest match those provided
by the build system.
@@ -119,7 +127,12 @@
required = trim_namespace_parts(required)
optional = trim_namespace_parts(optional)
- if manifest_required == required and manifest_optional == optional:
+ existing_manifest_optional = [
+ lib for lib in manifest_optional if lib not in missing_optional]
+
+ # The order of the existing libraries matter, while the order of the missing
+ # ones doesn't.
+ if manifest_required == required and existing_manifest_optional == optional:
return None
#pylint: disable=line-too-long
@@ -129,6 +142,7 @@
'\t- required libraries in build system: %s[%s]%s\n' % (C_RED, ', '.join(required), C_OFF),
'\t vs. in the manifest: %s[%s]%s\n' % (C_RED, ', '.join(manifest_required), C_OFF),
'\t- optional libraries in build system: %s[%s]%s\n' % (C_RED, ', '.join(optional), C_OFF),
+ '\t and missing ones in build system: %s[%s]%s\n' % (C_RED, ', '.join(missing_optional), C_OFF),
'\t vs. in the manifest: %s[%s]%s\n' % (C_RED, ', '.join(manifest_optional), C_OFF),
'\t- tags in the manifest (%s):\n' % path,
'\t\t%s\n' % '\t\t'.join(tags),
@@ -340,11 +354,14 @@
if args.enforce_uses_libraries:
# Load dexpreopt.config files and build a mapping from module
- # names to library names. This is necessary because build system
- # addresses libraries by their module name (`uses_libs`,
- # `optional_uses_libs`, `LOCAL_USES_LIBRARIES`,
- # `LOCAL_OPTIONAL_LIBRARY_NAMES` all contain module names), while
- # the manifest addresses libraries by their name.
+ # names to library names. This is for Make only and it's necessary
+ # because Make passes module names from `LOCAL_USES_LIBRARIES`,
+ # `LOCAL_OPTIONAL_LIBRARY_NAMES`, while the manifest addresses
+ # libraries by their name. Soong doesn't use it and doesn't need it
+ # because it converts the module names to the library names and
+ # passes the library names. There is no need to translate missing
+ # optional libs because they are missing and therefore there is no
+ # mapping for them.
mod_to_lib = load_dexpreopt_configs(args.dexpreopt_configs)
required = translate_libnames(args.uses_libraries, mod_to_lib)
optional = translate_libnames(args.optional_uses_libraries,
@@ -354,8 +371,8 @@
# those in the manifest. Raise an exception on mismatch, unless the
# script was passed a special parameter to suppress exceptions.
errmsg = enforce_uses_libraries(manifest, required, optional,
- args.enforce_uses_libraries_relax,
- is_apk, args.input)
+ args.missing_optional_uses_libraries,
+ args.enforce_uses_libraries_relax, is_apk, args.input)
# Create a status file that is empty on success, or contains an
# error message on failure. When exceptions are suppressed,
diff --git a/scripts/manifest_check_test.py b/scripts/manifest_check_test.py
index 3be7a30..8003b3e 100755
--- a/scripts/manifest_check_test.py
+++ b/scripts/manifest_check_test.py
@@ -44,15 +44,17 @@
class EnforceUsesLibrariesTest(unittest.TestCase):
"""Unit tests for add_extract_native_libs function."""
- def run_test(self, xml, apk, uses_libraries=[], optional_uses_libraries=[]): #pylint: disable=dangerous-default-value
+ def run_test(self, xml, apk, uses_libraries=[], optional_uses_libraries=[],
+ missing_optional_uses_libraries=[]): #pylint: disable=dangerous-default-value
doc = minidom.parseString(xml)
try:
relax = False
manifest_check.enforce_uses_libraries(
- doc, uses_libraries, optional_uses_libraries, relax, False,
- 'path/to/X/AndroidManifest.xml')
+ doc, uses_libraries, optional_uses_libraries, missing_optional_uses_libraries,
+ relax, False, 'path/to/X/AndroidManifest.xml')
manifest_check.enforce_uses_libraries(apk, uses_libraries,
optional_uses_libraries,
+ missing_optional_uses_libraries,
relax, True,
'path/to/X/X.apk')
return True
@@ -102,6 +104,15 @@
matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
self.assertFalse(matches)
+ def test_expected_missing_optional_uses_library(self):
+ xml = self.xml_tmpl % (
+ uses_library_xml('foo') + uses_library_xml('missing') + uses_library_xml('bar'))
+ apk = self.apk_tmpl % (
+ uses_library_apk('foo') + uses_library_apk('missing') + uses_library_apk('bar'))
+ matches = self.run_test(xml, apk, optional_uses_libraries=['foo', 'bar'],
+ missing_optional_uses_libraries=['missing'])
+ self.assertFalse(matches)
+
def test_missing_uses_library(self):
xml = self.xml_tmpl % ('')
apk = self.apk_tmpl % ('')