diff --git a/android/Android.bp b/android/Android.bp index c072ac230..49d5b91a9 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -36,6 +36,7 @@ bootstrap_go_package { "bazel_handler.go", "bazel_paths.go", "config.go", + "config_bp2build.go", "csuite_config.go", "deapexer.go", "defaults.go", @@ -96,6 +97,7 @@ bootstrap_go_package { "bazel_handler_test.go", "bazel_test.go", "config_test.go", + "config_bp2build_test.go", "csuite_config_test.go", "defaults_test.go", "depset_test.go", diff --git a/android/api_levels.go b/android/api_levels.go index 27a3b7fd8..81638940c 100644 --- a/android/api_levels.go +++ b/android/api_levels.go @@ -19,6 +19,7 @@ import ( "fmt" "strconv" + "android/soong/bazel" "android/soong/starlark_fmt" ) @@ -393,10 +394,10 @@ func printApiLevelsStarlarkDict(config Config) string { } func StarlarkApiLevelConfigs(config Config) string { - return fmt.Sprintf(`# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT. + return fmt.Sprintf(bazel.GeneratedBazelFileWarning+` _api_levels = %s api_levels = _api_levels `, printApiLevelsStarlarkDict(config), ) -} \ No newline at end of file +} diff --git a/cc/config/bp2build.go b/android/config_bp2build.go similarity index 51% rename from cc/config/bp2build.go rename to android/config_bp2build.go index 73f65f539..748be62cc 100644 --- a/cc/config/bp2build.go +++ b/android/config_bp2build.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package config +package android import ( "fmt" @@ -21,33 +21,123 @@ import ( "sort" "strings" - "android/soong/android" + "android/soong/bazel" "android/soong/starlark_fmt" "github.com/google/blueprint" ) -type bazelVarExporter interface { - asBazel(android.Config, exportedStringVariables, exportedStringListVariables, exportedConfigDependingVariables) []bazelConstant +// BazelVarExporter is a collection of configuration variables that can be exported for use in Bazel rules +type BazelVarExporter interface { + // asBazel expands strings of configuration variables into their concrete values + asBazel(Config, ExportedStringVariables, ExportedStringListVariables, ExportedConfigDependingVariables) []bazelConstant } -// Helpers for exporting cc configuration information to Bazel. -var ( +// ExportedVariables is a collection of interdependent configuration variables +type ExportedVariables struct { // Maps containing toolchain variables that are independent of the // environment variables of the build. - exportedStringListVars = exportedStringListVariables{} - exportedStringVars = exportedStringVariables{} - exportedStringListDictVars = exportedStringListDictVariables{} - // Note: these can only contain references to other variables and must be printed last - exportedVariableReferenceDictVars = exportedVariableReferenceDictVariables{} + exportedStringVars ExportedStringVariables + exportedStringListVars ExportedStringListVariables + exportedStringListDictVars ExportedStringListDictVariables + + exportedVariableReferenceDictVars ExportedVariableReferenceDictVariables /// Maps containing variables that are dependent on the build config. - exportedConfigDependingVars = exportedConfigDependingVariables{} -) + exportedConfigDependingVars ExportedConfigDependingVariables -type exportedConfigDependingVariables map[string]interface{} + pctx PackageContext +} -func (m exportedConfigDependingVariables) Set(k string, v interface{}) { +// NewExportedVariables creats an empty ExportedVariables struct with non-nil maps +func NewExportedVariables(pctx PackageContext) ExportedVariables { + return ExportedVariables{ + exportedStringVars: ExportedStringVariables{}, + exportedStringListVars: ExportedStringListVariables{}, + exportedStringListDictVars: ExportedStringListDictVariables{}, + exportedVariableReferenceDictVars: ExportedVariableReferenceDictVariables{}, + exportedConfigDependingVars: ExportedConfigDependingVariables{}, + pctx: pctx, + } +} + +func (ev ExportedVariables) asBazel(config Config, + stringVars ExportedStringVariables, stringListVars ExportedStringListVariables, cfgDepVars ExportedConfigDependingVariables) []bazelConstant { + ret := []bazelConstant{} + ret = append(ret, ev.exportedStringVars.asBazel(config, stringVars, stringListVars, cfgDepVars)...) + ret = append(ret, ev.exportedStringListVars.asBazel(config, stringVars, stringListVars, cfgDepVars)...) + ret = append(ret, ev.exportedStringListDictVars.asBazel(config, stringVars, stringListVars, cfgDepVars)...) + // Note: ExportedVariableReferenceDictVars collections can only contain references to other variables and must be printed last + ret = append(ret, ev.exportedVariableReferenceDictVars.asBazel(config, stringVars, stringListVars, cfgDepVars)...) + return ret +} + +// ExportStringStaticVariable declares a static string variable and exports it to +// Bazel's toolchain. +func (ev ExportedVariables) ExportStringStaticVariable(name string, value string) { + ev.pctx.StaticVariable(name, value) + ev.exportedStringVars.set(name, value) +} + +// ExportStringListStaticVariable declares a static variable and exports it to +// Bazel's toolchain. +func (ev ExportedVariables) ExportStringListStaticVariable(name string, value []string) { + ev.pctx.StaticVariable(name, strings.Join(value, " ")) + ev.exportedStringListVars.set(name, value) +} + +// ExportVariableConfigMethod declares a variable whose value is evaluated at +// runtime via a function with access to the Config and exports it to Bazel's +// toolchain. +func (ev ExportedVariables) ExportVariableConfigMethod(name string, method interface{}) blueprint.Variable { + ev.exportedConfigDependingVars.set(name, method) + return ev.pctx.VariableConfigMethod(name, method) +} + +// ExportSourcePathVariable declares a static "source path" variable and exports +// it to Bazel's toolchain. +func (ev ExportedVariables) ExportSourcePathVariable(name string, value string) { + ev.pctx.SourcePathVariable(name, value) + ev.exportedStringVars.set(name, value) +} + +// ExportVariableFuncVariable declares a variable whose value is evaluated at +// runtime via a function and exports it to Bazel's toolchain. +func (ev ExportedVariables) ExportVariableFuncVariable(name string, f func() string) { + ev.exportedConfigDependingVars.set(name, func(config Config) string { + return f() + }) + ev.pctx.VariableFunc(name, func(PackageVarContext) string { + return f() + }) +} + +// ExportString only exports a variable to Bazel, but does not declare it in Soong +func (ev ExportedVariables) ExportString(name string, value string) { + ev.exportedStringVars.set(name, value) +} + +// ExportStringList only exports a variable to Bazel, but does not declare it in Soong +func (ev ExportedVariables) ExportStringList(name string, value []string) { + ev.exportedStringListVars.set(name, value) +} + +// ExportStringListDict only exports a variable to Bazel, but does not declare it in Soong +func (ev ExportedVariables) ExportStringListDict(name string, value map[string][]string) { + ev.exportedStringListDictVars.set(name, value) +} + +// ExportVariableReferenceDict only exports a variable to Bazel, but does not declare it in Soong +func (ev ExportedVariables) ExportVariableReferenceDict(name string, value map[string]string) { + ev.exportedVariableReferenceDictVars.set(name, value) +} + +// ExportedConfigDependingVariables is a mapping of variable names to functions +// of type func(config Config) string which return the runtime-evaluated string +// value of a particular variable +type ExportedConfigDependingVariables map[string]interface{} + +func (m ExportedConfigDependingVariables) set(k string, v interface{}) { m[k] = v } @@ -67,14 +157,15 @@ type bazelConstant struct { sortLast bool } -type exportedStringVariables map[string]string +// ExportedStringVariables is a mapping of variable names to string values +type ExportedStringVariables map[string]string -func (m exportedStringVariables) Set(k string, v string) { +func (m ExportedStringVariables) set(k string, v string) { m[k] = v } -func (m exportedStringVariables) asBazel(config android.Config, - stringVars exportedStringVariables, stringListVars exportedStringListVariables, cfgDepVars exportedConfigDependingVariables) []bazelConstant { +func (m ExportedStringVariables) asBazel(config Config, + stringVars ExportedStringVariables, stringListVars ExportedStringListVariables, cfgDepVars ExportedConfigDependingVariables) []bazelConstant { ret := make([]bazelConstant, 0, len(m)) for k, variableValue := range m { expandedVar, err := expandVar(config, variableValue, stringVars, stringListVars, cfgDepVars) @@ -92,21 +183,16 @@ func (m exportedStringVariables) asBazel(config android.Config, return ret } -// Convenience function to declare a static variable and export it to Bazel's cc_toolchain. -func exportStringStaticVariable(name string, value string) { - pctx.StaticVariable(name, value) - exportedStringVars.Set(name, value) -} +// ExportedStringListVariables is a mapping of variable names to a list of strings +type ExportedStringListVariables map[string][]string -type exportedStringListVariables map[string][]string - -func (m exportedStringListVariables) Set(k string, v []string) { +func (m ExportedStringListVariables) set(k string, v []string) { m[k] = v } -func (m exportedStringListVariables) asBazel(config android.Config, - stringScope exportedStringVariables, stringListScope exportedStringListVariables, - exportedVars exportedConfigDependingVariables) []bazelConstant { +func (m ExportedStringListVariables) asBazel(config Config, + stringScope ExportedStringVariables, stringListScope ExportedStringListVariables, + exportedVars ExportedConfigDependingVariables) []bazelConstant { ret := make([]bazelConstant, 0, len(m)) // For each exported variable, recursively expand elements in the variableValue // list to ensure that interpolated variables are expanded according to their values @@ -130,37 +216,17 @@ func (m exportedStringListVariables) asBazel(config android.Config, return ret } -// Convenience function to declare a static "source path" variable and export it to Bazel's cc_toolchain. -func exportVariableConfigMethod(name string, method interface{}) blueprint.Variable { - exportedConfigDependingVars.Set(name, method) - return pctx.VariableConfigMethod(name, method) -} +// ExportedStringListDictVariables is a mapping from variable names to a +// dictionary which maps keys to lists of strings +type ExportedStringListDictVariables map[string]map[string][]string -// Convenience function to declare a static "source path" variable and export it to Bazel's cc_toolchain. -func exportSourcePathVariable(name string, value string) { - pctx.SourcePathVariable(name, value) - exportedStringVars.Set(name, value) -} - -// Convenience function to declare a static variable and export it to Bazel's cc_toolchain. -func exportStringListStaticVariable(name string, value []string) { - pctx.StaticVariable(name, strings.Join(value, " ")) - exportedStringListVars.Set(name, value) -} - -func ExportStringList(name string, value []string) { - exportedStringListVars.Set(name, value) -} - -type exportedStringListDictVariables map[string]map[string][]string - -func (m exportedStringListDictVariables) Set(k string, v map[string][]string) { +func (m ExportedStringListDictVariables) set(k string, v map[string][]string) { m[k] = v } // Since dictionaries are not supported in Ninja, we do not expand variables for dictionaries -func (m exportedStringListDictVariables) asBazel(_ android.Config, _ exportedStringVariables, - _ exportedStringListVariables, _ exportedConfigDependingVariables) []bazelConstant { +func (m ExportedStringListDictVariables) asBazel(_ Config, _ ExportedStringVariables, + _ ExportedStringListVariables, _ ExportedConfigDependingVariables) []bazelConstant { ret := make([]bazelConstant, 0, len(m)) for k, dict := range m { ret = append(ret, bazelConstant{ @@ -171,14 +237,23 @@ func (m exportedStringListDictVariables) asBazel(_ android.Config, _ exportedStr return ret } -type exportedVariableReferenceDictVariables map[string]map[string]string +// ExportedVariableReferenceDictVariables is a mapping from variable names to a +// dictionary which references previously defined variables. This is used to +// create a Starlark output such as: +// string_var1 = "string1 +// var_ref_dict_var1 = { +// "key1": string_var1 +// } +// This type of variable collection must be expanded last so that it recognizes +// previously defined variables. +type ExportedVariableReferenceDictVariables map[string]map[string]string -func (m exportedVariableReferenceDictVariables) Set(k string, v map[string]string) { +func (m ExportedVariableReferenceDictVariables) set(k string, v map[string]string) { m[k] = v } -func (m exportedVariableReferenceDictVariables) asBazel(_ android.Config, _ exportedStringVariables, - _ exportedStringListVariables, _ exportedConfigDependingVariables) []bazelConstant { +func (m ExportedVariableReferenceDictVariables) asBazel(_ Config, _ ExportedStringVariables, + _ ExportedStringListVariables, _ ExportedConfigDependingVariables) []bazelConstant { ret := make([]bazelConstant, 0, len(m)) for n, dict := range m { for k, v := range dict { @@ -201,24 +276,15 @@ func (m exportedVariableReferenceDictVariables) asBazel(_ android.Config, _ expo return ret } -// BazelCcToolchainVars generates bzl file content containing variables for -// Bazel's cc_toolchain configuration. -func BazelCcToolchainVars(config android.Config) string { - return bazelToolchainVars( +// BazelToolchainVars expands an ExportedVariables collection and returns a string +// of formatted Starlark variable definitions +func BazelToolchainVars(config Config, exportedVars ExportedVariables) string { + results := exportedVars.asBazel( config, - exportedStringListDictVars, - exportedStringListVars, - exportedStringVars, - exportedVariableReferenceDictVars) -} - -func bazelToolchainVars(config android.Config, vars ...bazelVarExporter) string { - ret := "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.\n\n" - - results := []bazelConstant{} - for _, v := range vars { - results = append(results, v.asBazel(config, exportedStringVars, exportedStringListVars, exportedConfigDependingVars)...) - } + exportedVars.exportedStringVars, + exportedVars.exportedStringListVars, + exportedVars.exportedConfigDependingVars, + ) sort.Slice(results, func(i, j int) bool { if results[i].sortLast != results[j].sortLast { @@ -237,6 +303,8 @@ func bazelToolchainVars(config android.Config, vars ...bazelVarExporter) string } // Build the exported constants struct. + ret := bazel.GeneratedBazelFileWarning + ret += "\n\n" ret += strings.Join(definitions, "\n\n") ret += "\n\n" ret += "constants = struct(\n" @@ -279,8 +347,8 @@ func variableReference(input string) (match, error) { // string slice than to handle a pass-by-referenced map, which would make it // quite complex to track depth-first interpolations. It's also unlikely the // interpolation stacks are deep (n > 1). -func expandVar(config android.Config, toExpand string, stringScope exportedStringVariables, - stringListScope exportedStringListVariables, exportedVars exportedConfigDependingVariables) ([]string, error) { +func expandVar(config Config, toExpand string, stringScope ExportedStringVariables, + stringListScope ExportedStringListVariables, exportedVars ExportedConfigDependingVariables) ([]string, error) { // Internal recursive function. var expandVarInternal func(string, map[string]bool) (string, error) @@ -346,7 +414,8 @@ func expandVar(config android.Config, toExpand string, stringScope exportedStrin return ret, nil } var ret []string - for _, v := range strings.Split(toExpand, " ") { + stringFields := splitStringKeepingQuotedSubstring(toExpand, ' ') + for _, v := range stringFields { val, err := expandVarInternal(v, map[string]bool{}) if err != nil { return ret, err @@ -357,6 +426,46 @@ func expandVar(config android.Config, toExpand string, stringScope exportedStrin return ret, nil } +// splitStringKeepingQuotedSubstring splits a string on a provided separator, +// but it will not split substrings inside unescaped double quotes. If the double +// quotes are escaped, then the returned string will only include the quote, and +// not the escape. +func splitStringKeepingQuotedSubstring(s string, delimiter byte) []string { + var ret []string + quote := byte('"') + + var substring []byte + quoted := false + escaped := false + + for i := range s { + if !quoted && s[i] == delimiter { + ret = append(ret, string(substring)) + substring = []byte{} + continue + } + + characterIsEscape := i < len(s)-1 && s[i] == '\\' && s[i+1] == quote + if characterIsEscape { + escaped = true + continue + } + + if s[i] == quote { + if !escaped { + quoted = !quoted + } + escaped = false + } + + substring = append(substring, s[i]) + } + + ret = append(ret, string(substring)) + + return ret +} + func validateVariableMethod(name string, methodValue reflect.Value) { methodType := methodValue.Type() if methodType.Kind() != reflect.Func { diff --git a/android/config_bp2build_test.go b/android/config_bp2build_test.go new file mode 100644 index 000000000..1a0ba7b93 --- /dev/null +++ b/android/config_bp2build_test.go @@ -0,0 +1,454 @@ +// Copyright 2021 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 android + +import ( + "android/soong/bazel" + "testing" +) + +func TestExpandVars(t *testing.T) { + android_arm64_config := TestConfig("out", nil, "", nil) + android_arm64_config.BuildOS = Android + android_arm64_config.BuildArch = Arm64 + + testCases := []struct { + description string + config Config + stringScope ExportedStringVariables + stringListScope ExportedStringListVariables + configVars ExportedConfigDependingVariables + toExpand string + expectedValues []string + }{ + { + description: "no expansion for non-interpolated value", + toExpand: "foo", + expectedValues: []string{"foo"}, + }, + { + description: "single level expansion for string var", + stringScope: ExportedStringVariables{ + "foo": "bar", + }, + toExpand: "${foo}", + expectedValues: []string{"bar"}, + }, + { + description: "single level expansion with short-name for string var", + stringScope: ExportedStringVariables{ + "foo": "bar", + }, + toExpand: "${config.foo}", + expectedValues: []string{"bar"}, + }, + { + description: "single level expansion string list var", + stringListScope: ExportedStringListVariables{ + "foo": []string{"bar"}, + }, + toExpand: "${foo}", + expectedValues: []string{"bar"}, + }, + { + description: "mixed level expansion for string list var", + stringScope: ExportedStringVariables{ + "foo": "${bar}", + "qux": "hello", + }, + stringListScope: ExportedStringListVariables{ + "bar": []string{"baz", "${qux}"}, + }, + toExpand: "${foo}", + expectedValues: []string{"baz hello"}, + }, + { + description: "double level expansion", + stringListScope: ExportedStringListVariables{ + "foo": []string{"${bar}"}, + "bar": []string{"baz"}, + }, + toExpand: "${foo}", + expectedValues: []string{"baz"}, + }, + { + description: "double level expansion with a literal", + stringListScope: ExportedStringListVariables{ + "a": []string{"${b}", "c"}, + "b": []string{"d"}, + }, + toExpand: "${a}", + expectedValues: []string{"d c"}, + }, + { + description: "double level expansion, with two variables in a string", + stringListScope: ExportedStringListVariables{ + "a": []string{"${b} ${c}"}, + "b": []string{"d"}, + "c": []string{"e"}, + }, + toExpand: "${a}", + expectedValues: []string{"d e"}, + }, + { + description: "triple level expansion with two variables in a string", + stringListScope: ExportedStringListVariables{ + "a": []string{"${b} ${c}"}, + "b": []string{"${c}", "${d}"}, + "c": []string{"${d}"}, + "d": []string{"foo"}, + }, + toExpand: "${a}", + expectedValues: []string{"foo foo foo"}, + }, + { + description: "expansion with config depending vars", + configVars: ExportedConfigDependingVariables{ + "a": func(c Config) string { return c.BuildOS.String() }, + "b": func(c Config) string { return c.BuildArch.String() }, + }, + config: android_arm64_config, + toExpand: "${a}-${b}", + expectedValues: []string{"android-arm64"}, + }, + { + description: "double level multi type expansion", + stringListScope: ExportedStringListVariables{ + "platform": []string{"${os}-${arch}"}, + "const": []string{"const"}, + }, + configVars: ExportedConfigDependingVariables{ + "os": func(c Config) string { return c.BuildOS.String() }, + "arch": func(c Config) string { return c.BuildArch.String() }, + "foo": func(c Config) string { return "foo" }, + }, + config: android_arm64_config, + toExpand: "${const}/${platform}/${foo}", + expectedValues: []string{"const/android-arm64/foo"}, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.description, func(t *testing.T) { + output, _ := expandVar(testCase.config, testCase.toExpand, testCase.stringScope, testCase.stringListScope, testCase.configVars) + if len(output) != len(testCase.expectedValues) { + t.Errorf("Expected %d values, got %d", len(testCase.expectedValues), len(output)) + } + for i, actual := range output { + expectedValue := testCase.expectedValues[i] + if actual != expectedValue { + t.Errorf("Actual value '%s' doesn't match expected value '%s'", actual, expectedValue) + } + } + }) + } +} + +func TestBazelToolchainVars(t *testing.T) { + testCases := []struct { + name string + config Config + vars ExportedVariables + expectedOut string + }{ + { + name: "exports strings", + vars: ExportedVariables{ + exportedStringVars: ExportedStringVariables{ + "a": "b", + "c": "d", + }, + }, + expectedOut: bazel.GeneratedBazelFileWarning + ` + +_a = "b" + +_c = "d" + +constants = struct( + a = _a, + c = _c, +)`, + }, + { + name: "exports string lists", + vars: ExportedVariables{ + exportedStringListVars: ExportedStringListVariables{ + "a": []string{"b1", "b2"}, + "c": []string{"d1", "d2"}, + }, + }, + expectedOut: bazel.GeneratedBazelFileWarning + ` + +_a = [ + "b1", + "b2", +] + +_c = [ + "d1", + "d2", +] + +constants = struct( + a = _a, + c = _c, +)`, + }, + { + name: "exports string lists dicts", + vars: ExportedVariables{ + exportedStringListDictVars: ExportedStringListDictVariables{ + "a": map[string][]string{"b1": {"b2"}}, + "c": map[string][]string{"d1": {"d2"}}, + }, + }, + expectedOut: bazel.GeneratedBazelFileWarning + ` + +_a = { + "b1": ["b2"], +} + +_c = { + "d1": ["d2"], +} + +constants = struct( + a = _a, + c = _c, +)`, + }, + { + name: "exports dict with var refs", + vars: ExportedVariables{ + exportedVariableReferenceDictVars: ExportedVariableReferenceDictVariables{ + "a": map[string]string{"b1": "${b2}"}, + "c": map[string]string{"d1": "${config.d2}"}, + }, + }, + expectedOut: bazel.GeneratedBazelFileWarning + ` + +_a = { + "b1": _b2, +} + +_c = { + "d1": _d2, +} + +constants = struct( + a = _a, + c = _c, +)`, + }, + { + name: "sorts across types with variable references last", + vars: ExportedVariables{ + exportedStringVars: ExportedStringVariables{ + "b": "b-val", + "d": "d-val", + }, + exportedStringListVars: ExportedStringListVariables{ + "c": []string{"c-val"}, + "e": []string{"e-val"}, + }, + exportedStringListDictVars: ExportedStringListDictVariables{ + "a": map[string][]string{"a1": {"a2"}}, + "f": map[string][]string{"f1": {"f2"}}, + }, + exportedVariableReferenceDictVars: ExportedVariableReferenceDictVariables{ + "aa": map[string]string{"b1": "${b}"}, + "cc": map[string]string{"d1": "${config.d}"}, + }, + }, + expectedOut: bazel.GeneratedBazelFileWarning + ` + +_a = { + "a1": ["a2"], +} + +_b = "b-val" + +_c = ["c-val"] + +_d = "d-val" + +_e = ["e-val"] + +_f = { + "f1": ["f2"], +} + +_aa = { + "b1": _b, +} + +_cc = { + "d1": _d, +} + +constants = struct( + a = _a, + b = _b, + c = _c, + d = _d, + e = _e, + f = _f, + aa = _aa, + cc = _cc, +)`, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + out := BazelToolchainVars(tc.config, tc.vars) + if out != tc.expectedOut { + t.Errorf("Expected \n%s, got \n%s", tc.expectedOut, out) + } + }) + } +} + +func TestSplitStringKeepingQuotedSubstring(t *testing.T) { + testCases := []struct { + description string + s string + delimiter byte + split []string + }{ + { + description: "empty string returns single empty string", + s: "", + delimiter: ' ', + split: []string{ + "", + }, + }, + { + description: "string with single space returns two empty strings", + s: " ", + delimiter: ' ', + split: []string{ + "", + "", + }, + }, + { + description: "string with two spaces returns three empty strings", + s: " ", + delimiter: ' ', + split: []string{ + "", + "", + "", + }, + }, + { + description: "string with four words returns four word string", + s: "hello world with words", + delimiter: ' ', + split: []string{ + "hello", + "world", + "with", + "words", + }, + }, + { + description: "string with words and nested quote returns word strings and quote string", + s: `hello "world with" words`, + delimiter: ' ', + split: []string{ + "hello", + `"world with"`, + "words", + }, + }, + { + description: "string with escaped quote inside real quotes", + s: `hello \"world "with\" words"`, + delimiter: ' ', + split: []string{ + "hello", + `"world`, + `"with" words"`, + }, + }, + { + description: "string with words and escaped quotes returns word strings", + s: `hello \"world with\" words`, + delimiter: ' ', + split: []string{ + "hello", + `"world`, + `with"`, + "words", + }, + }, + { + description: "string which is single quoted substring returns only substring", + s: `"hello world with words"`, + delimiter: ' ', + split: []string{ + `"hello world with words"`, + }, + }, + { + description: "string starting with quote returns quoted string", + s: `"hello world with" words`, + delimiter: ' ', + split: []string{ + `"hello world with"`, + "words", + }, + }, + { + description: "string with starting quote and no ending quote returns quote to end of string", + s: `hello "world with words`, + delimiter: ' ', + split: []string{ + "hello", + `"world with words`, + }, + }, + { + description: "quoted string is treated as a single \"word\" unless separated by delimiter", + s: `hello "world"with words`, + delimiter: ' ', + split: []string{ + "hello", + `"world"with`, + "words", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.description, func(t *testing.T) { + split := splitStringKeepingQuotedSubstring(tc.s, tc.delimiter) + if len(split) != len(tc.split) { + t.Fatalf("number of split string elements (%d) differs from expected (%d): split string (%v), expected (%v)", + len(split), len(tc.split), split, tc.split, + ) + } + for i := range split { + if split[i] != tc.split[i] { + t.Errorf("split string element (%d), %v, differs from expected, %v", i, split[i], tc.split[i]) + } + } + }) + } +} diff --git a/bazel/constants.go b/bazel/constants.go index 6beb496a5..b10f256f0 100644 --- a/bazel/constants.go +++ b/bazel/constants.go @@ -21,7 +21,7 @@ const ( SoongInjectionDirName = "soong_injection" - GeneratedBazelFileWarning = "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT" + GeneratedBazelFileWarning = "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT." ) // String returns the name of the run. diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go index 1d3b10550..a96a3fc7b 100644 --- a/bp2build/build_conversion.go +++ b/bp2build/build_conversion.go @@ -580,7 +580,9 @@ func prettyPrint(propertyValue reflect.Value, indent int, emitZeroValues bool) ( elements = append(elements, val) } } - return starlark_fmt.PrintList(elements, indent, "%s"), nil + return starlark_fmt.PrintList(elements, indent, func(s string) string { + return "%s" + }), nil case reflect.Struct: // Special cases where the bp2build sends additional information to the codegenerator diff --git a/bp2build/conversion.go b/bp2build/conversion.go index 91e614d23..1790dd7b8 100644 --- a/bp2build/conversion.go +++ b/bp2build/conversion.go @@ -7,7 +7,8 @@ import ( "strings" "android/soong/android" - "android/soong/cc/config" + cc_config "android/soong/cc/config" + java_config "android/soong/java/config" "github.com/google/blueprint/proptools" ) @@ -22,7 +23,10 @@ func CreateSoongInjectionFiles(cfg android.Config, metrics CodegenMetrics) []Baz var files []BazelFile files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package. - files = append(files, newFile("cc_toolchain", "constants.bzl", config.BazelCcToolchainVars(cfg))) + files = append(files, newFile("cc_toolchain", "constants.bzl", cc_config.BazelCcToolchainVars(cfg))) + + files = append(files, newFile("java_toolchain", GeneratedBuildFileName, "")) // Creates a //java_toolchain package. + files = append(files, newFile("java_toolchain", "constants.bzl", java_config.BazelJavaToolchainVars(cfg))) files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.convertedModules, "\n"))) diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go index d65ece8c7..e49d8553b 100644 --- a/bp2build/conversion_test.go +++ b/bp2build/conversion_test.go @@ -94,6 +94,14 @@ func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) { dir: "cc_toolchain", basename: "constants.bzl", }, + { + dir: "java_toolchain", + basename: GeneratedBuildFileName, + }, + { + dir: "java_toolchain", + basename: "constants.bzl", + }, { dir: "metrics", basename: "converted_modules.txt", diff --git a/cc/config/Android.bp b/cc/config/Android.bp index e1b06057b..1a21c1361 100644 --- a/cc/config/Android.bp +++ b/cc/config/Android.bp @@ -11,7 +11,6 @@ bootstrap_go_package { "soong-starlark-format", ], srcs: [ - "bp2build.go", "clang.go", "global.go", "tidy.go", @@ -33,7 +32,6 @@ bootstrap_go_package { "arm64_linux_host.go", ], testSrcs: [ - "bp2build_test.go", "tidy_test.go", ], } diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go index 4d0ae1a12..dfe143f95 100644 --- a/cc/config/arm64_device.go +++ b/cc/config/arm64_device.go @@ -98,28 +98,28 @@ func init() { pctx.SourcePathVariable("Arm64GccRoot", "prebuilts/gcc/${HostPrebuiltTag}/aarch64/aarch64-linux-android-${arm64GccVersion}") - exportStringListStaticVariable("Arm64Ldflags", arm64Ldflags) - exportStringListStaticVariable("Arm64Lldflags", arm64Lldflags) + exportedVars.ExportStringListStaticVariable("Arm64Ldflags", arm64Ldflags) + exportedVars.ExportStringListStaticVariable("Arm64Lldflags", arm64Lldflags) - exportStringListStaticVariable("Arm64Cflags", arm64Cflags) - exportStringListStaticVariable("Arm64Cppflags", arm64Cppflags) + exportedVars.ExportStringListStaticVariable("Arm64Cflags", arm64Cflags) + exportedVars.ExportStringListStaticVariable("Arm64Cppflags", arm64Cppflags) - exportedVariableReferenceDictVars.Set("Arm64ArchVariantCflags", arm64ArchVariantCflagsVar) - exportedVariableReferenceDictVars.Set("Arm64CpuVariantCflags", arm64CpuVariantCflagsVar) - exportedVariableReferenceDictVars.Set("Arm64CpuVariantLdflags", arm64CpuVariantLdflags) + exportedVars.ExportVariableReferenceDict("Arm64ArchVariantCflags", arm64ArchVariantCflagsVar) + exportedVars.ExportVariableReferenceDict("Arm64CpuVariantCflags", arm64CpuVariantCflagsVar) + exportedVars.ExportVariableReferenceDict("Arm64CpuVariantLdflags", arm64CpuVariantLdflags) - exportStringListStaticVariable("Arm64Armv8ACflags", arm64ArchVariantCflags["armv8-a"]) - exportStringListStaticVariable("Arm64Armv8ABranchProtCflags", arm64ArchVariantCflags["armv8-a-branchprot"]) - exportStringListStaticVariable("Arm64Armv82ACflags", arm64ArchVariantCflags["armv8-2a"]) - exportStringListStaticVariable("Arm64Armv82ADotprodCflags", arm64ArchVariantCflags["armv8-2a-dotprod"]) + exportedVars.ExportStringListStaticVariable("Arm64Armv8ACflags", arm64ArchVariantCflags["armv8-a"]) + exportedVars.ExportStringListStaticVariable("Arm64Armv8ABranchProtCflags", arm64ArchVariantCflags["armv8-a-branchprot"]) + exportedVars.ExportStringListStaticVariable("Arm64Armv82ACflags", arm64ArchVariantCflags["armv8-2a"]) + exportedVars.ExportStringListStaticVariable("Arm64Armv82ADotprodCflags", arm64ArchVariantCflags["armv8-2a-dotprod"]) - exportStringListStaticVariable("Arm64CortexA53Cflags", arm64CpuVariantCflags["cortex-a53"]) - exportStringListStaticVariable("Arm64CortexA55Cflags", arm64CpuVariantCflags["cortex-a55"]) - exportStringListStaticVariable("Arm64KryoCflags", arm64CpuVariantCflags["kryo"]) - exportStringListStaticVariable("Arm64ExynosM1Cflags", arm64CpuVariantCflags["exynos-m1"]) - exportStringListStaticVariable("Arm64ExynosM2Cflags", arm64CpuVariantCflags["exynos-m2"]) + exportedVars.ExportStringListStaticVariable("Arm64CortexA53Cflags", arm64CpuVariantCflags["cortex-a53"]) + exportedVars.ExportStringListStaticVariable("Arm64CortexA55Cflags", arm64CpuVariantCflags["cortex-a55"]) + exportedVars.ExportStringListStaticVariable("Arm64KryoCflags", arm64CpuVariantCflags["kryo"]) + exportedVars.ExportStringListStaticVariable("Arm64ExynosM1Cflags", arm64CpuVariantCflags["exynos-m1"]) + exportedVars.ExportStringListStaticVariable("Arm64ExynosM2Cflags", arm64CpuVariantCflags["exynos-m2"]) - exportStringListStaticVariable("Arm64FixCortexA53Ldflags", []string{"-Wl,--fix-cortex-a53-843419"}) + exportedVars.ExportStringListStaticVariable("Arm64FixCortexA53Ldflags", []string{"-Wl,--fix-cortex-a53-843419"}) } var ( diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go index 4466632ea..d702c6145 100644 --- a/cc/config/arm_device.go +++ b/cc/config/arm_device.go @@ -178,41 +178,41 @@ func init() { pctx.SourcePathVariable("ArmGccRoot", "prebuilts/gcc/${HostPrebuiltTag}/arm/arm-linux-androideabi-${armGccVersion}") // Just exported. Not created as a Ninja static variable. - exportedStringVars.Set("ArmClangTriple", clangTriple) + exportedVars.ExportString("ArmClangTriple", clangTriple) - exportStringListStaticVariable("ArmLdflags", armLdflags) - exportStringListStaticVariable("ArmLldflags", armLldflags) + exportedVars.ExportStringListStaticVariable("ArmLdflags", armLdflags) + exportedVars.ExportStringListStaticVariable("ArmLldflags", armLldflags) - exportStringListStaticVariable("ArmFixCortexA8LdFlags", armFixCortexA8LdFlags) - exportStringListStaticVariable("ArmNoFixCortexA8LdFlags", armNoFixCortexA8LdFlags) + exportedVars.ExportStringListStaticVariable("ArmFixCortexA8LdFlags", armFixCortexA8LdFlags) + exportedVars.ExportStringListStaticVariable("ArmNoFixCortexA8LdFlags", armNoFixCortexA8LdFlags) // Clang cflags - exportStringListStaticVariable("ArmToolchainCflags", armToolchainCflags) - exportStringListStaticVariable("ArmCflags", armCflags) - exportStringListStaticVariable("ArmCppflags", armCppflags) + exportedVars.ExportStringListStaticVariable("ArmToolchainCflags", armToolchainCflags) + exportedVars.ExportStringListStaticVariable("ArmCflags", armCflags) + exportedVars.ExportStringListStaticVariable("ArmCppflags", armCppflags) // Clang ARM vs. Thumb instruction set cflags - exportStringListStaticVariable("ArmArmCflags", armArmCflags) - exportStringListStaticVariable("ArmThumbCflags", armThumbCflags) + exportedVars.ExportStringListStaticVariable("ArmArmCflags", armArmCflags) + exportedVars.ExportStringListStaticVariable("ArmThumbCflags", armThumbCflags) - exportedVariableReferenceDictVars.Set("ArmArchVariantCflags", armArchVariantCflagsVar) - exportedVariableReferenceDictVars.Set("ArmCpuVariantCflags", armCpuVariantCflagsVar) + exportedVars.ExportVariableReferenceDict("ArmArchVariantCflags", armArchVariantCflagsVar) + exportedVars.ExportVariableReferenceDict("ArmCpuVariantCflags", armCpuVariantCflagsVar) // Clang arch variant cflags - exportStringListStaticVariable("ArmArmv7ACflags", armArchVariantCflags["armv7-a"]) - exportStringListStaticVariable("ArmArmv7ANeonCflags", armArchVariantCflags["armv7-a-neon"]) - exportStringListStaticVariable("ArmArmv8ACflags", armArchVariantCflags["armv8-a"]) - exportStringListStaticVariable("ArmArmv82ACflags", armArchVariantCflags["armv8-2a"]) + exportedVars.ExportStringListStaticVariable("ArmArmv7ACflags", armArchVariantCflags["armv7-a"]) + exportedVars.ExportStringListStaticVariable("ArmArmv7ANeonCflags", armArchVariantCflags["armv7-a-neon"]) + exportedVars.ExportStringListStaticVariable("ArmArmv8ACflags", armArchVariantCflags["armv8-a"]) + exportedVars.ExportStringListStaticVariable("ArmArmv82ACflags", armArchVariantCflags["armv8-2a"]) // Clang cpu variant cflags - exportStringListStaticVariable("ArmGenericCflags", armCpuVariantCflags[""]) - exportStringListStaticVariable("ArmCortexA7Cflags", armCpuVariantCflags["cortex-a7"]) - exportStringListStaticVariable("ArmCortexA8Cflags", armCpuVariantCflags["cortex-a8"]) - exportStringListStaticVariable("ArmCortexA15Cflags", armCpuVariantCflags["cortex-a15"]) - exportStringListStaticVariable("ArmCortexA53Cflags", armCpuVariantCflags["cortex-a53"]) - exportStringListStaticVariable("ArmCortexA55Cflags", armCpuVariantCflags["cortex-a55"]) - exportStringListStaticVariable("ArmKraitCflags", armCpuVariantCflags["krait"]) - exportStringListStaticVariable("ArmKryoCflags", armCpuVariantCflags["kryo"]) + exportedVars.ExportStringListStaticVariable("ArmGenericCflags", armCpuVariantCflags[""]) + exportedVars.ExportStringListStaticVariable("ArmCortexA7Cflags", armCpuVariantCflags["cortex-a7"]) + exportedVars.ExportStringListStaticVariable("ArmCortexA8Cflags", armCpuVariantCflags["cortex-a8"]) + exportedVars.ExportStringListStaticVariable("ArmCortexA15Cflags", armCpuVariantCflags["cortex-a15"]) + exportedVars.ExportStringListStaticVariable("ArmCortexA53Cflags", armCpuVariantCflags["cortex-a53"]) + exportedVars.ExportStringListStaticVariable("ArmCortexA55Cflags", armCpuVariantCflags["cortex-a55"]) + exportedVars.ExportStringListStaticVariable("ArmKraitCflags", armCpuVariantCflags["krait"]) + exportedVars.ExportStringListStaticVariable("ArmKryoCflags", armCpuVariantCflags["kryo"]) } var ( diff --git a/cc/config/bp2build_test.go b/cc/config/bp2build_test.go deleted file mode 100644 index 9a8178af6..000000000 --- a/cc/config/bp2build_test.go +++ /dev/null @@ -1,324 +0,0 @@ -// Copyright 2021 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 config - -import ( - "testing" - - "android/soong/android" -) - -func TestExpandVars(t *testing.T) { - android_arm64_config := android.TestConfig("out", nil, "", nil) - android_arm64_config.BuildOS = android.Android - android_arm64_config.BuildArch = android.Arm64 - - testCases := []struct { - description string - config android.Config - stringScope exportedStringVariables - stringListScope exportedStringListVariables - configVars exportedConfigDependingVariables - toExpand string - expectedValues []string - }{ - { - description: "no expansion for non-interpolated value", - toExpand: "foo", - expectedValues: []string{"foo"}, - }, - { - description: "single level expansion for string var", - stringScope: exportedStringVariables{ - "foo": "bar", - }, - toExpand: "${foo}", - expectedValues: []string{"bar"}, - }, - { - description: "single level expansion with short-name for string var", - stringScope: exportedStringVariables{ - "foo": "bar", - }, - toExpand: "${config.foo}", - expectedValues: []string{"bar"}, - }, - { - description: "single level expansion string list var", - stringListScope: exportedStringListVariables{ - "foo": []string{"bar"}, - }, - toExpand: "${foo}", - expectedValues: []string{"bar"}, - }, - { - description: "mixed level expansion for string list var", - stringScope: exportedStringVariables{ - "foo": "${bar}", - "qux": "hello", - }, - stringListScope: exportedStringListVariables{ - "bar": []string{"baz", "${qux}"}, - }, - toExpand: "${foo}", - expectedValues: []string{"baz hello"}, - }, - { - description: "double level expansion", - stringListScope: exportedStringListVariables{ - "foo": []string{"${bar}"}, - "bar": []string{"baz"}, - }, - toExpand: "${foo}", - expectedValues: []string{"baz"}, - }, - { - description: "double level expansion with a literal", - stringListScope: exportedStringListVariables{ - "a": []string{"${b}", "c"}, - "b": []string{"d"}, - }, - toExpand: "${a}", - expectedValues: []string{"d c"}, - }, - { - description: "double level expansion, with two variables in a string", - stringListScope: exportedStringListVariables{ - "a": []string{"${b} ${c}"}, - "b": []string{"d"}, - "c": []string{"e"}, - }, - toExpand: "${a}", - expectedValues: []string{"d e"}, - }, - { - description: "triple level expansion with two variables in a string", - stringListScope: exportedStringListVariables{ - "a": []string{"${b} ${c}"}, - "b": []string{"${c}", "${d}"}, - "c": []string{"${d}"}, - "d": []string{"foo"}, - }, - toExpand: "${a}", - expectedValues: []string{"foo foo foo"}, - }, - { - description: "expansion with config depending vars", - configVars: exportedConfigDependingVariables{ - "a": func(c android.Config) string { return c.BuildOS.String() }, - "b": func(c android.Config) string { return c.BuildArch.String() }, - }, - config: android_arm64_config, - toExpand: "${a}-${b}", - expectedValues: []string{"android-arm64"}, - }, - { - description: "double level multi type expansion", - stringListScope: exportedStringListVariables{ - "platform": []string{"${os}-${arch}"}, - "const": []string{"const"}, - }, - configVars: exportedConfigDependingVariables{ - "os": func(c android.Config) string { return c.BuildOS.String() }, - "arch": func(c android.Config) string { return c.BuildArch.String() }, - "foo": func(c android.Config) string { return "foo" }, - }, - config: android_arm64_config, - toExpand: "${const}/${platform}/${foo}", - expectedValues: []string{"const/android-arm64/foo"}, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.description, func(t *testing.T) { - output, _ := expandVar(testCase.config, testCase.toExpand, testCase.stringScope, testCase.stringListScope, testCase.configVars) - if len(output) != len(testCase.expectedValues) { - t.Errorf("Expected %d values, got %d", len(testCase.expectedValues), len(output)) - } - for i, actual := range output { - expectedValue := testCase.expectedValues[i] - if actual != expectedValue { - t.Errorf("Actual value '%s' doesn't match expected value '%s'", actual, expectedValue) - } - } - }) - } -} - -func TestBazelToolchainVars(t *testing.T) { - testCases := []struct { - name string - config android.Config - vars []bazelVarExporter - expectedOut string - }{ - { - name: "exports strings", - vars: []bazelVarExporter{ - exportedStringVariables{ - "a": "b", - "c": "d", - }, - }, - expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT. - -_a = "b" - -_c = "d" - -constants = struct( - a = _a, - c = _c, -)`, - }, - { - name: "exports string lists", - vars: []bazelVarExporter{ - exportedStringListVariables{ - "a": []string{"b1", "b2"}, - "c": []string{"d1", "d2"}, - }, - }, - expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT. - -_a = [ - "b1", - "b2", -] - -_c = [ - "d1", - "d2", -] - -constants = struct( - a = _a, - c = _c, -)`, - }, - { - name: "exports string lists dicts", - vars: []bazelVarExporter{ - exportedStringListDictVariables{ - "a": map[string][]string{"b1": []string{"b2"}}, - "c": map[string][]string{"d1": []string{"d2"}}, - }, - }, - expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT. - -_a = { - "b1": ["b2"], -} - -_c = { - "d1": ["d2"], -} - -constants = struct( - a = _a, - c = _c, -)`, - }, - { - name: "exports dict with var refs", - vars: []bazelVarExporter{ - exportedVariableReferenceDictVariables{ - "a": map[string]string{"b1": "${b2}"}, - "c": map[string]string{"d1": "${config.d2}"}, - }, - }, - expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT. - -_a = { - "b1": _b2, -} - -_c = { - "d1": _d2, -} - -constants = struct( - a = _a, - c = _c, -)`, - }, - { - name: "sorts across types with variable references last", - vars: []bazelVarExporter{ - exportedStringVariables{ - "b": "b-val", - "d": "d-val", - }, - exportedStringListVariables{ - "c": []string{"c-val"}, - "e": []string{"e-val"}, - }, - exportedStringListDictVariables{ - "a": map[string][]string{"a1": []string{"a2"}}, - "f": map[string][]string{"f1": []string{"f2"}}, - }, - exportedVariableReferenceDictVariables{ - "aa": map[string]string{"b1": "${b}"}, - "cc": map[string]string{"d1": "${config.d}"}, - }, - }, - expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT. - -_a = { - "a1": ["a2"], -} - -_b = "b-val" - -_c = ["c-val"] - -_d = "d-val" - -_e = ["e-val"] - -_f = { - "f1": ["f2"], -} - -_aa = { - "b1": _b, -} - -_cc = { - "d1": _d, -} - -constants = struct( - a = _a, - b = _b, - c = _c, - d = _d, - e = _e, - f = _f, - aa = _aa, - cc = _cc, -)`, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - out := bazelToolchainVars(tc.config, tc.vars...) - if out != tc.expectedOut { - t.Errorf("Expected \n%s, got \n%s", tc.expectedOut, out) - } - }) - } -} diff --git a/cc/config/global.go b/cc/config/global.go index dfb9a66ea..9c7168369 100644 --- a/cc/config/global.go +++ b/cc/config/global.go @@ -23,6 +23,9 @@ import ( ) var ( + pctx = android.NewPackageContext("android/soong/cc/config") + exportedVars = android.NewExportedVariables(pctx) + // Flags used by lots of devices. Putting them in package static variables // will save bytes in build.ninja so they aren't repeated for every file commonGlobalCflags = []string{ @@ -296,20 +299,28 @@ var ( WarningAllowedOldProjects = []string{} ) -var pctx = android.NewPackageContext("android/soong/cc/config") +// BazelCcToolchainVars generates bzl file content containing variables for +// Bazel's cc_toolchain configuration. +func BazelCcToolchainVars(config android.Config) string { + return android.BazelToolchainVars(config, exportedVars) +} + +func ExportStringList(name string, value []string) { + exportedVars.ExportStringList(name, value) +} func init() { if runtime.GOOS == "linux" { commonGlobalCflags = append(commonGlobalCflags, "-fdebug-prefix-map=/proc/self/cwd=") } - exportStringListStaticVariable("CommonGlobalConlyflags", commonGlobalConlyflags) - exportStringListStaticVariable("DeviceGlobalCppflags", deviceGlobalCppflags) - exportStringListStaticVariable("DeviceGlobalLdflags", deviceGlobalLdflags) - exportStringListStaticVariable("DeviceGlobalLldflags", deviceGlobalLldflags) - exportStringListStaticVariable("HostGlobalCppflags", hostGlobalCppflags) - exportStringListStaticVariable("HostGlobalLdflags", hostGlobalLdflags) - exportStringListStaticVariable("HostGlobalLldflags", hostGlobalLldflags) + exportedVars.ExportStringListStaticVariable("CommonGlobalConlyflags", commonGlobalConlyflags) + exportedVars.ExportStringListStaticVariable("DeviceGlobalCppflags", deviceGlobalCppflags) + exportedVars.ExportStringListStaticVariable("DeviceGlobalLdflags", deviceGlobalLdflags) + exportedVars.ExportStringListStaticVariable("DeviceGlobalLldflags", deviceGlobalLldflags) + exportedVars.ExportStringListStaticVariable("HostGlobalCppflags", hostGlobalCppflags) + exportedVars.ExportStringListStaticVariable("HostGlobalLdflags", hostGlobalLdflags) + exportedVars.ExportStringListStaticVariable("HostGlobalLldflags", hostGlobalLldflags) // Export the static default CommonGlobalCflags to Bazel. // TODO(187086342): handle cflags that are set in VariableFuncs. @@ -320,7 +331,7 @@ func init() { "-ftrivial-auto-var-init=zero", "-enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang", }...) - exportedStringListVars.Set("CommonGlobalCflags", bazelCommonGlobalCflags) + exportedVars.ExportStringList("CommonGlobalCflags", bazelCommonGlobalCflags) pctx.VariableFunc("CommonGlobalCflags", func(ctx android.PackageVarContext) string { flags := commonGlobalCflags @@ -349,17 +360,17 @@ func init() { // Export the static default DeviceGlobalCflags to Bazel. // TODO(187086342): handle cflags that are set in VariableFuncs. - exportedStringListVars.Set("DeviceGlobalCflags", deviceGlobalCflags) + exportedVars.ExportStringList("DeviceGlobalCflags", deviceGlobalCflags) pctx.VariableFunc("DeviceGlobalCflags", func(ctx android.PackageVarContext) string { return strings.Join(deviceGlobalCflags, " ") }) - exportStringListStaticVariable("HostGlobalCflags", hostGlobalCflags) - exportStringListStaticVariable("NoOverrideGlobalCflags", noOverrideGlobalCflags) - exportStringListStaticVariable("NoOverrideExternalGlobalCflags", noOverrideExternalGlobalCflags) - exportStringListStaticVariable("CommonGlobalCppflags", commonGlobalCppflags) - exportStringListStaticVariable("ExternalCflags", extraExternalCflags) + exportedVars.ExportStringListStaticVariable("HostGlobalCflags", hostGlobalCflags) + exportedVars.ExportStringListStaticVariable("NoOverrideGlobalCflags", noOverrideGlobalCflags) + exportedVars.ExportStringListStaticVariable("NoOverrideExternalGlobalCflags", noOverrideExternalGlobalCflags) + exportedVars.ExportStringListStaticVariable("CommonGlobalCppflags", commonGlobalCppflags) + exportedVars.ExportStringListStaticVariable("ExternalCflags", extraExternalCflags) // Everything in these lists is a crime against abstraction and dependency tracking. // Do not add anything to this list. @@ -374,11 +385,11 @@ func init() { "frameworks/native/opengl/include", "frameworks/av/include", } - exportedStringListVars.Set("CommonGlobalIncludes", commonGlobalIncludes) + exportedVars.ExportStringList("CommonGlobalIncludes", commonGlobalIncludes) pctx.PrefixedExistentPathsForSourcesVariable("CommonGlobalIncludes", "-I", commonGlobalIncludes) - exportStringStaticVariable("CLANG_DEFAULT_VERSION", ClangDefaultVersion) - exportStringStaticVariable("CLANG_DEFAULT_SHORT_VERSION", ClangDefaultShortVersion) + exportedVars.ExportStringStaticVariable("CLANG_DEFAULT_VERSION", ClangDefaultVersion) + exportedVars.ExportStringStaticVariable("CLANG_DEFAULT_SHORT_VERSION", ClangDefaultShortVersion) pctx.StaticVariableWithEnvOverride("ClangBase", "LLVM_PREBUILTS_BASE", ClangDefaultBase) pctx.StaticVariableWithEnvOverride("ClangVersion", "LLVM_PREBUILTS_VERSION", ClangDefaultVersion) @@ -418,7 +429,7 @@ func init() { pctx.StaticVariableWithEnvOverride("REAbiLinkerExecStrategy", "RBE_ABI_LINKER_EXEC_STRATEGY", remoteexec.LocalExecStrategy) } -var HostPrebuiltTag = exportVariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS) +var HostPrebuiltTag = exportedVars.ExportVariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS) func ClangPath(ctx android.PathContext, file string) android.SourcePath { type clangToolKey string diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go index 164e7a67f..aebda0b51 100644 --- a/cc/config/x86_64_device.go +++ b/cc/config/x86_64_device.go @@ -91,26 +91,26 @@ func init() { pctx.SourcePathVariable("X86_64GccRoot", "prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${x86_64GccVersion}") - exportStringListStaticVariable("X86_64ToolchainCflags", []string{"-m64"}) - exportStringListStaticVariable("X86_64ToolchainLdflags", []string{"-m64"}) + exportedVars.ExportStringListStaticVariable("X86_64ToolchainCflags", []string{"-m64"}) + exportedVars.ExportStringListStaticVariable("X86_64ToolchainLdflags", []string{"-m64"}) - exportStringListStaticVariable("X86_64Ldflags", x86_64Ldflags) - exportStringListStaticVariable("X86_64Lldflags", x86_64Ldflags) + exportedVars.ExportStringListStaticVariable("X86_64Ldflags", x86_64Ldflags) + exportedVars.ExportStringListStaticVariable("X86_64Lldflags", x86_64Ldflags) // Clang cflags - exportStringListStaticVariable("X86_64Cflags", x86_64Cflags) - exportStringListStaticVariable("X86_64Cppflags", x86_64Cppflags) + exportedVars.ExportStringListStaticVariable("X86_64Cflags", x86_64Cflags) + exportedVars.ExportStringListStaticVariable("X86_64Cppflags", x86_64Cppflags) // Yasm flags - exportStringListStaticVariable("X86_64YasmFlags", []string{ + exportedVars.ExportStringListStaticVariable("X86_64YasmFlags", []string{ "-f elf64", "-m amd64", }) // Extended cflags - exportedStringListDictVars.Set("X86_64ArchVariantCflags", x86_64ArchVariantCflags) - exportedStringListDictVars.Set("X86_64ArchFeatureCflags", x86_64ArchFeatureCflags) + exportedVars.ExportStringListDict("X86_64ArchVariantCflags", x86_64ArchVariantCflags) + exportedVars.ExportStringListDict("X86_64ArchFeatureCflags", x86_64ArchFeatureCflags) // Architecture variant cflags for variant, cflags := range x86_64ArchVariantCflags { diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go index e32e1bde7..421b08354 100644 --- a/cc/config/x86_device.go +++ b/cc/config/x86_device.go @@ -98,25 +98,25 @@ func init() { pctx.SourcePathVariable("X86GccRoot", "prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${x86GccVersion}") - exportStringListStaticVariable("X86ToolchainCflags", []string{"-m32"}) - exportStringListStaticVariable("X86ToolchainLdflags", []string{"-m32"}) + exportedVars.ExportStringListStaticVariable("X86ToolchainCflags", []string{"-m32"}) + exportedVars.ExportStringListStaticVariable("X86ToolchainLdflags", []string{"-m32"}) - exportStringListStaticVariable("X86Ldflags", x86Ldflags) - exportStringListStaticVariable("X86Lldflags", x86Ldflags) + exportedVars.ExportStringListStaticVariable("X86Ldflags", x86Ldflags) + exportedVars.ExportStringListStaticVariable("X86Lldflags", x86Ldflags) // Clang cflags - exportStringListStaticVariable("X86Cflags", x86Cflags) - exportStringListStaticVariable("X86Cppflags", x86Cppflags) + exportedVars.ExportStringListStaticVariable("X86Cflags", x86Cflags) + exportedVars.ExportStringListStaticVariable("X86Cppflags", x86Cppflags) // Yasm flags - exportStringListStaticVariable("X86YasmFlags", []string{ + exportedVars.ExportStringListStaticVariable("X86YasmFlags", []string{ "-f elf32", "-m x86", }) // Extended cflags - exportedStringListDictVars.Set("X86ArchVariantCflags", x86ArchVariantCflags) - exportedStringListDictVars.Set("X86ArchFeatureCflags", x86ArchFeatureCflags) + exportedVars.ExportStringListDict("X86ArchVariantCflags", x86ArchVariantCflags) + exportedVars.ExportStringListDict("X86ArchFeatureCflags", x86ArchFeatureCflags) // Architecture variant cflags for variant, cflags := range x86ArchVariantCflags { diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go index e1659d380..4e8fd7752 100644 --- a/cc/config/x86_linux_host.go +++ b/cc/config/x86_linux_host.go @@ -121,40 +121,40 @@ const ( ) func init() { - exportStringStaticVariable("LinuxGccVersion", linuxGccVersion) - exportStringStaticVariable("LinuxGlibcVersion", linuxGlibcVersion) + exportedVars.ExportStringStaticVariable("LinuxGccVersion", linuxGccVersion) + exportedVars.ExportStringStaticVariable("LinuxGlibcVersion", linuxGlibcVersion) // Most places use the full GCC version. A few only use up to the first two numbers. if p := strings.Split(linuxGccVersion, "."); len(p) > 2 { - exportStringStaticVariable("ShortLinuxGccVersion", strings.Join(p[:2], ".")) + exportedVars.ExportStringStaticVariable("ShortLinuxGccVersion", strings.Join(p[:2], ".")) } else { - exportStringStaticVariable("ShortLinuxGccVersion", linuxGccVersion) + exportedVars.ExportStringStaticVariable("ShortLinuxGccVersion", linuxGccVersion) } - exportSourcePathVariable("LinuxGccRoot", + exportedVars.ExportSourcePathVariable("LinuxGccRoot", "prebuilts/gcc/linux-x86/host/x86_64-linux-glibc${LinuxGlibcVersion}-${ShortLinuxGccVersion}") - exportStringListStaticVariable("LinuxGccTriple", []string{"x86_64-linux"}) + exportedVars.ExportStringListStaticVariable("LinuxGccTriple", []string{"x86_64-linux"}) - exportStringListStaticVariable("LinuxCflags", linuxCflags) - exportStringListStaticVariable("LinuxLdflags", linuxLdflags) - exportStringListStaticVariable("LinuxLldflags", linuxLdflags) - exportStringListStaticVariable("LinuxGlibcCflags", linuxGlibcCflags) - exportStringListStaticVariable("LinuxGlibcLdflags", linuxGlibcLdflags) - exportStringListStaticVariable("LinuxGlibcLldflags", linuxGlibcLdflags) - exportStringListStaticVariable("LinuxMuslCflags", linuxMuslCflags) - exportStringListStaticVariable("LinuxMuslLdflags", linuxMuslLdflags) - exportStringListStaticVariable("LinuxMuslLldflags", linuxMuslLdflags) + exportedVars.ExportStringListStaticVariable("LinuxCflags", linuxCflags) + exportedVars.ExportStringListStaticVariable("LinuxLdflags", linuxLdflags) + exportedVars.ExportStringListStaticVariable("LinuxLldflags", linuxLdflags) + exportedVars.ExportStringListStaticVariable("LinuxGlibcCflags", linuxGlibcCflags) + exportedVars.ExportStringListStaticVariable("LinuxGlibcLdflags", linuxGlibcLdflags) + exportedVars.ExportStringListStaticVariable("LinuxGlibcLldflags", linuxGlibcLdflags) + exportedVars.ExportStringListStaticVariable("LinuxMuslCflags", linuxMuslCflags) + exportedVars.ExportStringListStaticVariable("LinuxMuslLdflags", linuxMuslLdflags) + exportedVars.ExportStringListStaticVariable("LinuxMuslLldflags", linuxMuslLdflags) - exportStringListStaticVariable("LinuxX86Cflags", linuxX86Cflags) - exportStringListStaticVariable("LinuxX8664Cflags", linuxX8664Cflags) - exportStringListStaticVariable("LinuxX86Ldflags", linuxX86Ldflags) - exportStringListStaticVariable("LinuxX86Lldflags", linuxX86Ldflags) - exportStringListStaticVariable("LinuxX8664Ldflags", linuxX8664Ldflags) - exportStringListStaticVariable("LinuxX8664Lldflags", linuxX8664Ldflags) + exportedVars.ExportStringListStaticVariable("LinuxX86Cflags", linuxX86Cflags) + exportedVars.ExportStringListStaticVariable("LinuxX8664Cflags", linuxX8664Cflags) + exportedVars.ExportStringListStaticVariable("LinuxX86Ldflags", linuxX86Ldflags) + exportedVars.ExportStringListStaticVariable("LinuxX86Lldflags", linuxX86Ldflags) + exportedVars.ExportStringListStaticVariable("LinuxX8664Ldflags", linuxX8664Ldflags) + exportedVars.ExportStringListStaticVariable("LinuxX8664Lldflags", linuxX8664Ldflags) // Yasm flags - exportStringListStaticVariable("LinuxX86YasmFlags", []string{"-f elf32 -m x86"}) - exportStringListStaticVariable("LinuxX8664YasmFlags", []string{"-f elf64 -m amd64"}) + exportedVars.ExportStringListStaticVariable("LinuxX86YasmFlags", []string{"-f elf32 -m x86"}) + exportedVars.ExportStringListStaticVariable("LinuxX8664YasmFlags", []string{"-f elf64 -m amd64"}) } type toolchainLinux struct { diff --git a/java/config/config.go b/java/config/config.go index 262c53196..95b841fa7 100644 --- a/java/config/config.go +++ b/java/config/config.go @@ -26,7 +26,8 @@ import ( ) var ( - pctx = android.NewPackageContext("android/soong/java/config") + pctx = android.NewPackageContext("android/soong/java/config") + exportedVars = android.NewExportedVariables(pctx) LegacyCorePlatformBootclasspathLibraries = []string{"legacy.core.platform.api.stubs", "core-lambda-stubs"} LegacyCorePlatformSystemModules = "legacy-core-platform-api-stubs-system-modules" @@ -53,25 +54,40 @@ var ( } ) -const ( - JavaVmFlags = `-XX:OnError="cat hs_err_pid%p.log" -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads` - JavacVmFlags = `-J-XX:OnError="cat hs_err_pid%p.log" -J-XX:CICompilerCount=6 -J-XX:+UseDynamicNumberOfGCThreads -J-XX:+TieredCompilation -J-XX:TieredStopAtLevel=1` +var ( + JavacVmFlags = strings.Join(javacVmFlagsList, " ") + javaVmFlagsList = []string{ + `-XX:OnError="cat hs_err_pid%p.log"`, + "-XX:CICompilerCount=6", + "-XX:+UseDynamicNumberOfGCThreads", + } + javacVmFlagsList = []string{ + `-J-XX:OnError="cat hs_err_pid%p.log"`, + "-J-XX:CICompilerCount=6", + "-J-XX:+UseDynamicNumberOfGCThreads", + "-J-XX:+TieredCompilation", + "-J-XX:TieredStopAtLevel=1", + } ) func init() { pctx.Import("github.com/google/blueprint/bootstrap") - pctx.StaticVariable("JavacHeapSize", "2048M") - pctx.StaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}") + exportedVars.ExportStringStaticVariable("JavacHeapSize", "2048M") + exportedVars.ExportStringStaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}") // ErrorProne can use significantly more memory than javac alone, give it a higher heap // size (b/221480398). - pctx.StaticVariable("ErrorProneHeapSize", "4096M") - pctx.StaticVariable("ErrorProneHeapFlags", "-J-Xmx${ErrorProneHeapSize}") + exportedVars.ExportStringStaticVariable("ErrorProneHeapSize", "4096M") + exportedVars.ExportStringStaticVariable("ErrorProneHeapFlags", "-J-Xmx${ErrorProneHeapSize}") - pctx.StaticVariable("DexFlags", "-JXX:OnError='cat hs_err_pid%p.log' -JXX:CICompilerCount=6 -JXX:+UseDynamicNumberOfGCThreads") + exportedVars.ExportStringListStaticVariable("DexFlags", []string{ + `-JXX:OnError="cat hs_err_pid%p.log"`, + "-JXX:CICompilerCount=6", + "-JXX:+UseDynamicNumberOfGCThreads", + }) - pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{ + exportedVars.ExportStringListStaticVariable("CommonJdkFlags", []string{ `-Xmaxerrs 9999999`, `-encoding UTF-8`, `-sourcepath ""`, @@ -85,10 +101,10 @@ func init() { // b/65004097: prevent using java.lang.invoke.StringConcatFactory when using -target 1.9 `-XDstringConcat=inline`, - }, " ")) + }) - pctx.StaticVariable("JavaVmFlags", JavaVmFlags) - pctx.StaticVariable("JavacVmFlags", JavacVmFlags) + exportedVars.ExportStringListStaticVariable("JavaVmFlags", javaVmFlagsList) + exportedVars.ExportStringListStaticVariable("JavacVmFlags", javacVmFlagsList) pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS) @@ -184,6 +200,10 @@ func init() { hostJNIToolVariableWithSdkToolsPrebuilt("SignapkJniLibrary", "libconscrypt_openjdk_jni") } +func BazelJavaToolchainVars(config android.Config) string { + return android.BazelToolchainVars(config, exportedVars) +} + func hostBinToolVariableWithSdkToolsPrebuilt(name, tool string) { pctx.VariableFunc(name, func(ctx android.PackageVarContext) string { if ctx.Config().AlwaysUsePrebuiltSdks() { diff --git a/java/config/error_prone.go b/java/config/error_prone.go index 48681b5c9..5f853c812 100644 --- a/java/config/error_prone.go +++ b/java/config/error_prone.go @@ -16,8 +16,6 @@ package config import ( "strings" - - "android/soong/android" ) var ( @@ -31,23 +29,23 @@ var ( ) // Wrapper that grabs value of val late so it can be initialized by a later module's init function -func errorProneVar(name string, val *[]string, sep string) { - pctx.VariableFunc(name, func(android.PackageVarContext) string { +func errorProneVar(val *[]string, sep string) func() string { + return func() string { return strings.Join(*val, sep) - }) + } } func init() { - errorProneVar("ErrorProneClasspath", &ErrorProneClasspath, ":") - errorProneVar("ErrorProneChecksError", &ErrorProneChecksError, " ") - errorProneVar("ErrorProneChecksWarning", &ErrorProneChecksWarning, " ") - errorProneVar("ErrorProneChecksDefaultDisabled", &ErrorProneChecksDefaultDisabled, " ") - errorProneVar("ErrorProneChecksOff", &ErrorProneChecksOff, " ") - errorProneVar("ErrorProneFlags", &ErrorProneFlags, " ") - pctx.StaticVariable("ErrorProneChecks", strings.Join([]string{ + exportedVars.ExportVariableFuncVariable("ErrorProneClasspath", errorProneVar(&ErrorProneClasspath, ":")) + exportedVars.ExportVariableFuncVariable("ErrorProneChecksError", errorProneVar(&ErrorProneChecksError, " ")) + exportedVars.ExportVariableFuncVariable("ErrorProneChecksWarning", errorProneVar(&ErrorProneChecksWarning, " ")) + exportedVars.ExportVariableFuncVariable("ErrorProneChecksDefaultDisabled", errorProneVar(&ErrorProneChecksDefaultDisabled, " ")) + exportedVars.ExportVariableFuncVariable("ErrorProneChecksOff", errorProneVar(&ErrorProneChecksOff, " ")) + exportedVars.ExportVariableFuncVariable("ErrorProneFlags", errorProneVar(&ErrorProneFlags, " ")) + exportedVars.ExportStringListStaticVariable("ErrorProneChecks", []string{ "${ErrorProneChecksOff}", "${ErrorProneChecksError}", "${ErrorProneChecksWarning}", "${ErrorProneChecksDefaultDisabled}", - }, " ")) + }) } diff --git a/starlark_fmt/format.go b/starlark_fmt/format.go index 23eee59b3..3e51fa14c 100644 --- a/starlark_fmt/format.go +++ b/starlark_fmt/format.go @@ -39,21 +39,26 @@ func PrintBool(item bool) string { // PrintsStringList returns a Starlark-compatible string of a list of Strings/Labels. func PrintStringList(items []string, indentLevel int) string { - return PrintList(items, indentLevel, `"%s"`) + return PrintList(items, indentLevel, func(s string) string { + if strings.Contains(s, "\"") { + return `'''%s'''` + } + return `"%s"` + }) } // PrintList returns a Starlark-compatible string of list formmated as requested. -func PrintList(items []string, indentLevel int, formatString string) string { +func PrintList(items []string, indentLevel int, formatString func(string) string) string { if len(items) == 0 { return "[]" } else if len(items) == 1 { - return fmt.Sprintf("["+formatString+"]", items[0]) + return fmt.Sprintf("["+formatString(items[0])+"]", 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, fmt.Sprintf(`%s`+formatString(item)+`,`, innerIndent, item)) } list = append(list, Indention(indentLevel)+"]") return strings.Join(list, "\n") diff --git a/starlark_fmt/format_test.go b/starlark_fmt/format_test.go index 90f78ef7a..9450a31b3 100644 --- a/starlark_fmt/format_test.go +++ b/starlark_fmt/format_test.go @@ -18,6 +18,10 @@ import ( "testing" ) +func simpleFormat(s string) string { + return "%s" +} + func TestPrintEmptyStringList(t *testing.T) { in := []string{} indentLevel := 0 @@ -54,7 +58,7 @@ func TestPrintMultiElementStringList(t *testing.T) { func TestPrintEmptyList(t *testing.T) { in := []string{} indentLevel := 0 - out := PrintList(in, indentLevel, "%s") + out := PrintList(in, indentLevel, simpleFormat) expectedOut := "[]" if out != expectedOut { t.Errorf("Expected %q, got %q", expectedOut, out) @@ -64,7 +68,7 @@ func TestPrintEmptyList(t *testing.T) { func TestPrintSingleElementList(t *testing.T) { in := []string{"1"} indentLevel := 0 - out := PrintList(in, indentLevel, "%s") + out := PrintList(in, indentLevel, simpleFormat) expectedOut := `[1]` if out != expectedOut { t.Errorf("Expected %q, got %q", expectedOut, out) @@ -74,7 +78,7 @@ func TestPrintSingleElementList(t *testing.T) { func TestPrintMultiElementList(t *testing.T) { in := []string{"1", "2"} indentLevel := 0 - out := PrintList(in, indentLevel, "%s") + out := PrintList(in, indentLevel, simpleFormat) expectedOut := `[ 1, 2, @@ -87,7 +91,7 @@ func TestPrintMultiElementList(t *testing.T) { func TestListWithNonZeroIndent(t *testing.T) { in := []string{"1", "2"} indentLevel := 1 - out := PrintList(in, indentLevel, "%s") + out := PrintList(in, indentLevel, simpleFormat) expectedOut := `[ 1, 2,