Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7f88956c authored by Sam Delmerico's avatar Sam Delmerico
Browse files

refactor Bazel variable export

Most of the variable export code for cc modules can be re-used for
exporting variables for java modules. Refactor this code into a more
composable structure for reuse.

Test: build/bazel/bp2build.sh
Test: manual comparison of
  out/soong/soong_injection/cc_toolchain/constants.bzl
  with previous output
Change-Id: Ie5a6fee08cc888b7dc69c3e324e5c3f8aa269a8f
parent 85b935ef
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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",
+3 −2
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ import (
	"fmt"
	"strconv"

	"android/soong/bazel"
	"android/soong/starlark_fmt"
)

@@ -393,7 +394,7 @@ 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
+137 −80
Original line number Diff line number Diff line
@@ -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,112 @@ 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

	pctx PackageContext
}

// 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)
}

// 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)
}

type exportedConfigDependingVariables map[string]interface{}
// 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{}) {
func (m ExportedConfigDependingVariables) set(k string, v interface{}) {
	m[k] = v
}

@@ -67,14 +146,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 +172,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 +205,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 +226,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 +265,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 +292,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 +336,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)
+51 −52
Original line number Diff line number Diff line
@@ -12,25 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package config
package android

import (
	"android/soong/bazel"
	"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
	android_arm64_config := TestConfig("out", nil, "", nil)
	android_arm64_config.BuildOS = Android
	android_arm64_config.BuildArch = Arm64

	testCases := []struct {
		description     string
		config          android.Config
		stringScope     exportedStringVariables
		stringListScope exportedStringListVariables
		configVars      exportedConfigDependingVariables
		config          Config
		stringScope     ExportedStringVariables
		stringListScope ExportedStringListVariables
		configVars      ExportedConfigDependingVariables
		toExpand        string
		expectedValues  []string
	}{
@@ -41,7 +40,7 @@ func TestExpandVars(t *testing.T) {
		},
		{
			description: "single level expansion for string var",
			stringScope: exportedStringVariables{
			stringScope: ExportedStringVariables{
				"foo": "bar",
			},
			toExpand:       "${foo}",
@@ -49,7 +48,7 @@ func TestExpandVars(t *testing.T) {
		},
		{
			description: "single level expansion with short-name for string var",
			stringScope: exportedStringVariables{
			stringScope: ExportedStringVariables{
				"foo": "bar",
			},
			toExpand:       "${config.foo}",
@@ -57,7 +56,7 @@ func TestExpandVars(t *testing.T) {
		},
		{
			description: "single level expansion string list var",
			stringListScope: exportedStringListVariables{
			stringListScope: ExportedStringListVariables{
				"foo": []string{"bar"},
			},
			toExpand:       "${foo}",
@@ -65,11 +64,11 @@ func TestExpandVars(t *testing.T) {
		},
		{
			description: "mixed level expansion for string list var",
			stringScope: exportedStringVariables{
			stringScope: ExportedStringVariables{
				"foo": "${bar}",
				"qux": "hello",
			},
			stringListScope: exportedStringListVariables{
			stringListScope: ExportedStringListVariables{
				"bar": []string{"baz", "${qux}"},
			},
			toExpand:       "${foo}",
@@ -77,7 +76,7 @@ func TestExpandVars(t *testing.T) {
		},
		{
			description: "double level expansion",
			stringListScope: exportedStringListVariables{
			stringListScope: ExportedStringListVariables{
				"foo": []string{"${bar}"},
				"bar": []string{"baz"},
			},
@@ -86,7 +85,7 @@ func TestExpandVars(t *testing.T) {
		},
		{
			description: "double level expansion with a literal",
			stringListScope: exportedStringListVariables{
			stringListScope: ExportedStringListVariables{
				"a": []string{"${b}", "c"},
				"b": []string{"d"},
			},
@@ -95,7 +94,7 @@ func TestExpandVars(t *testing.T) {
		},
		{
			description: "double level expansion, with two variables in a string",
			stringListScope: exportedStringListVariables{
			stringListScope: ExportedStringListVariables{
				"a": []string{"${b} ${c}"},
				"b": []string{"d"},
				"c": []string{"e"},
@@ -105,7 +104,7 @@ func TestExpandVars(t *testing.T) {
		},
		{
			description: "triple level expansion with two variables in a string",
			stringListScope: exportedStringListVariables{
			stringListScope: ExportedStringListVariables{
				"a": []string{"${b} ${c}"},
				"b": []string{"${c}", "${d}"},
				"c": []string{"${d}"},
@@ -116,9 +115,9 @@ func TestExpandVars(t *testing.T) {
		},
		{
			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() },
			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}",
@@ -126,14 +125,14 @@ func TestExpandVars(t *testing.T) {
		},
		{
			description: "double level multi type expansion",
			stringListScope: exportedStringListVariables{
			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" },
			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}",
@@ -160,19 +159,19 @@ func TestExpandVars(t *testing.T) {
func TestBazelToolchainVars(t *testing.T) {
	testCases := []struct {
		name        string
		config      android.Config
		vars        []bazelVarExporter
		config      Config
		vars        ExportedVariables
		expectedOut string
	}{
		{
			name: "exports strings",
			vars: []bazelVarExporter{
				exportedStringVariables{
			vars: ExportedVariables{
				exportedStringVars: ExportedStringVariables{
					"a": "b",
					"c": "d",
				},
			},
			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
			expectedOut: bazel.GeneratedBazelFileWarning + `

_a = "b"

@@ -185,13 +184,13 @@ constants = struct(
		},
		{
			name: "exports string lists",
			vars: []bazelVarExporter{
				exportedStringListVariables{
			vars: ExportedVariables{
				exportedStringListVars: ExportedStringListVariables{
					"a": []string{"b1", "b2"},
					"c": []string{"d1", "d2"},
				},
			},
			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
			expectedOut: bazel.GeneratedBazelFileWarning + `

_a = [
    "b1",
@@ -210,13 +209,13 @@ constants = struct(
		},
		{
			name: "exports string lists dicts",
			vars: []bazelVarExporter{
				exportedStringListDictVariables{
					"a": map[string][]string{"b1": []string{"b2"}},
					"c": map[string][]string{"d1": []string{"d2"}},
			vars: ExportedVariables{
				exportedStringListDictVars: ExportedStringListDictVariables{
					"a": map[string][]string{"b1": {"b2"}},
					"c": map[string][]string{"d1": {"d2"}},
				},
			},
			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
			expectedOut: bazel.GeneratedBazelFileWarning + `

_a = {
    "b1": ["b2"],
@@ -233,13 +232,13 @@ constants = struct(
		},
		{
			name: "exports dict with var refs",
			vars: []bazelVarExporter{
				exportedVariableReferenceDictVariables{
			vars: ExportedVariables{
				exportedVariableReferenceDictVars: ExportedVariableReferenceDictVariables{
					"a": map[string]string{"b1": "${b2}"},
					"c": map[string]string{"d1": "${config.d2}"},
				},
			},
			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
			expectedOut: bazel.GeneratedBazelFileWarning + `

_a = {
    "b1": _b2,
@@ -256,25 +255,25 @@ constants = struct(
		},
		{
			name: "sorts across types with variable references last",
			vars: []bazelVarExporter{
				exportedStringVariables{
			vars: ExportedVariables{
				exportedStringVars: ExportedStringVariables{
					"b": "b-val",
					"d": "d-val",
				},
				exportedStringListVariables{
				exportedStringListVars: ExportedStringListVariables{
					"c": []string{"c-val"},
					"e": []string{"e-val"},
				},
				exportedStringListDictVariables{
					"a": map[string][]string{"a1": []string{"a2"}},
					"f": map[string][]string{"f1": []string{"f2"}},
				exportedStringListDictVars: ExportedStringListDictVariables{
					"a": map[string][]string{"a1": {"a2"}},
					"f": map[string][]string{"f1": {"f2"}},
				},
				exportedVariableReferenceDictVariables{
				exportedVariableReferenceDictVars: ExportedVariableReferenceDictVariables{
					"aa": map[string]string{"b1": "${b}"},
					"cc": map[string]string{"d1": "${config.d}"},
				},
			},
			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
			expectedOut: bazel.GeneratedBazelFileWarning + `

_a = {
    "a1": ["a2"],
@@ -315,7 +314,7 @@ constants = struct(

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			out := bazelToolchainVars(tc.config, tc.vars...)
			out := BazelToolchainVars(tc.config, tc.vars)
			if out != tc.expectedOut {
				t.Errorf("Expected \n%s, got \n%s", tc.expectedOut, out)
			}
+1 −1
Original line number Diff line number Diff line
@@ -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.
Loading