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

Commit 9525e710 authored by Liz Kammer's avatar Liz Kammer
Browse files

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
parent ed4900f9
Loading
Loading
Loading
Loading
+59 −1
Original line number Diff line number Diff line
@@ -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) {}
+201 −0
Original line number Diff line number Diff line
@@ -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())

		})
	}
}
+7 −10
Original line number Diff line number Diff line
@@ -244,17 +244,14 @@ func TestAddJSONData(t *testing.T) {
	}
	jsonData := map[string]interface{}{}
	prebuiltStubsSources.AddJSONData(&jsonData)
	if fmt.Sprint(jsonData) != fmt.Sprint(
		map[string]interface{}{
			"Android": map[string]interface{}{},
			"Actions": []map[string]interface{}{
	expectedOut := []map[string]interface{}{
		map[string]interface{}{
			"Inputs":  []string{},
			"Outputs": []string{},
		},
			},
		}) {
		t.Errorf("The JSON data map isn't as expected %s.", jsonData)
	}
	if !reflect.DeepEqual(jsonData["Actions"], expectedOut) {
		t.Errorf("The JSON action data %#v isn't as expected %#v.", jsonData["Actions"], expectedOut)
	}
}