Add package for printing starlark formatted data
Bug: 216168792
Test: build/bazel/ci/bp2build.sh
Change-Id: I3a06b19396f7ffe1c638042cda7e731dd840f1d6
diff --git a/android/Android.bp b/android/Android.bp
index da36959..d3540b2 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -16,7 +16,9 @@
"soong-remoteexec",
"soong-response",
"soong-shared",
+ "soong-starlark-format",
"soong-ui-metrics_proto",
+
"golang-protobuf-proto",
"golang-protobuf-encoding-prototext",
diff --git a/android/config.go b/android/config.go
index 10e074c..f10732b 100644
--- a/android/config.go
+++ b/android/config.go
@@ -38,6 +38,7 @@
"android/soong/android/soongconfig"
"android/soong/bazel"
"android/soong/remoteexec"
+ "android/soong/starlark_fmt"
)
// Bool re-exports proptools.Bool for the android package.
@@ -286,14 +287,12 @@
}
}
- //TODO(b/216168792) should use common function to print Starlark code
- nonArchVariantProductVariablesJson, err := json.MarshalIndent(&nonArchVariantProductVariables, "", " ")
+ nonArchVariantProductVariablesJson := starlark_fmt.PrintStringList(nonArchVariantProductVariables, 0)
if err != nil {
return fmt.Errorf("cannot marshal product variable data: %s", err.Error())
}
- //TODO(b/216168792) should use common function to print Starlark code
- archVariantProductVariablesJson, err := json.MarshalIndent(&archVariantProductVariables, "", " ")
+ archVariantProductVariablesJson := starlark_fmt.PrintStringList(archVariantProductVariables, 0)
if err != nil {
return fmt.Errorf("cannot marshal arch variant product variable data: %s", err.Error())
}
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index acb9d18..ceb8e45 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -386,6 +386,46 @@
})).RunTest(t)
}
+func TestDuplicateStringValueInSoongConfigStringVariable(t *testing.T) {
+ bp := `
+ soong_config_string_variable {
+ name: "board",
+ values: ["soc_a", "soc_b", "soc_c", "soc_a"],
+ }
+
+ soong_config_module_type {
+ name: "acme_test",
+ module_type: "test",
+ config_namespace: "acme",
+ variables: ["board"],
+ properties: ["cflags", "srcs", "defaults"],
+ }
+ `
+
+ fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
+ return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+ variables.VendorVars = vars
+ })
+ }
+
+ GroupFixturePreparers(
+ fixtureForVendorVars(map[string]map[string]string{"acme": {"feature1": "1"}}),
+ PrepareForTestWithDefaults,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("soong_config_module_type_import", SoongConfigModuleTypeImportFactory)
+ ctx.RegisterModuleType("soong_config_module_type", SoongConfigModuleTypeFactory)
+ ctx.RegisterModuleType("soong_config_string_variable", SoongConfigStringVariableDummyFactory)
+ ctx.RegisterModuleType("soong_config_bool_variable", SoongConfigBoolVariableDummyFactory)
+ ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
+ ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
+ }),
+ FixtureWithRootAndroidBp(bp),
+ ).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
+ // TODO(b/171232169): improve the error message for non-existent properties
+ `Android.bp: soong_config_string_variable: values property error: duplicate value: "soc_a"`,
+ })).RunTest(t)
+}
+
func testConfigWithVendorVars(buildDir, bp string, fs map[string][]byte, vendorVars map[string]map[string]string) Config {
config := TestConfig(buildDir, nil, bp, fs)
diff --git a/android/soongconfig/Android.bp b/android/soongconfig/Android.bp
index 9bf3344..8fe1ff1 100644
--- a/android/soongconfig/Android.bp
+++ b/android/soongconfig/Android.bp
@@ -10,6 +10,7 @@
"blueprint-parser",
"blueprint-proptools",
"soong-bazel",
+ "soong-starlark-format",
],
srcs: [
"config.go",
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 09a5057..212b752 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -25,6 +25,8 @@
"github.com/google/blueprint"
"github.com/google/blueprint/parser"
"github.com/google/blueprint/proptools"
+
+ "android/soong/starlark_fmt"
)
const conditionsDefault = "conditions_default"
@@ -177,10 +179,14 @@
return []error{fmt.Errorf("values property must be set")}
}
+ vals := make(map[string]bool, len(stringProps.Values))
for _, name := range stringProps.Values {
if err := checkVariableName(name); err != nil {
return []error{fmt.Errorf("soong_config_string_variable: values property error %s", err)}
+ } else if _, ok := vals[name]; ok {
+ return []error{fmt.Errorf("soong_config_string_variable: values property error: duplicate value: %q", name)}
}
+ vals[name] = true
}
v.variables[base.variable] = &stringVariable{
@@ -235,7 +241,12 @@
// string vars, bool vars and value vars created by every
// soong_config_module_type in this build.
type Bp2BuildSoongConfigDefinitions struct {
- StringVars map[string]map[string]bool
+ // varCache contains a cache of string variables namespace + property
+ // The same variable may be used in multiple module types (for example, if need support
+ // for cc_default and java_default), only need to process once
+ varCache map[string]bool
+
+ StringVars map[string][]string
BoolVars map[string]bool
ValueVars map[string]bool
}
@@ -253,7 +264,7 @@
defer bp2buildSoongConfigVarsLock.Unlock()
if defs.StringVars == nil {
- defs.StringVars = make(map[string]map[string]bool)
+ defs.StringVars = make(map[string][]string)
}
if defs.BoolVars == nil {
defs.BoolVars = make(map[string]bool)
@@ -261,15 +272,24 @@
if defs.ValueVars == nil {
defs.ValueVars = make(map[string]bool)
}
+ if defs.varCache == nil {
+ defs.varCache = make(map[string]bool)
+ }
for _, moduleType := range mtDef.ModuleTypes {
for _, v := range moduleType.Variables {
key := strings.Join([]string{moduleType.ConfigNamespace, v.variableProperty()}, "__")
+
+ // The same variable may be used in multiple module types (for example, if need support
+ // for cc_default and java_default), only need to process once
+ if _, keyInCache := defs.varCache[key]; keyInCache {
+ continue
+ } else {
+ defs.varCache[key] = true
+ }
+
if strVar, ok := v.(*stringVariable); ok {
- if _, ok := defs.StringVars[key]; !ok {
- defs.StringVars[key] = make(map[string]bool, 0)
- }
for _, value := range strVar.values {
- defs.StringVars[key][value] = true
+ defs.StringVars[key] = append(defs.StringVars[key], value)
}
} else if _, ok := v.(*boolVariable); ok {
defs.BoolVars[key] = true
@@ -302,29 +322,16 @@
// String emits the Soong config variable definitions as Starlark dictionaries.
func (defs Bp2BuildSoongConfigDefinitions) String() string {
ret := ""
- ret += "soong_config_bool_variables = {\n"
- for _, boolVar := range sortedStringKeys(defs.BoolVars) {
- ret += fmt.Sprintf(" \"%s\": True,\n", boolVar)
- }
- ret += "}\n"
- ret += "\n"
+ ret += "soong_config_bool_variables = "
+ ret += starlark_fmt.PrintBoolDict(defs.BoolVars, 0)
+ ret += "\n\n"
- ret += "soong_config_value_variables = {\n"
- for _, valueVar := range sortedStringKeys(defs.ValueVars) {
- ret += fmt.Sprintf(" \"%s\": True,\n", valueVar)
- }
- ret += "}\n"
- ret += "\n"
+ ret += "soong_config_value_variables = "
+ ret += starlark_fmt.PrintBoolDict(defs.ValueVars, 0)
+ ret += "\n\n"
- ret += "soong_config_string_variables = {\n"
- for _, stringVar := range sortedStringKeys(defs.StringVars) {
- ret += fmt.Sprintf(" \"%s\": [\n", stringVar)
- for _, choice := range sortedStringKeys(defs.StringVars[stringVar]) {
- ret += fmt.Sprintf(" \"%s\",\n", choice)
- }
- ret += fmt.Sprintf(" ],\n")
- }
- ret += "}"
+ ret += "soong_config_string_variables = "
+ ret += starlark_fmt.PrintStringListDict(defs.StringVars, 0)
return ret
}
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index b14f8b4..a7800e8 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -367,19 +367,19 @@
func Test_Bp2BuildSoongConfigDefinitions(t *testing.T) {
testCases := []struct {
+ desc string
defs Bp2BuildSoongConfigDefinitions
expected string
}{
{
+ desc: "all empty",
defs: Bp2BuildSoongConfigDefinitions{},
- expected: `soong_config_bool_variables = {
-}
+ expected: `soong_config_bool_variables = {}
-soong_config_value_variables = {
-}
+soong_config_value_variables = {}
-soong_config_string_variables = {
-}`}, {
+soong_config_string_variables = {}`}, {
+ desc: "only bool",
defs: Bp2BuildSoongConfigDefinitions{
BoolVars: map[string]bool{
"bool_var": true,
@@ -389,39 +389,35 @@
"bool_var": True,
}
-soong_config_value_variables = {
-}
+soong_config_value_variables = {}
-soong_config_string_variables = {
-}`}, {
+soong_config_string_variables = {}`}, {
+ desc: "only value vars",
defs: Bp2BuildSoongConfigDefinitions{
ValueVars: map[string]bool{
"value_var": true,
},
},
- expected: `soong_config_bool_variables = {
-}
+ expected: `soong_config_bool_variables = {}
soong_config_value_variables = {
"value_var": True,
}
-soong_config_string_variables = {
-}`}, {
+soong_config_string_variables = {}`}, {
+ desc: "only string vars",
defs: Bp2BuildSoongConfigDefinitions{
- StringVars: map[string]map[string]bool{
- "string_var": map[string]bool{
- "choice1": true,
- "choice2": true,
- "choice3": true,
+ StringVars: map[string][]string{
+ "string_var": []string{
+ "choice1",
+ "choice2",
+ "choice3",
},
},
},
- expected: `soong_config_bool_variables = {
-}
+ expected: `soong_config_bool_variables = {}
-soong_config_value_variables = {
-}
+soong_config_value_variables = {}
soong_config_string_variables = {
"string_var": [
@@ -430,6 +426,7 @@
"choice3",
],
}`}, {
+ desc: "all vars",
defs: Bp2BuildSoongConfigDefinitions{
BoolVars: map[string]bool{
"bool_var_one": true,
@@ -438,15 +435,15 @@
"value_var_one": true,
"value_var_two": true,
},
- StringVars: map[string]map[string]bool{
- "string_var_one": map[string]bool{
- "choice1": true,
- "choice2": true,
- "choice3": true,
+ StringVars: map[string][]string{
+ "string_var_one": []string{
+ "choice1",
+ "choice2",
+ "choice3",
},
- "string_var_two": map[string]bool{
- "foo": true,
- "bar": true,
+ "string_var_two": []string{
+ "foo",
+ "bar",
},
},
},
@@ -466,15 +463,17 @@
"choice3",
],
"string_var_two": [
- "bar",
"foo",
+ "bar",
],
}`},
}
for _, test := range testCases {
- actual := test.defs.String()
- if actual != test.expected {
- t.Errorf("Expected:\n%s\nbut got:\n%s", test.expected, actual)
- }
+ t.Run(test.desc, func(t *testing.T) {
+ actual := test.defs.String()
+ if actual != test.expected {
+ t.Errorf("Expected:\n%s\nbut got:\n%s", test.expected, actual)
+ }
+ })
}
}