Add package for printing starlark formatted data
Bug: 216168792 Test: build/bazel/ci/bp2build.sh Change-Id: I3a06b19396f7ffe1c638042cda7e731dd840f1d6
This commit is contained in:
parent
db07f002b8
commit
72beb34609
15 changed files with 435 additions and 166 deletions
|
@ -16,7 +16,9 @@ bootstrap_go_package {
|
|||
"soong-remoteexec",
|
||||
"soong-response",
|
||||
"soong-shared",
|
||||
"soong-starlark-format",
|
||||
"soong-ui-metrics_proto",
|
||||
|
||||
"golang-protobuf-proto",
|
||||
"golang-protobuf-encoding-prototext",
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ import (
|
|||
"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 @@ func saveToBazelConfigFile(config *productVariables, outDir string) error {
|
|||
}
|
||||
}
|
||||
|
||||
//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())
|
||||
}
|
||||
|
|
|
@ -386,6 +386,46 @@ func TestNonExistentPropertyInSoongConfigModule(t *testing.T) {
|
|||
})).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)
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ bootstrap_go_package {
|
|||
"blueprint-parser",
|
||||
"blueprint-proptools",
|
||||
"soong-bazel",
|
||||
"soong-starlark-format",
|
||||
],
|
||||
srcs: [
|
||||
"config.go",
|
||||
|
|
|
@ -25,6 +25,8 @@ import (
|
|||
"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 @@ func processStringVariableDef(v *SoongConfigDefinition, def *parser.Module) (err
|
|||
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 @@ type SoongConfigDefinition struct {
|
|||
// 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 @@ func (defs *Bp2BuildSoongConfigDefinitions) AddVars(mtDef SoongConfigDefinition)
|
|||
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 @@ func (defs *Bp2BuildSoongConfigDefinitions) AddVars(mtDef SoongConfigDefinition)
|
|||
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 @@ func sortedStringKeys(m interface{}) []string {
|
|||
// 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
|
||||
}
|
||||
|
|
|
@ -367,19 +367,19 @@ func Test_PropertiesToApply(t *testing.T) {
|
|||
|
||||
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 @@ soong_config_string_variables = {
|
|||
"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 @@ soong_config_string_variables = {
|
|||
"choice3",
|
||||
],
|
||||
}`}, {
|
||||
desc: "all vars",
|
||||
defs: Bp2BuildSoongConfigDefinitions{
|
||||
BoolVars: map[string]bool{
|
||||
"bool_var_one": true,
|
||||
|
@ -438,15 +435,15 @@ soong_config_string_variables = {
|
|||
"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 @@ soong_config_string_variables = {
|
|||
"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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ bootstrap_go_package {
|
|||
"soong-genrule",
|
||||
"soong-python",
|
||||
"soong-sh",
|
||||
"soong-starlark-format",
|
||||
"soong-ui-metrics",
|
||||
],
|
||||
testSrcs: [
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
|
||||
"android/soong/android"
|
||||
"android/soong/bazel"
|
||||
"android/soong/starlark_fmt"
|
||||
|
||||
"github.com/google/blueprint"
|
||||
"github.com/google/blueprint/proptools"
|
||||
|
@ -559,48 +560,27 @@ func prettyPrint(propertyValue reflect.Value, indent int, emitZeroValues bool) (
|
|||
return "", nil
|
||||
}
|
||||
|
||||
var ret string
|
||||
switch propertyValue.Kind() {
|
||||
case reflect.String:
|
||||
ret = fmt.Sprintf("\"%v\"", escapeString(propertyValue.String()))
|
||||
return fmt.Sprintf("\"%v\"", escapeString(propertyValue.String())), nil
|
||||
case reflect.Bool:
|
||||
ret = strings.Title(fmt.Sprintf("%v", propertyValue.Interface()))
|
||||
return starlark_fmt.PrintBool(propertyValue.Bool()), nil
|
||||
case reflect.Int, reflect.Uint, reflect.Int64:
|
||||
ret = fmt.Sprintf("%v", propertyValue.Interface())
|
||||
return fmt.Sprintf("%v", propertyValue.Interface()), nil
|
||||
case reflect.Ptr:
|
||||
return prettyPrint(propertyValue.Elem(), indent, emitZeroValues)
|
||||
case reflect.Slice:
|
||||
if propertyValue.Len() == 0 {
|
||||
return "[]", nil
|
||||
}
|
||||
|
||||
if propertyValue.Len() == 1 {
|
||||
// Single-line list for list with only 1 element
|
||||
ret += "["
|
||||
indexedValue, err := prettyPrint(propertyValue.Index(0), indent, emitZeroValues)
|
||||
elements := make([]string, 0, propertyValue.Len())
|
||||
for i := 0; i < propertyValue.Len(); i++ {
|
||||
val, err := prettyPrint(propertyValue.Index(i), indent, emitZeroValues)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ret += indexedValue
|
||||
ret += "]"
|
||||
} else {
|
||||
// otherwise, use a multiline list.
|
||||
ret += "[\n"
|
||||
for i := 0; i < propertyValue.Len(); i++ {
|
||||
indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1, emitZeroValues)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if indexedValue != "" {
|
||||
ret += makeIndent(indent + 1)
|
||||
ret += indexedValue
|
||||
ret += ",\n"
|
||||
}
|
||||
if val != "" {
|
||||
elements = append(elements, val)
|
||||
}
|
||||
ret += makeIndent(indent)
|
||||
ret += "]"
|
||||
}
|
||||
return starlark_fmt.PrintList(elements, indent, "%s"), nil
|
||||
|
||||
case reflect.Struct:
|
||||
// Special cases where the bp2build sends additional information to the codegenerator
|
||||
|
@ -611,18 +591,12 @@ func prettyPrint(propertyValue reflect.Value, indent int, emitZeroValues bool) (
|
|||
return fmt.Sprintf("%q", label.Label), nil
|
||||
}
|
||||
|
||||
ret = "{\n"
|
||||
// Sort and print the struct props by the key.
|
||||
structProps := extractStructProperties(propertyValue, indent)
|
||||
if len(structProps) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
for _, k := range android.SortedStringKeys(structProps) {
|
||||
ret += makeIndent(indent + 1)
|
||||
ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
|
||||
}
|
||||
ret += makeIndent(indent)
|
||||
ret += "}"
|
||||
return starlark_fmt.PrintDict(structProps, indent), nil
|
||||
case reflect.Interface:
|
||||
// TODO(b/164227191): implement pretty print for interfaces.
|
||||
// Interfaces are used for for arch, multilib and target properties.
|
||||
|
@ -631,7 +605,6 @@ func prettyPrint(propertyValue reflect.Value, indent int, emitZeroValues bool) (
|
|||
return "", fmt.Errorf(
|
||||
"unexpected kind for property struct field: %s", propertyValue.Kind())
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Converts a reflected property struct value into a map of property names and property values,
|
||||
|
@ -736,13 +709,6 @@ func escapeString(s string) string {
|
|||
return strings.ReplaceAll(s, "\"", "\\\"")
|
||||
}
|
||||
|
||||
func makeIndent(indent int) string {
|
||||
if indent < 0 {
|
||||
panic(fmt.Errorf("indent column cannot be less than 0, but got %d", indent))
|
||||
}
|
||||
return strings.Repeat(" ", indent)
|
||||
}
|
||||
|
||||
func targetNameWithVariant(c bpToBuildContext, logicModule blueprint.Module) string {
|
||||
name := ""
|
||||
if c.ModuleSubDir(logicModule) != "" {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"android/soong/android"
|
||||
"android/soong/bazel"
|
||||
"android/soong/starlark_fmt"
|
||||
)
|
||||
|
||||
// Configurability support for bp2build.
|
||||
|
@ -250,10 +251,10 @@ func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue *stri
|
|||
} else if defaultValue != nil {
|
||||
// Print an explicit empty list (the default value) even if the value is
|
||||
// empty, to avoid errors about not finding a configuration that matches.
|
||||
ret += fmt.Sprintf("%s\"%s\": %s,\n", makeIndent(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue)
|
||||
ret += fmt.Sprintf("%s\"%s\": %s,\n", starlark_fmt.Indention(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue)
|
||||
}
|
||||
|
||||
ret += makeIndent(indent)
|
||||
ret += starlark_fmt.Indention(indent)
|
||||
ret += "})"
|
||||
|
||||
return ret, nil
|
||||
|
@ -262,7 +263,7 @@ func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue *stri
|
|||
// prettyPrintSelectEntry converts a reflect.Value into an entry in a select map
|
||||
// with a provided key.
|
||||
func prettyPrintSelectEntry(value reflect.Value, key string, indent int, emitZeroValues bool) (string, error) {
|
||||
s := makeIndent(indent + 1)
|
||||
s := starlark_fmt.Indention(indent + 1)
|
||||
v, err := prettyPrint(value, indent+1, emitZeroValues)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
|
@ -8,6 +8,7 @@ bootstrap_go_package {
|
|||
deps: [
|
||||
"soong-android",
|
||||
"soong-remoteexec",
|
||||
"soong-starlark-format",
|
||||
],
|
||||
srcs: [
|
||||
"bp2build.go",
|
||||
|
|
|
@ -22,14 +22,11 @@ import (
|
|||
"strings"
|
||||
|
||||
"android/soong/android"
|
||||
"android/soong/starlark_fmt"
|
||||
|
||||
"github.com/google/blueprint"
|
||||
)
|
||||
|
||||
const (
|
||||
bazelIndent = 4
|
||||
)
|
||||
|
||||
type bazelVarExporter interface {
|
||||
asBazel(android.Config, exportedStringVariables, exportedStringListVariables, exportedConfigDependingVariables) []bazelConstant
|
||||
}
|
||||
|
@ -73,21 +70,6 @@ func (m exportedStringVariables) Set(k string, v string) {
|
|||
m[k] = v
|
||||
}
|
||||
|
||||
func bazelIndention(level int) string {
|
||||
return strings.Repeat(" ", level*bazelIndent)
|
||||
}
|
||||
|
||||
func printBazelList(items []string, indentLevel int) string {
|
||||
list := make([]string, 0, len(items)+2)
|
||||
list = append(list, "[")
|
||||
innerIndent := bazelIndention(indentLevel + 1)
|
||||
for _, item := range items {
|
||||
list = append(list, fmt.Sprintf(`%s"%s",`, innerIndent, item))
|
||||
}
|
||||
list = append(list, bazelIndention(indentLevel)+"]")
|
||||
return strings.Join(list, "\n")
|
||||
}
|
||||
|
||||
func (m exportedStringVariables) asBazel(config android.Config,
|
||||
stringVars exportedStringVariables, stringListVars exportedStringListVariables, cfgDepVars exportedConfigDependingVariables) []bazelConstant {
|
||||
ret := make([]bazelConstant, 0, len(m))
|
||||
|
@ -139,7 +121,7 @@ func (m exportedStringListVariables) asBazel(config android.Config,
|
|||
// out through a constants struct later.
|
||||
ret = append(ret, bazelConstant{
|
||||
variableName: k,
|
||||
internalDefinition: printBazelList(expandedVars, 0),
|
||||
internalDefinition: starlark_fmt.PrintStringList(expandedVars, 0),
|
||||
})
|
||||
}
|
||||
return ret
|
||||
|
@ -173,17 +155,6 @@ func (m exportedStringListDictVariables) Set(k string, v map[string][]string) {
|
|||
m[k] = v
|
||||
}
|
||||
|
||||
func printBazelStringListDict(dict map[string][]string) string {
|
||||
bazelDict := make([]string, 0, len(dict)+2)
|
||||
bazelDict = append(bazelDict, "{")
|
||||
for k, v := range dict {
|
||||
bazelDict = append(bazelDict,
|
||||
fmt.Sprintf(`%s"%s": %s,`, bazelIndention(1), k, printBazelList(v, 1)))
|
||||
}
|
||||
bazelDict = append(bazelDict, "}")
|
||||
return strings.Join(bazelDict, "\n")
|
||||
}
|
||||
|
||||
// Since dictionaries are not supported in Ninja, we do not expand variables for dictionaries
|
||||
func (m exportedStringListDictVariables) asBazel(_ android.Config, _ exportedStringVariables,
|
||||
_ exportedStringListVariables, _ exportedConfigDependingVariables) []bazelConstant {
|
||||
|
@ -191,7 +162,7 @@ func (m exportedStringListDictVariables) asBazel(_ android.Config, _ exportedStr
|
|||
for k, dict := range m {
|
||||
ret = append(ret, bazelConstant{
|
||||
variableName: k,
|
||||
internalDefinition: printBazelStringListDict(dict),
|
||||
internalDefinition: starlark_fmt.PrintStringListDict(dict, 0),
|
||||
})
|
||||
}
|
||||
return ret
|
||||
|
@ -223,7 +194,7 @@ func bazelToolchainVars(config android.Config, vars ...bazelVarExporter) string
|
|||
definitions = append(definitions,
|
||||
fmt.Sprintf("_%s = %s", b.variableName, b.internalDefinition))
|
||||
constants = append(constants,
|
||||
fmt.Sprintf("%[1]s%[2]s = _%[2]s,", bazelIndention(1), b.variableName))
|
||||
fmt.Sprintf("%[1]s%[2]s = _%[2]s,", starlark_fmt.Indention(1), b.variableName))
|
||||
}
|
||||
|
||||
// Build the exported constants struct.
|
||||
|
|
|
@ -211,15 +211,11 @@ constants = struct(
|
|||
expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
|
||||
|
||||
_a = {
|
||||
"b1": [
|
||||
"b2",
|
||||
],
|
||||
"b1": ["b2"],
|
||||
}
|
||||
|
||||
_c = {
|
||||
"d1": [
|
||||
"d2",
|
||||
],
|
||||
"d1": ["d2"],
|
||||
}
|
||||
|
||||
constants = struct(
|
||||
|
@ -246,27 +242,19 @@ constants = struct(
|
|||
expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
|
||||
|
||||
_a = {
|
||||
"a1": [
|
||||
"a2",
|
||||
],
|
||||
"a1": ["a2"],
|
||||
}
|
||||
|
||||
_b = "b-val"
|
||||
|
||||
_c = [
|
||||
"c-val",
|
||||
]
|
||||
_c = ["c-val"]
|
||||
|
||||
_d = "d-val"
|
||||
|
||||
_e = [
|
||||
"e-val",
|
||||
]
|
||||
_e = ["e-val"]
|
||||
|
||||
_f = {
|
||||
"f1": [
|
||||
"f2",
|
||||
],
|
||||
"f1": ["f2"],
|
||||
}
|
||||
|
||||
constants = struct(
|
||||
|
|
28
starlark_fmt/Android.bp
Normal file
28
starlark_fmt/Android.bp
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2022 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-starlark-format",
|
||||
pkgPath: "android/soong/starlark_fmt",
|
||||
srcs: [
|
||||
"format.go",
|
||||
],
|
||||
testSrcs: [
|
||||
"format_test.go",
|
||||
],
|
||||
}
|
96
starlark_fmt/format.go
Normal file
96
starlark_fmt/format.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
// Copyright 2022 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 starlark_fmt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
indent = 4
|
||||
)
|
||||
|
||||
// Indention returns an indent string of the specified level.
|
||||
func Indention(level int) string {
|
||||
if level < 0 {
|
||||
panic(fmt.Errorf("indent level cannot be less than 0, but got %d", level))
|
||||
}
|
||||
return strings.Repeat(" ", level*indent)
|
||||
}
|
||||
|
||||
// PrintBool returns a Starlark compatible bool string.
|
||||
func PrintBool(item bool) string {
|
||||
return strings.Title(fmt.Sprintf("%t", item))
|
||||
}
|
||||
|
||||
// PrintsStringList returns a Starlark-compatible string of a list of Strings/Labels.
|
||||
func PrintStringList(items []string, indentLevel int) string {
|
||||
return PrintList(items, indentLevel, `"%s"`)
|
||||
}
|
||||
|
||||
// PrintList returns a Starlark-compatible string of list formmated as requested.
|
||||
func PrintList(items []string, indentLevel int, formatString string) string {
|
||||
if len(items) == 0 {
|
||||
return "[]"
|
||||
} else if len(items) == 1 {
|
||||
return fmt.Sprintf("["+formatString+"]", items[0])
|
||||
}
|
||||
list := make([]string, 0, len(items)+2)
|
||||
list = append(list, "[")
|
||||
innerIndent := Indention(indentLevel + 1)
|
||||
for _, item := range items {
|
||||
list = append(list, fmt.Sprintf(`%s`+formatString+`,`, innerIndent, item))
|
||||
}
|
||||
list = append(list, Indention(indentLevel)+"]")
|
||||
return strings.Join(list, "\n")
|
||||
}
|
||||
|
||||
// PrintStringListDict returns a Starlark-compatible string formatted as dictionary with
|
||||
// string keys and list of string values.
|
||||
func PrintStringListDict(dict map[string][]string, indentLevel int) string {
|
||||
formattedValueDict := make(map[string]string, len(dict))
|
||||
for k, v := range dict {
|
||||
formattedValueDict[k] = PrintStringList(v, indentLevel+1)
|
||||
}
|
||||
return PrintDict(formattedValueDict, indentLevel)
|
||||
}
|
||||
|
||||
// PrintBoolDict returns a starlark-compatible string containing a dictionary with string keys and
|
||||
// values printed with no additional formatting.
|
||||
func PrintBoolDict(dict map[string]bool, indentLevel int) string {
|
||||
formattedValueDict := make(map[string]string, len(dict))
|
||||
for k, v := range dict {
|
||||
formattedValueDict[k] = PrintBool(v)
|
||||
}
|
||||
return PrintDict(formattedValueDict, indentLevel)
|
||||
}
|
||||
|
||||
// PrintDict returns a starlark-compatible string containing a dictionary with string keys and
|
||||
// values printed with no additional formatting.
|
||||
func PrintDict(dict map[string]string, indentLevel int) string {
|
||||
if len(dict) == 0 {
|
||||
return "{}"
|
||||
}
|
||||
items := make([]string, 0, len(dict))
|
||||
for k, v := range dict {
|
||||
items = append(items, fmt.Sprintf(`%s"%s": %s,`, Indention(indentLevel+1), k, v))
|
||||
}
|
||||
sort.Strings(items)
|
||||
return fmt.Sprintf(`{
|
||||
%s
|
||||
%s}`, strings.Join(items, "\n"), Indention(indentLevel))
|
||||
}
|
169
starlark_fmt/format_test.go
Normal file
169
starlark_fmt/format_test.go
Normal file
|
@ -0,0 +1,169 @@
|
|||
// Copyright 2022 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 starlark_fmt
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPrintEmptyStringList(t *testing.T) {
|
||||
in := []string{}
|
||||
indentLevel := 0
|
||||
out := PrintStringList(in, indentLevel)
|
||||
expectedOut := "[]"
|
||||
if out != expectedOut {
|
||||
t.Errorf("Expected %q, got %q", expectedOut, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintSingleElementStringList(t *testing.T) {
|
||||
in := []string{"a"}
|
||||
indentLevel := 0
|
||||
out := PrintStringList(in, indentLevel)
|
||||
expectedOut := `["a"]`
|
||||
if out != expectedOut {
|
||||
t.Errorf("Expected %q, got %q", expectedOut, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintMultiElementStringList(t *testing.T) {
|
||||
in := []string{"a", "b"}
|
||||
indentLevel := 0
|
||||
out := PrintStringList(in, indentLevel)
|
||||
expectedOut := `[
|
||||
"a",
|
||||
"b",
|
||||
]`
|
||||
if out != expectedOut {
|
||||
t.Errorf("Expected %q, got %q", expectedOut, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintEmptyList(t *testing.T) {
|
||||
in := []string{}
|
||||
indentLevel := 0
|
||||
out := PrintList(in, indentLevel, "%s")
|
||||
expectedOut := "[]"
|
||||
if out != expectedOut {
|
||||
t.Errorf("Expected %q, got %q", expectedOut, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintSingleElementList(t *testing.T) {
|
||||
in := []string{"1"}
|
||||
indentLevel := 0
|
||||
out := PrintList(in, indentLevel, "%s")
|
||||
expectedOut := `[1]`
|
||||
if out != expectedOut {
|
||||
t.Errorf("Expected %q, got %q", expectedOut, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintMultiElementList(t *testing.T) {
|
||||
in := []string{"1", "2"}
|
||||
indentLevel := 0
|
||||
out := PrintList(in, indentLevel, "%s")
|
||||
expectedOut := `[
|
||||
1,
|
||||
2,
|
||||
]`
|
||||
if out != expectedOut {
|
||||
t.Errorf("Expected %q, got %q", expectedOut, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListWithNonZeroIndent(t *testing.T) {
|
||||
in := []string{"1", "2"}
|
||||
indentLevel := 1
|
||||
out := PrintList(in, indentLevel, "%s")
|
||||
expectedOut := `[
|
||||
1,
|
||||
2,
|
||||
]`
|
||||
if out != expectedOut {
|
||||
t.Errorf("Expected %q, got %q", expectedOut, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringListDictEmpty(t *testing.T) {
|
||||
in := map[string][]string{}
|
||||
indentLevel := 0
|
||||
out := PrintStringListDict(in, indentLevel)
|
||||
expectedOut := `{}`
|
||||
if out != expectedOut {
|
||||
t.Errorf("Expected %q, got %q", expectedOut, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringListDict(t *testing.T) {
|
||||
in := map[string][]string{
|
||||
"key1": []string{},
|
||||
"key2": []string{"a"},
|
||||
"key3": []string{"1", "2"},
|
||||
}
|
||||
indentLevel := 0
|
||||
out := PrintStringListDict(in, indentLevel)
|
||||
expectedOut := `{
|
||||
"key1": [],
|
||||
"key2": ["a"],
|
||||
"key3": [
|
||||
"1",
|
||||
"2",
|
||||
],
|
||||
}`
|
||||
if out != expectedOut {
|
||||
t.Errorf("Expected %q, got %q", expectedOut, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintDict(t *testing.T) {
|
||||
in := map[string]string{
|
||||
"key1": `""`,
|
||||
"key2": `"a"`,
|
||||
"key3": `[
|
||||
1,
|
||||
2,
|
||||
]`,
|
||||
}
|
||||
indentLevel := 0
|
||||
out := PrintDict(in, indentLevel)
|
||||
expectedOut := `{
|
||||
"key1": "",
|
||||
"key2": "a",
|
||||
"key3": [
|
||||
1,
|
||||
2,
|
||||
],
|
||||
}`
|
||||
if out != expectedOut {
|
||||
t.Errorf("Expected %q, got %q", expectedOut, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintDictWithIndent(t *testing.T) {
|
||||
in := map[string]string{
|
||||
"key1": `""`,
|
||||
"key2": `"a"`,
|
||||
}
|
||||
indentLevel := 1
|
||||
out := PrintDict(in, indentLevel)
|
||||
expectedOut := `{
|
||||
"key1": "",
|
||||
"key2": "a",
|
||||
}`
|
||||
if out != expectedOut {
|
||||
t.Errorf("Expected %q, got %q", expectedOut, out)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue