diff --git a/android/module.go b/android/module.go index c2fa84847..82a806778 100644 --- a/android/module.go +++ b/android/module.go @@ -19,6 +19,7 @@ import ( "os" "path" "path/filepath" + "reflect" "regexp" "strings" "text/scanner" @@ -1326,7 +1327,64 @@ func (m *ModuleBase) GetUnconvertedBp2buildDeps() []string { } func (m *ModuleBase) AddJSONData(d *map[string]interface{}) { - (*d)["Android"] = map[string]interface{}{} + (*d)["Android"] = map[string]interface{}{ + // Properties set in Blueprint or in blueprint of a defaults modules + "SetProperties": m.propertiesWithValues(), + } +} + +type propInfo struct { + Name string + Type string +} + +func (m *ModuleBase) propertiesWithValues() []propInfo { + var info []propInfo + props := m.GetProperties() + + var propsWithValues func(name string, v reflect.Value) + propsWithValues = func(name string, v reflect.Value) { + kind := v.Kind() + switch kind { + case reflect.Ptr, reflect.Interface: + if v.IsNil() { + return + } + propsWithValues(name, v.Elem()) + case reflect.Struct: + if v.IsZero() { + return + } + for i := 0; i < v.NumField(); i++ { + namePrefix := name + sTyp := v.Type().Field(i) + if proptools.ShouldSkipProperty(sTyp) { + continue + } + if name != "" && !strings.HasSuffix(namePrefix, ".") { + namePrefix += "." + } + if !proptools.IsEmbedded(sTyp) { + namePrefix += sTyp.Name + } + sVal := v.Field(i) + propsWithValues(namePrefix, sVal) + } + case reflect.Array, reflect.Slice: + if v.IsNil() { + return + } + elKind := v.Type().Elem().Kind() + info = append(info, propInfo{name, elKind.String() + " " + kind.String()}) + default: + info = append(info, propInfo{name, kind.String()}) + } + } + + for _, p := range props { + propsWithValues("", reflect.ValueOf(p).Elem()) + } + return info } func (m *ModuleBase) ComponentDepsMutator(BottomUpMutatorContext) {} diff --git a/android/module_test.go b/android/module_test.go index d9e2c87a4..c35e66ed6 100644 --- a/android/module_test.go +++ b/android/module_test.go @@ -615,3 +615,204 @@ func parseMkRules(t *testing.T, config Config, nodes []mkparser.Node) []installM return rules } + +type PropsTestModuleEmbedded struct { + Embedded_prop *string +} + +type propsTestModule struct { + ModuleBase + DefaultableModuleBase + props struct { + A string `android:"arch_variant"` + B *bool + C []string + } + otherProps struct { + PropsTestModuleEmbedded + + D *int64 + Nested struct { + E *string + } + F *string `blueprint:"mutated"` + } +} + +func propsTestModuleFactory() Module { + module := &propsTestModule{} + module.AddProperties(&module.props, &module.otherProps) + InitAndroidArchModule(module, HostAndDeviceSupported, MultilibBoth) + InitDefaultableModule(module) + return module +} + +type propsTestModuleDefaults struct { + ModuleBase + DefaultsModuleBase +} + +func propsTestModuleDefaultsFactory() Module { + defaults := &propsTestModuleDefaults{} + module := propsTestModule{} + defaults.AddProperties(&module.props, &module.otherProps) + InitDefaultsModule(defaults) + return defaults +} + +func (p *propsTestModule) GenerateAndroidBuildActions(ctx ModuleContext) { + str := "abc" + p.otherProps.F = &str +} + +func TestUsedProperties(t *testing.T) { + testCases := []struct { + desc string + bp string + expectedProps []propInfo + }{ + { + desc: "only name", + bp: `test { + name: "foo", + } + `, + expectedProps: []propInfo{ + propInfo{"Name", "string"}, + }, + }, + { + desc: "some props", + bp: `test { + name: "foo", + a: "abc", + b: true, + d: 123, + } + `, + expectedProps: []propInfo{ + propInfo{"A", "string"}, + propInfo{"B", "bool"}, + propInfo{"D", "int64"}, + propInfo{"Name", "string"}, + }, + }, + { + desc: "unused non-pointer prop", + bp: `test { + name: "foo", + b: true, + d: 123, + } + `, + expectedProps: []propInfo{ + // for non-pointer cannot distinguish between unused and intentionally set to empty + propInfo{"A", "string"}, + propInfo{"B", "bool"}, + propInfo{"D", "int64"}, + propInfo{"Name", "string"}, + }, + }, + { + desc: "nested props", + bp: `test { + name: "foo", + nested: { + e: "abc", + } + } + `, + expectedProps: []propInfo{ + propInfo{"Nested.E", "string"}, + propInfo{"Name", "string"}, + }, + }, + { + desc: "arch props", + bp: `test { + name: "foo", + arch: { + x86_64: { + a: "abc", + }, + } + } + `, + expectedProps: []propInfo{ + propInfo{"Name", "string"}, + propInfo{"Arch.X86_64.A", "string"}, + }, + }, + { + desc: "embedded props", + bp: `test { + name: "foo", + embedded_prop: "a", + } + `, + expectedProps: []propInfo{ + propInfo{"Embedded_prop", "string"}, + propInfo{"Name", "string"}, + }, + }, + { + desc: "defaults", + bp: ` +test_defaults { + name: "foo_defaults", + a: "a", + b: true, + embedded_prop:"a", + arch: { + x86_64: { + a: "a", + }, + }, +} +test { + name: "foo", + defaults: ["foo_defaults"], + c: ["a"], + nested: { + e: "d", + }, + target: { + linux: { + a: "a", + }, + }, +} + `, + expectedProps: []propInfo{ + propInfo{"A", "string"}, + propInfo{"B", "bool"}, + propInfo{"C", "string slice"}, + propInfo{"Embedded_prop", "string"}, + propInfo{"Nested.E", "string"}, + propInfo{"Name", "string"}, + propInfo{"Arch.X86_64.A", "string"}, + propInfo{"Target.Linux.A", "string"}, + propInfo{"Defaults", "string slice"}, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + result := GroupFixturePreparers( + PrepareForTestWithAllowMissingDependencies, + PrepareForTestWithDefaults, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("test", propsTestModuleFactory) + ctx.RegisterModuleType("test_defaults", propsTestModuleDefaultsFactory) + }), + FixtureWithRootAndroidBp(tc.bp), + ).RunTest(t) + + foo := result.ModuleForTests("foo", "").Module().base() + + AssertDeepEquals(t, "foo ", tc.expectedProps, foo.propertiesWithValues()) + + }) + } +} diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go index 573821749..82ebba7b1 100644 --- a/java/droidstubs_test.go +++ b/java/droidstubs_test.go @@ -244,17 +244,14 @@ func TestAddJSONData(t *testing.T) { } jsonData := map[string]interface{}{} prebuiltStubsSources.AddJSONData(&jsonData) - if fmt.Sprint(jsonData) != fmt.Sprint( + expectedOut := []map[string]interface{}{ map[string]interface{}{ - "Android": map[string]interface{}{}, - "Actions": []map[string]interface{}{ - map[string]interface{}{ - "Inputs": []string{}, - "Outputs": []string{}, - }, - }, - }) { - t.Errorf("The JSON data map isn't as expected %s.", jsonData) + "Inputs": []string{}, + "Outputs": []string{}, + }, + } + if !reflect.DeepEqual(jsonData["Actions"], expectedOut) { + t.Errorf("The JSON action data %#v isn't as expected %#v.", jsonData["Actions"], expectedOut) } }