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

Commit 1062943c authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "soong config: add value_variable substitution"

parents c8d1da11 b0935db8
Loading
Loading
Loading
Loading
+9 −3
Original line number Diff line number Diff line
@@ -421,6 +421,7 @@ soong_config_module_type {
    config_namespace: "acme",
    variables: ["board"],
    bool_variables: ["feature"],
    value_variables: ["width"],
    properties: ["cflags", "srcs"],
}

@@ -431,8 +432,9 @@ soong_config_string_variable {
```

This example describes a new `acme_cc_defaults` module type that extends the
`cc_defaults` module type, with two additional conditionals based on variables
`board` and `feature`, which can affect properties `cflags` and `srcs`.
`cc_defaults` module type, with three additional conditionals based on
variables `board`, `feature` and `width`, which can affect properties `cflags`
and `srcs`.

The values of the variables can be set from a product's `BoardConfig.mk` file:
```
@@ -443,6 +445,7 @@ SOONG_CONFIG_acme += \

SOONG_CONFIG_acme_board := soc_a
SOONG_CONFIG_acme_feature := true
SOONG_CONFIG_acme_width := 200
```

The `acme_cc_defaults` module type can be used anywhere after the definition in
@@ -471,6 +474,9 @@ acme_cc_defaults {
        feature: {
            cflags: ["-DFEATURE"],
        },
        width: {
            cflags: ["-DWIDTH=%s"],
        },
    },
}

@@ -482,7 +488,7 @@ cc_library {
```

With the `BoardConfig.mk` snippet above, libacme_foo would build with
cflags "-DGENERIC -DSOC_A -DFEATURE".
cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".

`soong_config_module_type` modules will work best when used to wrap defaults
modules (`cc_defaults`, `java_defaults`, etc.), which can then be referenced
+17 −2
Original line number Diff line number Diff line
@@ -73,6 +73,9 @@ type soongConfigModuleTypeImportProperties struct {
//             feature: {
//                 cflags: ["-DFEATURE"],
//             },
//             width: {
//                 cflags: ["-DWIDTH=%s"],
//             },
//         },
//     }
//
@@ -90,6 +93,7 @@ type soongConfigModuleTypeImportProperties struct {
//         config_namespace: "acme",
//         variables: ["board"],
//         bool_variables: ["feature"],
//         value_variables: ["width"],
//         properties: ["cflags", "srcs"],
//     }
//
@@ -107,8 +111,9 @@ type soongConfigModuleTypeImportProperties struct {
//
//     SOONG_CONFIG_acme_board := soc_a
//     SOONG_CONFIG_acme_feature := true
//     SOONG_CONFIG_acme_width := 200
//
// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
func soongConfigModuleTypeImportFactory() Module {
	module := &soongConfigModuleTypeImport{}

@@ -151,6 +156,7 @@ type soongConfigModuleTypeModule struct {
//         config_namespace: "acme",
//         variables: ["board"],
//         bool_variables: ["feature"],
//         value_variables: ["width"],
//         properties: ["cflags", "srcs"],
//     }
//
@@ -174,6 +180,9 @@ type soongConfigModuleTypeModule struct {
//             feature: {
//                 cflags: ["-DFEATURE"],
//             },
//             width: {
//	               cflags: ["-DWIDTH=%s"],
//             },
//         },
//     }
//
@@ -192,6 +201,7 @@ type soongConfigModuleTypeModule struct {
//
//     SOONG_CONFIG_acme_board := soc_a
//     SOONG_CONFIG_acme_feature := true
//     SOONG_CONFIG_acme_width := 200
//
// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
func soongConfigModuleTypeFactory() Module {
@@ -352,7 +362,12 @@ func soongConfigModuleFactory(factory blueprint.ModuleFactory,

			AddLoadHook(module, func(ctx LoadHookContext) {
				config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
				for _, ps := range soongconfig.PropertiesToApply(moduleType, conditionalProps, config) {
				newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, config)
				if err != nil {
					ctx.ModuleErrorf("%s", err)
					return
				}
				for _, ps := range newProps {
					ctx.AppendProperties(ps)
				}
			})
+6 −1
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ func TestSoongConfigModule(t *testing.T) {
			config_namespace: "acme",
			variables: ["board", "feature1", "FEATURE3"],
			bool_variables: ["feature2"],
			value_variables: ["size"],
			properties: ["cflags", "srcs"],
		}

@@ -82,6 +83,9 @@ func TestSoongConfigModule(t *testing.T) {
						cflags: ["-DSOC_B"],
					},
				},
				size: {
					cflags: ["-DSIZE=%s"],
				},
				feature1: {
					cflags: ["-DFEATURE1"],
				},
@@ -101,6 +105,7 @@ func TestSoongConfigModule(t *testing.T) {
		config.TestProductVariables.VendorVars = map[string]map[string]string{
			"acme": map[string]string{
				"board":    "soc_a",
				"size":     "42",
				"feature1": "true",
				"feature2": "false",
				// FEATURE3 unset
@@ -121,7 +126,7 @@ func TestSoongConfigModule(t *testing.T) {
		FailIfErrored(t, errs)

		foo := ctx.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
		if g, w := foo.props.Cflags, []string{"-DGENERIC", "-DSOC_A", "-DFEATURE1"}; !reflect.DeepEqual(g, w) {
		if g, w := foo.props.Cflags, []string{"-DGENERIC", "-DSIZE=42", "-DSOC_A", "-DFEATURE1"}; !reflect.DeepEqual(g, w) {
			t.Errorf("wanted foo cflags %q, got %q", w, g)
		}
	}
+99 −9
Original line number Diff line number Diff line
@@ -112,6 +112,10 @@ type ModuleTypeProperties struct {
	// the list of boolean SOONG_CONFIG variables that this module type will read
	Bool_variables []string

	// the list of SOONG_CONFIG variables that this module type will read. The value will be
	// inserted into the properties with %s substitution.
	Value_variables []string

	// the list of properties that this module type will extend.
	Properties []string
}
@@ -161,6 +165,18 @@ func processModuleTypeDef(v *SoongConfigDefinition, def *parser.Module) (errs []
		})
	}

	for _, name := range props.Value_variables {
		if name == "" {
			return []error{fmt.Errorf("value_variables entry must not be blank")}
		}

		mt.Variables = append(mt.Variables, &valueVariable{
			baseVariable: baseVariable{
				variable: name,
			},
		})
	}

	return nil
}

@@ -404,15 +420,17 @@ func typeForPropertyFromPropertyStruct(ps interface{}, property string) reflect.

// PropertiesToApply returns the applicable properties from a ModuleType that should be applied
// based on SoongConfig values.
func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) []interface{} {
func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
	var ret []interface{}
	props = props.Elem().FieldByName(soongConfigProperty)
	for i, c := range moduleType.Variables {
		if ps := c.PropertiesToApply(config, props.Field(i)); ps != nil {
		if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
			return nil, err
		} else if ps != nil {
			ret = append(ret, ps)
		}
	}
	return ret
	return ret, nil
}

type ModuleType struct {
@@ -438,7 +456,7 @@ type soongConfigVariable interface {

	// PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied
	// to the module.
	PropertiesToApply(config SoongConfig, values reflect.Value) interface{}
	PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error)
}

type baseVariable struct {
@@ -473,14 +491,14 @@ func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type)
	}
}

func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) interface{} {
func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
	for j, v := range s.values {
		if config.String(s.variable) == v {
			return values.Field(j).Interface()
			return values.Field(j).Interface(), nil
		}
	}

	return nil
	return nil, nil
}

type boolVariable struct {
@@ -495,11 +513,83 @@ func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
	v.Set(reflect.Zero(typ))
}

func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) interface{} {
func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
	if config.Bool(b.variable) {
		return values.Interface()
		return values.Interface(), nil
	}

	return nil, nil
}

type valueVariable struct {
	baseVariable
}

func (s *valueVariable) variableValuesType() reflect.Type {
	return emptyInterfaceType
}

func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
	v.Set(reflect.Zero(typ))
}

func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
	if !config.IsSet(s.variable) {
		return nil, nil
	}
	configValue := config.String(s.variable)

	propStruct := values.Elem().Elem()
	for i := 0; i < propStruct.NumField(); i++ {
		field := propStruct.Field(i)
		kind := field.Kind()
		if kind == reflect.Ptr {
			if field.IsNil() {
				continue
			}
			field = field.Elem()
		}
		switch kind {
		case reflect.String:
			err := printfIntoProperty(field, configValue)
			if err != nil {
				return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
			}
		case reflect.Slice:
			for j := 0; j < field.Len(); j++ {
				err := printfIntoProperty(field.Index(j), configValue)
				if err != nil {
					return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
				}
			}
		case reflect.Bool:
			// Nothing to do
		default:
			return nil, fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, propStruct.Type().Field(i).Name, kind)
		}
	}

	return values.Interface(), nil
}

func printfIntoProperty(propertyValue reflect.Value, configValue string) error {
	s := propertyValue.String()

	count := strings.Count(s, "%")
	if count == 0 {
		return nil
	}

	if count > 1 {
		return fmt.Errorf("value variable properties only support a single '%%'")
	}

	if !strings.Contains(s, "%s") {
		return fmt.Errorf("unsupported %% in value variable property")
	}

	propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, configValue)))

	return nil
}