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

Commit fdbf5d47 authored by Cole Faust's avatar Cole Faust
Browse files

Update the ConfigurableEvaluator for typed selects

See the blueprint cl for more information.

Also added tests for both multivariable and typed selects.

Bug: 323382414
Test: m nothing --no-skip-soong-tests
Change-Id: I00c1a3c56d34affb88f4b4d911c318b28ffe7695
parent 033ffb95
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ import (
	"strings"

	"github.com/google/blueprint"
	"github.com/google/blueprint/parser"
	"github.com/google/blueprint/proptools"
)

// BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns
@@ -219,7 +219,7 @@ type BaseModuleContext interface {

	// EvaluateConfiguration makes ModuleContext a valid proptools.ConfigurableEvaluator, so this context
	// can be used to evaluate the final value of Configurable properties.
	EvaluateConfiguration(parser.SelectType, string, string) (string, bool)
	EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue
}

type baseModuleContext struct {
@@ -577,6 +577,6 @@ func (b *baseModuleContext) GetPathString(skipFirst bool) string {
	return sb.String()
}

func (m *baseModuleContext) EvaluateConfiguration(ty parser.SelectType, property, condition string) (string, bool) {
	return m.Module().ConfigurableEvaluator(m).EvaluateConfiguration(ty, property, condition)
func (m *baseModuleContext) EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue {
	return m.Module().ConfigurableEvaluator(m).EvaluateConfiguration(condition, property)
}
+55 −22
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ import (
	"android/soong/bazel"

	"github.com/google/blueprint"
	"github.com/google/blueprint/parser"
	"github.com/google/blueprint/proptools"
)

@@ -2140,41 +2139,75 @@ func (e configurationEvalutor) PropertyErrorf(property string, fmt string, args
	e.ctx.OtherModulePropertyErrorf(e.m, property, fmt, args)
}

func (e configurationEvalutor) EvaluateConfiguration(ty parser.SelectType, property, condition string) (string, bool) {
func (e configurationEvalutor) EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue {
	ctx := e.ctx
	m := e.m
	switch ty {
	case parser.SelectTypeReleaseVariable:
		if v, ok := ctx.Config().productVariables.BuildFlags[condition]; ok {
			return v, true
	switch condition.FunctionName {
	case "release_variable":
		if len(condition.Args) != 1 {
			ctx.OtherModulePropertyErrorf(m, property, "release_variable requires 1 argument, found %d", len(condition.Args))
			return proptools.ConfigurableValueUndefined()
		}
		return "", false
	case parser.SelectTypeProductVariable:
		if v, ok := ctx.Config().productVariables.BuildFlags[condition.Args[0]]; ok {
			return proptools.ConfigurableValueString(v)
		}
		return proptools.ConfigurableValueUndefined()
	case "product_variable":
		// TODO(b/323382414): Might add these on a case-by-case basis
		ctx.OtherModulePropertyErrorf(m, property, "TODO(b/323382414): Product variables are not yet supported in selects")
		return "", false
	case parser.SelectTypeSoongConfigVariable:
		parts := strings.Split(condition, ":")
		namespace := parts[0]
		variable := parts[1]
		return proptools.ConfigurableValueUndefined()
	case "soong_config_variable":
		if len(condition.Args) != 2 {
			ctx.OtherModulePropertyErrorf(m, property, "soong_config_variable requires 2 arguments, found %d", len(condition.Args))
			return proptools.ConfigurableValueUndefined()
		}
		namespace := condition.Args[0]
		variable := condition.Args[1]
		if n, ok := ctx.Config().productVariables.VendorVars[namespace]; ok {
			if v, ok := n[variable]; ok {
				return v, true
				return proptools.ConfigurableValueString(v)
			}
		}
		return proptools.ConfigurableValueUndefined()
	case "variant":
		if len(condition.Args) != 1 {
			ctx.OtherModulePropertyErrorf(m, property, "variant requires 1 argument, found %d", len(condition.Args))
			return proptools.ConfigurableValueUndefined()
		}
		return "", false
	case parser.SelectTypeVariant:
		if condition == "arch" {
		if condition.Args[0] == "arch" {
			if !m.base().ArchReady() {
				ctx.OtherModulePropertyErrorf(m, property, "A select on arch was attempted before the arch mutator ran")
				return "", false
				return proptools.ConfigurableValueUndefined()
			}
			return proptools.ConfigurableValueString(m.base().Arch().ArchType.Name)
		}
		ctx.OtherModulePropertyErrorf(m, property, "Unknown variant %s", condition.Args[0])
		return proptools.ConfigurableValueUndefined()
	case "boolean_var_for_testing":
		// We currently don't have any other boolean variables (we should add support for typing
		// the soong config variables), so add this fake one for testing the boolean select
		// functionality.
		if len(condition.Args) != 0 {
			ctx.OtherModulePropertyErrorf(m, property, "boolean_var_for_testing requires 0 arguments, found %d", len(condition.Args))
			return proptools.ConfigurableValueUndefined()
		}

		if n, ok := ctx.Config().productVariables.VendorVars["boolean_var"]; ok {
			if v, ok := n["for_testing"]; ok {
				switch v {
				case "true":
					return proptools.ConfigurableValueBool(true)
				case "false":
					return proptools.ConfigurableValueBool(false)
				default:
					ctx.OtherModulePropertyErrorf(m, property, "testing:my_boolean_var can only be true or false, found %q", v)
				}
			}
			return m.base().Arch().ArchType.Name, true
		}
		ctx.OtherModulePropertyErrorf(m, property, "Unknown variant %s", condition)
		return "", false
		return proptools.ConfigurableValueUndefined()
	default:
		panic("Should be unreachable")
		ctx.OtherModulePropertyErrorf(m, property, "Unknown select condition %s", condition.FunctionName)
		return proptools.ConfigurableValueUndefined()
	}
}

+166 −0
Original line number Diff line number Diff line
@@ -327,8 +327,10 @@ func TestSelects(t *testing.T) {
			my_module_type {
				name: "foo",
				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
					"foo": "bar",
					default: unset,
				}) + select(soong_config_variable("my_namespace", "my_variable2"), {
					"baz": "qux",
					default: unset,
				})
			}
@@ -341,6 +343,7 @@ func TestSelects(t *testing.T) {
			my_module_type {
				name: "foo",
				my_string: select(soong_config_variable("my_namespace", "my_variable"), {
					"foo": "bar",
					default: unset,
				}) + select(soong_config_variable("my_namespace", "my_variable2"), {
					default: "a",
@@ -414,6 +417,169 @@ func TestSelects(t *testing.T) {
				replacing_string_list: &[]string{"b1"},
			},
		},
		{
			name: "Multi-condition string 1",
			bp: `
			my_module_type {
				name: "foo",
				my_string: select((
					soong_config_variable("my_namespace", "my_variable"),
					soong_config_variable("my_namespace", "my_variable2"),
				), {
					("a", "b"): "a+b",
					("a", default): "a+default",
					(default, default): "default",
				}),
			}
			`,
			vendorVars: map[string]map[string]string{
				"my_namespace": {
					"my_variable":  "a",
					"my_variable2": "b",
				},
			},
			provider: selectsTestProvider{
				my_string: proptools.StringPtr("a+b"),
			},
		},
		{
			name: "Multi-condition string 2",
			bp: `
			my_module_type {
				name: "foo",
				my_string: select((
					soong_config_variable("my_namespace", "my_variable"),
					soong_config_variable("my_namespace", "my_variable2"),
				), {
					("a", "b"): "a+b",
					("a", default): "a+default",
					(default, default): "default",
				}),
			}
			`,
			vendorVars: map[string]map[string]string{
				"my_namespace": {
					"my_variable":  "a",
					"my_variable2": "c",
				},
			},
			provider: selectsTestProvider{
				my_string: proptools.StringPtr("a+default"),
			},
		},
		{
			name: "Multi-condition string 3",
			bp: `
			my_module_type {
				name: "foo",
				my_string: select((
					soong_config_variable("my_namespace", "my_variable"),
					soong_config_variable("my_namespace", "my_variable2"),
				), {
					("a", "b"): "a+b",
					("a", default): "a+default",
					(default, default): "default",
				}),
			}
			`,
			vendorVars: map[string]map[string]string{
				"my_namespace": {
					"my_variable":  "c",
					"my_variable2": "b",
				},
			},
			provider: selectsTestProvider{
				my_string: proptools.StringPtr("default"),
			},
		},
		{
			name: "Select on boolean",
			bp: `
			my_module_type {
				name: "foo",
				my_string: select(boolean_var_for_testing(), {
					true: "t",
					false: "f",
				}),
			}
			`,
			vendorVars: map[string]map[string]string{
				"boolean_var": {
					"for_testing": "true",
				},
			},
			provider: selectsTestProvider{
				my_string: proptools.StringPtr("t"),
			},
		},
		{
			name: "Select on boolean false",
			bp: `
			my_module_type {
				name: "foo",
				my_string: select(boolean_var_for_testing(), {
					true: "t",
					false: "f",
				}),
			}
			`,
			vendorVars: map[string]map[string]string{
				"boolean_var": {
					"for_testing": "false",
				},
			},
			provider: selectsTestProvider{
				my_string: proptools.StringPtr("f"),
			},
		},
		{
			name: "Select on boolean undefined",
			bp: `
			my_module_type {
				name: "foo",
				my_string: select(boolean_var_for_testing(), {
					true: "t",
					false: "f",
				}),
			}
			`,
			expectedError: "foo",
		},
		{
			name: "Select on boolean undefined with default",
			bp: `
			my_module_type {
				name: "foo",
				my_string: select(boolean_var_for_testing(), {
					true: "t",
					false: "f",
					default: "default",
				}),
			}
			`,
			provider: selectsTestProvider{
				my_string: proptools.StringPtr("default"),
			},
		},
		{
			name: "Mismatched condition types",
			bp: `
			my_module_type {
				name: "foo",
				my_string: select(boolean_var_for_testing(), {
					"true": "t",
					"false": "f",
					default: "default",
				}),
			}
			`,
			vendorVars: map[string]map[string]string{
				"boolean_var": {
					"for_testing": "false",
				},
			},
			expectedError: "Expected all branches of a select on condition boolean_var_for_testing\\(\\) to have type bool, found string",
		},
	}

	for _, tc := range testCases {