From 1da0b20575bc02c784815d87efc4a3e556e8128a Mon Sep 17 00:00:00 2001 From: Cole Faust Date: Wed, 21 Feb 2024 10:50:33 -0800 Subject: [PATCH] Allow soong config value variables to set nested properties Previously, it would error out if it saw anything that wasn't a string or slice of strings. Now it will also recurse in sub-structs. Fixes: 326255534 Test: go test Change-Id: Icbca8e4a2cf54b5610599a10805550fed05eb396 --- android/soongconfig/modules.go | 26 ++++++++--- android/soongconfig/modules_test.go | 70 +++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 5 deletions(-) diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go index d525bdcfe..c78b72669 100644 --- a/android/soongconfig/modules.go +++ b/android/soongconfig/modules.go @@ -681,6 +681,14 @@ func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Val if !propStruct.IsValid() { return nil, nil } + if err := s.printfIntoPropertyRecursive(nil, propStruct, configValue); err != nil { + return nil, err + } + + return values.Interface(), nil +} + +func (s *valueVariable) printfIntoPropertyRecursive(fieldName []string, propStruct reflect.Value, configValue string) error { for i := 0; i < propStruct.NumField(); i++ { field := propStruct.Field(i) kind := field.Kind() @@ -695,23 +703,31 @@ func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Val 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) + fieldName = append(fieldName, propStruct.Type().Field(i).Name) + return fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, strings.Join(fieldName, "."), 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) + fieldName = append(fieldName, propStruct.Type().Field(i).Name) + return fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, strings.Join(fieldName, "."), err) } } case reflect.Bool: // Nothing to do + case reflect.Struct: + fieldName = append(fieldName, propStruct.Type().Field(i).Name) + if err := s.printfIntoPropertyRecursive(fieldName, field, configValue); err != nil { + return err + } + fieldName = fieldName[:len(fieldName)-1] default: - return nil, fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, propStruct.Type().Field(i).Name, kind) + fieldName = append(fieldName, propStruct.Type().Field(i).Name) + return fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, strings.Join(fieldName, "."), kind) } } - - return values.Interface(), nil + return nil } func printfIntoProperty(propertyValue reflect.Value, configValue string) error { diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go index db2c83ccb..1da0b49ad 100644 --- a/android/soongconfig/modules_test.go +++ b/android/soongconfig/modules_test.go @@ -429,6 +429,76 @@ func Test_PropertiesToApply_Value(t *testing.T) { } } +func Test_PropertiesToApply_Value_Nested(t *testing.T) { + mt, _ := newModuleType(&ModuleTypeProperties{ + Module_type: "foo", + Config_namespace: "bar", + Value_variables: []string{"my_value_var"}, + Properties: []string{"a.b"}, + }) + type properties struct { + A struct { + B string + } + } + conditionsDefault := &properties{ + A: struct{ B string }{ + B: "default", + }, + } + type valueVarProps struct { + A struct { + B string + } + Conditions_default *properties + } + actualProps := &struct { + Soong_config_variables valueSoongConfigVars + }{ + Soong_config_variables: valueSoongConfigVars{ + My_value_var: &valueVarProps{ + A: struct{ B string }{ + B: "A.B=%s", + }, + Conditions_default: conditionsDefault, + }, + }, + } + props := reflect.ValueOf(actualProps) + + testCases := []struct { + name string + config SoongConfig + wantProps []interface{} + }{ + { + name: "no_vendor_config", + config: Config(map[string]string{}), + wantProps: []interface{}{conditionsDefault}, + }, + { + name: "value_var_set", + config: Config(map[string]string{"my_value_var": "Hello"}), + wantProps: []interface{}{&properties{ + A: struct{ B string }{ + B: "A.B=Hello", + }, + }}, + }, + } + + for _, tc := range testCases { + gotProps, err := PropertiesToApply(mt, props, tc.config) + if err != nil { + t.Errorf("%s: Unexpected error in PropertiesToApply: %s", tc.name, err) + } + + if !reflect.DeepEqual(gotProps, tc.wantProps) { + t.Errorf("%s: Expected %s, got %s", tc.name, tc.wantProps, gotProps) + } + } +} + func Test_PropertiesToApply_String_Error(t *testing.T) { mt, _ := newModuleType(&ModuleTypeProperties{ Module_type: "foo",