From 9525e71003bb1bc17301b04894e2b3cc715772ca Mon Sep 17 00:00:00 2001 From: Liz Kammer Date: Wed, 5 Jan 2022 13:46:24 -0500 Subject: [PATCH] Add SetProperties to json-module-graph SetProperties contains name and type of properties set in the bp file and any set via defaults. There may be properties that were not specified in an Android.bp file due to: * specified via go code (e.g. LoadHooks) * property is _not_ a pointer -- so it is not possible to tell between not set in bp file and default value. Test: soong tests Test: m json-module-graph and verify Change-Id: I4cb868b1d7db566e72636c6fb53bb9c7090f236a --- android/module.go | 60 +++++++++++- android/module_test.go | 201 ++++++++++++++++++++++++++++++++++++++++ java/droidstubs_test.go | 17 ++-- 3 files changed, 267 insertions(+), 11 deletions(-) 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) } }