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-remoteexec",
|
||||||
"soong-response",
|
"soong-response",
|
||||||
"soong-shared",
|
"soong-shared",
|
||||||
|
"soong-starlark-format",
|
||||||
"soong-ui-metrics_proto",
|
"soong-ui-metrics_proto",
|
||||||
|
|
||||||
"golang-protobuf-proto",
|
"golang-protobuf-proto",
|
||||||
"golang-protobuf-encoding-prototext",
|
"golang-protobuf-encoding-prototext",
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ import (
|
||||||
"android/soong/android/soongconfig"
|
"android/soong/android/soongconfig"
|
||||||
"android/soong/bazel"
|
"android/soong/bazel"
|
||||||
"android/soong/remoteexec"
|
"android/soong/remoteexec"
|
||||||
|
"android/soong/starlark_fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bool re-exports proptools.Bool for the android package.
|
// 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 := starlark_fmt.PrintStringList(nonArchVariantProductVariables, 0)
|
||||||
nonArchVariantProductVariablesJson, err := json.MarshalIndent(&nonArchVariantProductVariables, "", " ")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot marshal product variable data: %s", err.Error())
|
return fmt.Errorf("cannot marshal product variable data: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO(b/216168792) should use common function to print Starlark code
|
archVariantProductVariablesJson := starlark_fmt.PrintStringList(archVariantProductVariables, 0)
|
||||||
archVariantProductVariablesJson, err := json.MarshalIndent(&archVariantProductVariables, "", " ")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot marshal arch variant product variable data: %s", err.Error())
|
return fmt.Errorf("cannot marshal arch variant product variable data: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
@ -386,6 +386,46 @@ func TestNonExistentPropertyInSoongConfigModule(t *testing.T) {
|
||||||
})).RunTest(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 {
|
func testConfigWithVendorVars(buildDir, bp string, fs map[string][]byte, vendorVars map[string]map[string]string) Config {
|
||||||
config := TestConfig(buildDir, nil, bp, fs)
|
config := TestConfig(buildDir, nil, bp, fs)
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ bootstrap_go_package {
|
||||||
"blueprint-parser",
|
"blueprint-parser",
|
||||||
"blueprint-proptools",
|
"blueprint-proptools",
|
||||||
"soong-bazel",
|
"soong-bazel",
|
||||||
|
"soong-starlark-format",
|
||||||
],
|
],
|
||||||
srcs: [
|
srcs: [
|
||||||
"config.go",
|
"config.go",
|
||||||
|
|
|
@ -25,6 +25,8 @@ import (
|
||||||
"github.com/google/blueprint"
|
"github.com/google/blueprint"
|
||||||
"github.com/google/blueprint/parser"
|
"github.com/google/blueprint/parser"
|
||||||
"github.com/google/blueprint/proptools"
|
"github.com/google/blueprint/proptools"
|
||||||
|
|
||||||
|
"android/soong/starlark_fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
const conditionsDefault = "conditions_default"
|
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")}
|
return []error{fmt.Errorf("values property must be set")}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vals := make(map[string]bool, len(stringProps.Values))
|
||||||
for _, name := range stringProps.Values {
|
for _, name := range stringProps.Values {
|
||||||
if err := checkVariableName(name); err != nil {
|
if err := checkVariableName(name); err != nil {
|
||||||
return []error{fmt.Errorf("soong_config_string_variable: values property error %s", err)}
|
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{
|
v.variables[base.variable] = &stringVariable{
|
||||||
|
@ -235,7 +241,12 @@ type SoongConfigDefinition struct {
|
||||||
// string vars, bool vars and value vars created by every
|
// string vars, bool vars and value vars created by every
|
||||||
// soong_config_module_type in this build.
|
// soong_config_module_type in this build.
|
||||||
type Bp2BuildSoongConfigDefinitions struct {
|
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
|
BoolVars map[string]bool
|
||||||
ValueVars map[string]bool
|
ValueVars map[string]bool
|
||||||
}
|
}
|
||||||
|
@ -253,7 +264,7 @@ func (defs *Bp2BuildSoongConfigDefinitions) AddVars(mtDef SoongConfigDefinition)
|
||||||
defer bp2buildSoongConfigVarsLock.Unlock()
|
defer bp2buildSoongConfigVarsLock.Unlock()
|
||||||
|
|
||||||
if defs.StringVars == nil {
|
if defs.StringVars == nil {
|
||||||
defs.StringVars = make(map[string]map[string]bool)
|
defs.StringVars = make(map[string][]string)
|
||||||
}
|
}
|
||||||
if defs.BoolVars == nil {
|
if defs.BoolVars == nil {
|
||||||
defs.BoolVars = make(map[string]bool)
|
defs.BoolVars = make(map[string]bool)
|
||||||
|
@ -261,15 +272,24 @@ func (defs *Bp2BuildSoongConfigDefinitions) AddVars(mtDef SoongConfigDefinition)
|
||||||
if defs.ValueVars == nil {
|
if defs.ValueVars == nil {
|
||||||
defs.ValueVars = make(map[string]bool)
|
defs.ValueVars = make(map[string]bool)
|
||||||
}
|
}
|
||||||
|
if defs.varCache == nil {
|
||||||
|
defs.varCache = make(map[string]bool)
|
||||||
|
}
|
||||||
for _, moduleType := range mtDef.ModuleTypes {
|
for _, moduleType := range mtDef.ModuleTypes {
|
||||||
for _, v := range moduleType.Variables {
|
for _, v := range moduleType.Variables {
|
||||||
key := strings.Join([]string{moduleType.ConfigNamespace, v.variableProperty()}, "__")
|
key := strings.Join([]string{moduleType.ConfigNamespace, v.variableProperty()}, "__")
|
||||||
if strVar, ok := v.(*stringVariable); ok {
|
|
||||||
if _, ok := defs.StringVars[key]; !ok {
|
// The same variable may be used in multiple module types (for example, if need support
|
||||||
defs.StringVars[key] = make(map[string]bool, 0)
|
// 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 {
|
||||||
for _, value := range strVar.values {
|
for _, value := range strVar.values {
|
||||||
defs.StringVars[key][value] = true
|
defs.StringVars[key] = append(defs.StringVars[key], value)
|
||||||
}
|
}
|
||||||
} else if _, ok := v.(*boolVariable); ok {
|
} else if _, ok := v.(*boolVariable); ok {
|
||||||
defs.BoolVars[key] = true
|
defs.BoolVars[key] = true
|
||||||
|
@ -302,29 +322,16 @@ func sortedStringKeys(m interface{}) []string {
|
||||||
// String emits the Soong config variable definitions as Starlark dictionaries.
|
// String emits the Soong config variable definitions as Starlark dictionaries.
|
||||||
func (defs Bp2BuildSoongConfigDefinitions) String() string {
|
func (defs Bp2BuildSoongConfigDefinitions) String() string {
|
||||||
ret := ""
|
ret := ""
|
||||||
ret += "soong_config_bool_variables = {\n"
|
ret += "soong_config_bool_variables = "
|
||||||
for _, boolVar := range sortedStringKeys(defs.BoolVars) {
|
ret += starlark_fmt.PrintBoolDict(defs.BoolVars, 0)
|
||||||
ret += fmt.Sprintf(" \"%s\": True,\n", boolVar)
|
ret += "\n\n"
|
||||||
}
|
|
||||||
ret += "}\n"
|
|
||||||
ret += "\n"
|
|
||||||
|
|
||||||
ret += "soong_config_value_variables = {\n"
|
ret += "soong_config_value_variables = "
|
||||||
for _, valueVar := range sortedStringKeys(defs.ValueVars) {
|
ret += starlark_fmt.PrintBoolDict(defs.ValueVars, 0)
|
||||||
ret += fmt.Sprintf(" \"%s\": True,\n", valueVar)
|
ret += "\n\n"
|
||||||
}
|
|
||||||
ret += "}\n"
|
|
||||||
ret += "\n"
|
|
||||||
|
|
||||||
ret += "soong_config_string_variables = {\n"
|
ret += "soong_config_string_variables = "
|
||||||
for _, stringVar := range sortedStringKeys(defs.StringVars) {
|
ret += starlark_fmt.PrintStringListDict(defs.StringVars, 0)
|
||||||
ret += fmt.Sprintf(" \"%s\": [\n", stringVar)
|
|
||||||
for _, choice := range sortedStringKeys(defs.StringVars[stringVar]) {
|
|
||||||
ret += fmt.Sprintf(" \"%s\",\n", choice)
|
|
||||||
}
|
|
||||||
ret += fmt.Sprintf(" ],\n")
|
|
||||||
}
|
|
||||||
ret += "}"
|
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
|
@ -367,19 +367,19 @@ func Test_PropertiesToApply(t *testing.T) {
|
||||||
|
|
||||||
func Test_Bp2BuildSoongConfigDefinitions(t *testing.T) {
|
func Test_Bp2BuildSoongConfigDefinitions(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
defs Bp2BuildSoongConfigDefinitions
|
defs Bp2BuildSoongConfigDefinitions
|
||||||
expected string
|
expected string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
desc: "all empty",
|
||||||
defs: Bp2BuildSoongConfigDefinitions{},
|
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{
|
defs: Bp2BuildSoongConfigDefinitions{
|
||||||
BoolVars: map[string]bool{
|
BoolVars: map[string]bool{
|
||||||
"bool_var": true,
|
"bool_var": true,
|
||||||
|
@ -389,39 +389,35 @@ soong_config_string_variables = {
|
||||||
"bool_var": True,
|
"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{
|
defs: Bp2BuildSoongConfigDefinitions{
|
||||||
ValueVars: map[string]bool{
|
ValueVars: map[string]bool{
|
||||||
"value_var": true,
|
"value_var": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: `soong_config_bool_variables = {
|
expected: `soong_config_bool_variables = {}
|
||||||
}
|
|
||||||
|
|
||||||
soong_config_value_variables = {
|
soong_config_value_variables = {
|
||||||
"value_var": True,
|
"value_var": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
soong_config_string_variables = {
|
soong_config_string_variables = {}`}, {
|
||||||
}`}, {
|
desc: "only string vars",
|
||||||
defs: Bp2BuildSoongConfigDefinitions{
|
defs: Bp2BuildSoongConfigDefinitions{
|
||||||
StringVars: map[string]map[string]bool{
|
StringVars: map[string][]string{
|
||||||
"string_var": map[string]bool{
|
"string_var": []string{
|
||||||
"choice1": true,
|
"choice1",
|
||||||
"choice2": true,
|
"choice2",
|
||||||
"choice3": true,
|
"choice3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
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 = {
|
||||||
"string_var": [
|
"string_var": [
|
||||||
|
@ -430,6 +426,7 @@ soong_config_string_variables = {
|
||||||
"choice3",
|
"choice3",
|
||||||
],
|
],
|
||||||
}`}, {
|
}`}, {
|
||||||
|
desc: "all vars",
|
||||||
defs: Bp2BuildSoongConfigDefinitions{
|
defs: Bp2BuildSoongConfigDefinitions{
|
||||||
BoolVars: map[string]bool{
|
BoolVars: map[string]bool{
|
||||||
"bool_var_one": true,
|
"bool_var_one": true,
|
||||||
|
@ -438,15 +435,15 @@ soong_config_string_variables = {
|
||||||
"value_var_one": true,
|
"value_var_one": true,
|
||||||
"value_var_two": true,
|
"value_var_two": true,
|
||||||
},
|
},
|
||||||
StringVars: map[string]map[string]bool{
|
StringVars: map[string][]string{
|
||||||
"string_var_one": map[string]bool{
|
"string_var_one": []string{
|
||||||
"choice1": true,
|
"choice1",
|
||||||
"choice2": true,
|
"choice2",
|
||||||
"choice3": true,
|
"choice3",
|
||||||
},
|
},
|
||||||
"string_var_two": map[string]bool{
|
"string_var_two": []string{
|
||||||
"foo": true,
|
"foo",
|
||||||
"bar": true,
|
"bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -466,15 +463,17 @@ soong_config_string_variables = {
|
||||||
"choice3",
|
"choice3",
|
||||||
],
|
],
|
||||||
"string_var_two": [
|
"string_var_two": [
|
||||||
"bar",
|
|
||||||
"foo",
|
"foo",
|
||||||
|
"bar",
|
||||||
],
|
],
|
||||||
}`},
|
}`},
|
||||||
}
|
}
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
actual := test.defs.String()
|
actual := test.defs.String()
|
||||||
if actual != test.expected {
|
if actual != test.expected {
|
||||||
t.Errorf("Expected:\n%s\nbut got:\n%s", test.expected, actual)
|
t.Errorf("Expected:\n%s\nbut got:\n%s", test.expected, actual)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ bootstrap_go_package {
|
||||||
"soong-genrule",
|
"soong-genrule",
|
||||||
"soong-python",
|
"soong-python",
|
||||||
"soong-sh",
|
"soong-sh",
|
||||||
|
"soong-starlark-format",
|
||||||
"soong-ui-metrics",
|
"soong-ui-metrics",
|
||||||
],
|
],
|
||||||
testSrcs: [
|
testSrcs: [
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
|
|
||||||
"android/soong/android"
|
"android/soong/android"
|
||||||
"android/soong/bazel"
|
"android/soong/bazel"
|
||||||
|
"android/soong/starlark_fmt"
|
||||||
|
|
||||||
"github.com/google/blueprint"
|
"github.com/google/blueprint"
|
||||||
"github.com/google/blueprint/proptools"
|
"github.com/google/blueprint/proptools"
|
||||||
|
@ -559,48 +560,27 @@ func prettyPrint(propertyValue reflect.Value, indent int, emitZeroValues bool) (
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret string
|
|
||||||
switch propertyValue.Kind() {
|
switch propertyValue.Kind() {
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
ret = fmt.Sprintf("\"%v\"", escapeString(propertyValue.String()))
|
return fmt.Sprintf("\"%v\"", escapeString(propertyValue.String())), nil
|
||||||
case reflect.Bool:
|
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:
|
case reflect.Int, reflect.Uint, reflect.Int64:
|
||||||
ret = fmt.Sprintf("%v", propertyValue.Interface())
|
return fmt.Sprintf("%v", propertyValue.Interface()), nil
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
return prettyPrint(propertyValue.Elem(), indent, emitZeroValues)
|
return prettyPrint(propertyValue.Elem(), indent, emitZeroValues)
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
if propertyValue.Len() == 0 {
|
elements := make([]string, 0, propertyValue.Len())
|
||||||
return "[]", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if propertyValue.Len() == 1 {
|
|
||||||
// Single-line list for list with only 1 element
|
|
||||||
ret += "["
|
|
||||||
indexedValue, err := prettyPrint(propertyValue.Index(0), 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++ {
|
for i := 0; i < propertyValue.Len(); i++ {
|
||||||
indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1, emitZeroValues)
|
val, err := prettyPrint(propertyValue.Index(i), indent, emitZeroValues)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
if val != "" {
|
||||||
if indexedValue != "" {
|
elements = append(elements, val)
|
||||||
ret += makeIndent(indent + 1)
|
|
||||||
ret += indexedValue
|
|
||||||
ret += ",\n"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret += makeIndent(indent)
|
return starlark_fmt.PrintList(elements, indent, "%s"), nil
|
||||||
ret += "]"
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
// Special cases where the bp2build sends additional information to the codegenerator
|
// 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
|
return fmt.Sprintf("%q", label.Label), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = "{\n"
|
|
||||||
// Sort and print the struct props by the key.
|
// Sort and print the struct props by the key.
|
||||||
structProps := extractStructProperties(propertyValue, indent)
|
structProps := extractStructProperties(propertyValue, indent)
|
||||||
if len(structProps) == 0 {
|
if len(structProps) == 0 {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
for _, k := range android.SortedStringKeys(structProps) {
|
return starlark_fmt.PrintDict(structProps, indent), nil
|
||||||
ret += makeIndent(indent + 1)
|
|
||||||
ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
|
|
||||||
}
|
|
||||||
ret += makeIndent(indent)
|
|
||||||
ret += "}"
|
|
||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
// TODO(b/164227191): implement pretty print for interfaces.
|
// TODO(b/164227191): implement pretty print for interfaces.
|
||||||
// Interfaces are used for for arch, multilib and target properties.
|
// 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(
|
return "", fmt.Errorf(
|
||||||
"unexpected kind for property struct field: %s", propertyValue.Kind())
|
"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,
|
// 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, "\"", "\\\"")
|
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 {
|
func targetNameWithVariant(c bpToBuildContext, logicModule blueprint.Module) string {
|
||||||
name := ""
|
name := ""
|
||||||
if c.ModuleSubDir(logicModule) != "" {
|
if c.ModuleSubDir(logicModule) != "" {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"android/soong/android"
|
"android/soong/android"
|
||||||
"android/soong/bazel"
|
"android/soong/bazel"
|
||||||
|
"android/soong/starlark_fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Configurability support for bp2build.
|
// Configurability support for bp2build.
|
||||||
|
@ -250,10 +251,10 @@ func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue *stri
|
||||||
} else if defaultValue != nil {
|
} else if defaultValue != nil {
|
||||||
// Print an explicit empty list (the default value) even if the value is
|
// Print an explicit empty list (the default value) even if the value is
|
||||||
// empty, to avoid errors about not finding a configuration that matches.
|
// 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 += "})"
|
ret += "})"
|
||||||
|
|
||||||
return ret, nil
|
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
|
// prettyPrintSelectEntry converts a reflect.Value into an entry in a select map
|
||||||
// with a provided key.
|
// with a provided key.
|
||||||
func prettyPrintSelectEntry(value reflect.Value, key string, indent int, emitZeroValues bool) (string, error) {
|
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)
|
v, err := prettyPrint(value, indent+1, emitZeroValues)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
|
@ -8,6 +8,7 @@ bootstrap_go_package {
|
||||||
deps: [
|
deps: [
|
||||||
"soong-android",
|
"soong-android",
|
||||||
"soong-remoteexec",
|
"soong-remoteexec",
|
||||||
|
"soong-starlark-format",
|
||||||
],
|
],
|
||||||
srcs: [
|
srcs: [
|
||||||
"bp2build.go",
|
"bp2build.go",
|
||||||
|
|
|
@ -22,14 +22,11 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"android/soong/android"
|
"android/soong/android"
|
||||||
|
"android/soong/starlark_fmt"
|
||||||
|
|
||||||
"github.com/google/blueprint"
|
"github.com/google/blueprint"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
bazelIndent = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
type bazelVarExporter interface {
|
type bazelVarExporter interface {
|
||||||
asBazel(android.Config, exportedStringVariables, exportedStringListVariables, exportedConfigDependingVariables) []bazelConstant
|
asBazel(android.Config, exportedStringVariables, exportedStringListVariables, exportedConfigDependingVariables) []bazelConstant
|
||||||
}
|
}
|
||||||
|
@ -73,21 +70,6 @@ func (m exportedStringVariables) Set(k string, v string) {
|
||||||
m[k] = v
|
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,
|
func (m exportedStringVariables) asBazel(config android.Config,
|
||||||
stringVars exportedStringVariables, stringListVars exportedStringListVariables, cfgDepVars exportedConfigDependingVariables) []bazelConstant {
|
stringVars exportedStringVariables, stringListVars exportedStringListVariables, cfgDepVars exportedConfigDependingVariables) []bazelConstant {
|
||||||
ret := make([]bazelConstant, 0, len(m))
|
ret := make([]bazelConstant, 0, len(m))
|
||||||
|
@ -139,7 +121,7 @@ func (m exportedStringListVariables) asBazel(config android.Config,
|
||||||
// out through a constants struct later.
|
// out through a constants struct later.
|
||||||
ret = append(ret, bazelConstant{
|
ret = append(ret, bazelConstant{
|
||||||
variableName: k,
|
variableName: k,
|
||||||
internalDefinition: printBazelList(expandedVars, 0),
|
internalDefinition: starlark_fmt.PrintStringList(expandedVars, 0),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
|
@ -173,17 +155,6 @@ func (m exportedStringListDictVariables) Set(k string, v map[string][]string) {
|
||||||
m[k] = v
|
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
|
// Since dictionaries are not supported in Ninja, we do not expand variables for dictionaries
|
||||||
func (m exportedStringListDictVariables) asBazel(_ android.Config, _ exportedStringVariables,
|
func (m exportedStringListDictVariables) asBazel(_ android.Config, _ exportedStringVariables,
|
||||||
_ exportedStringListVariables, _ exportedConfigDependingVariables) []bazelConstant {
|
_ exportedStringListVariables, _ exportedConfigDependingVariables) []bazelConstant {
|
||||||
|
@ -191,7 +162,7 @@ func (m exportedStringListDictVariables) asBazel(_ android.Config, _ exportedStr
|
||||||
for k, dict := range m {
|
for k, dict := range m {
|
||||||
ret = append(ret, bazelConstant{
|
ret = append(ret, bazelConstant{
|
||||||
variableName: k,
|
variableName: k,
|
||||||
internalDefinition: printBazelStringListDict(dict),
|
internalDefinition: starlark_fmt.PrintStringListDict(dict, 0),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
|
@ -223,7 +194,7 @@ func bazelToolchainVars(config android.Config, vars ...bazelVarExporter) string
|
||||||
definitions = append(definitions,
|
definitions = append(definitions,
|
||||||
fmt.Sprintf("_%s = %s", b.variableName, b.internalDefinition))
|
fmt.Sprintf("_%s = %s", b.variableName, b.internalDefinition))
|
||||||
constants = append(constants,
|
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.
|
// Build the exported constants struct.
|
||||||
|
|
|
@ -211,15 +211,11 @@ constants = struct(
|
||||||
expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
|
expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
|
||||||
|
|
||||||
_a = {
|
_a = {
|
||||||
"b1": [
|
"b1": ["b2"],
|
||||||
"b2",
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_c = {
|
_c = {
|
||||||
"d1": [
|
"d1": ["d2"],
|
||||||
"d2",
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constants = struct(
|
constants = struct(
|
||||||
|
@ -246,27 +242,19 @@ constants = struct(
|
||||||
expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
|
expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
|
||||||
|
|
||||||
_a = {
|
_a = {
|
||||||
"a1": [
|
"a1": ["a2"],
|
||||||
"a2",
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_b = "b-val"
|
_b = "b-val"
|
||||||
|
|
||||||
_c = [
|
_c = ["c-val"]
|
||||||
"c-val",
|
|
||||||
]
|
|
||||||
|
|
||||||
_d = "d-val"
|
_d = "d-val"
|
||||||
|
|
||||||
_e = [
|
_e = ["e-val"]
|
||||||
"e-val",
|
|
||||||
]
|
|
||||||
|
|
||||||
_f = {
|
_f = {
|
||||||
"f1": [
|
"f1": ["f2"],
|
||||||
"f2",
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constants = struct(
|
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