Loading README.md +9 −3 Original line number Diff line number Diff line Loading @@ -421,6 +421,7 @@ soong_config_module_type { config_namespace: "acme", variables: ["board"], bool_variables: ["feature"], value_variables: ["width"], properties: ["cflags", "srcs"], } Loading @@ -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: ``` Loading @@ -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 Loading Loading @@ -471,6 +474,9 @@ acme_cc_defaults { feature: { cflags: ["-DFEATURE"], }, width: { cflags: ["-DWIDTH=%s"], }, }, } Loading @@ -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 Loading android/soong_config_modules.go +17 −2 Original line number Diff line number Diff line Loading @@ -73,6 +73,9 @@ type soongConfigModuleTypeImportProperties struct { // feature: { // cflags: ["-DFEATURE"], // }, // width: { // cflags: ["-DWIDTH=%s"], // }, // }, // } // Loading @@ -90,6 +93,7 @@ type soongConfigModuleTypeImportProperties struct { // config_namespace: "acme", // variables: ["board"], // bool_variables: ["feature"], // value_variables: ["width"], // properties: ["cflags", "srcs"], // } // Loading @@ -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{} Loading Loading @@ -151,6 +156,7 @@ type soongConfigModuleTypeModule struct { // config_namespace: "acme", // variables: ["board"], // bool_variables: ["feature"], // value_variables: ["width"], // properties: ["cflags", "srcs"], // } // Loading @@ -174,6 +180,9 @@ type soongConfigModuleTypeModule struct { // feature: { // cflags: ["-DFEATURE"], // }, // width: { // cflags: ["-DWIDTH=%s"], // }, // }, // } // Loading @@ -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 { Loading Loading @@ -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) } }) Loading android/soong_config_modules_test.go +6 −1 Original line number Diff line number Diff line Loading @@ -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"], } Loading Loading @@ -82,6 +83,9 @@ func TestSoongConfigModule(t *testing.T) { cflags: ["-DSOC_B"], }, }, size: { cflags: ["-DSIZE=%s"], }, feature1: { cflags: ["-DFEATURE1"], }, Loading @@ -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 Loading @@ -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) } } Loading android/soongconfig/modules.go +99 −9 Original line number Diff line number Diff line Loading @@ -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 } Loading Loading @@ -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 } Loading Loading @@ -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 { Loading @@ -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 { Loading Loading @@ -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 { Loading @@ -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 } Loading Loading
README.md +9 −3 Original line number Diff line number Diff line Loading @@ -421,6 +421,7 @@ soong_config_module_type { config_namespace: "acme", variables: ["board"], bool_variables: ["feature"], value_variables: ["width"], properties: ["cflags", "srcs"], } Loading @@ -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: ``` Loading @@ -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 Loading Loading @@ -471,6 +474,9 @@ acme_cc_defaults { feature: { cflags: ["-DFEATURE"], }, width: { cflags: ["-DWIDTH=%s"], }, }, } Loading @@ -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 Loading
android/soong_config_modules.go +17 −2 Original line number Diff line number Diff line Loading @@ -73,6 +73,9 @@ type soongConfigModuleTypeImportProperties struct { // feature: { // cflags: ["-DFEATURE"], // }, // width: { // cflags: ["-DWIDTH=%s"], // }, // }, // } // Loading @@ -90,6 +93,7 @@ type soongConfigModuleTypeImportProperties struct { // config_namespace: "acme", // variables: ["board"], // bool_variables: ["feature"], // value_variables: ["width"], // properties: ["cflags", "srcs"], // } // Loading @@ -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{} Loading Loading @@ -151,6 +156,7 @@ type soongConfigModuleTypeModule struct { // config_namespace: "acme", // variables: ["board"], // bool_variables: ["feature"], // value_variables: ["width"], // properties: ["cflags", "srcs"], // } // Loading @@ -174,6 +180,9 @@ type soongConfigModuleTypeModule struct { // feature: { // cflags: ["-DFEATURE"], // }, // width: { // cflags: ["-DWIDTH=%s"], // }, // }, // } // Loading @@ -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 { Loading Loading @@ -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) } }) Loading
android/soong_config_modules_test.go +6 −1 Original line number Diff line number Diff line Loading @@ -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"], } Loading Loading @@ -82,6 +83,9 @@ func TestSoongConfigModule(t *testing.T) { cflags: ["-DSOC_B"], }, }, size: { cflags: ["-DSIZE=%s"], }, feature1: { cflags: ["-DFEATURE1"], }, Loading @@ -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 Loading @@ -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) } } Loading
android/soongconfig/modules.go +99 −9 Original line number Diff line number Diff line Loading @@ -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 } Loading Loading @@ -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 } Loading Loading @@ -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 { Loading @@ -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 { Loading Loading @@ -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 { Loading @@ -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 } Loading