Merge "Support extracting common values from embedded structures" am: 0a4c18e837

Change-Id: I7bf0483f32080def2c7547774dec727fd66d68e7
This commit is contained in:
Automerger Merge Worker
2020-03-13 13:57:06 +00:00
4 changed files with 141 additions and 10 deletions

View File

@@ -325,7 +325,8 @@ type SdkMemberType interface {
// //
// * The variant property structs are analysed to find exported (capitalized) fields which // * The variant property structs are analysed to find exported (capitalized) fields which
// have common values. Those fields are cleared and the common value added to the common // have common values. Those fields are cleared and the common value added to the common
// properties. // properties. A field annotated with a tag of `sdk:"keep"` will be treated as if it
// was not capitalized, i.e. not optimized for common values.
// //
// * The sdk module type populates the BpModule structure, creating the arch specific // * The sdk module type populates the BpModule structure, creating the arch specific
// structure and calls AddToPropertySet(...) on the properties struct to add the member // structure and calls AddToPropertySet(...) on the properties struct to add the member
@@ -452,13 +453,13 @@ func RegisterSdkMemberType(memberType SdkMemberType) {
// are not affected by the optimization to extract common values. // are not affected by the optimization to extract common values.
type SdkMemberPropertiesBase struct { type SdkMemberPropertiesBase struct {
// The setting to use for the compile_multilib property. // The setting to use for the compile_multilib property.
Compile_multilib string Compile_multilib string `sdk:"keep"`
// The number of unique os types supported by the member variants. // The number of unique os types supported by the member variants.
Os_count int Os_count int `sdk:"keep"`
// The os type for which these properties refer. // The os type for which these properties refer.
Os OsType Os OsType `sdk:"keep"`
} }
// The os prefix to use for any file paths in the sdk. // The os prefix to use for any file paths in the sdk.

View File

@@ -16,6 +16,8 @@ package sdk
import ( import (
"testing" "testing"
"github.com/google/blueprint/proptools"
) )
// Needed in an _test.go file in this package to ensure tests run correctly, particularly in IDE. // Needed in an _test.go file in this package to ensure tests run correctly, particularly in IDE.
@@ -222,3 +224,106 @@ func TestSDkInstall(t *testing.T) {
checkAllOtherCopyRules(`.intermediates/mysdk/common_os/mysdk-current.zip -> mysdk-current.zip`), checkAllOtherCopyRules(`.intermediates/mysdk/common_os/mysdk-current.zip -> mysdk-current.zip`),
) )
} }
type EmbeddedPropertiesStruct struct {
S_Embedded_Common string
S_Embedded_Different string
}
type testPropertiesStruct struct {
private string
Public_Kept string `sdk:"keep"`
S_Common string
S_Different string
A_Common []string
A_Different []string
F_Common *bool
F_Different *bool
EmbeddedPropertiesStruct
}
func TestCommonValueOptimization(t *testing.T) {
common := &testPropertiesStruct{}
structs := []*testPropertiesStruct{
&testPropertiesStruct{
private: "common",
Public_Kept: "common",
S_Common: "common",
S_Different: "upper",
A_Common: []string{"first", "second"},
A_Different: []string{"alpha", "beta"},
F_Common: proptools.BoolPtr(false),
F_Different: proptools.BoolPtr(false),
EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
S_Embedded_Common: "embedded_common",
S_Embedded_Different: "embedded_upper",
},
},
&testPropertiesStruct{
private: "common",
Public_Kept: "common",
S_Common: "common",
S_Different: "lower",
A_Common: []string{"first", "second"},
A_Different: []string{"alpha", "delta"},
F_Common: proptools.BoolPtr(false),
F_Different: proptools.BoolPtr(true),
EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
S_Embedded_Common: "embedded_common",
S_Embedded_Different: "embedded_lower",
},
},
}
extractor := newCommonValueExtractor(common)
extractor.extractCommonProperties(common, structs)
h := TestHelper{t}
h.AssertDeepEquals("common properties not correct", common,
&testPropertiesStruct{
private: "",
Public_Kept: "",
S_Common: "common",
S_Different: "",
A_Common: []string{"first", "second"},
A_Different: []string(nil),
F_Common: proptools.BoolPtr(false),
F_Different: nil,
EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
S_Embedded_Common: "embedded_common",
S_Embedded_Different: "",
},
})
h.AssertDeepEquals("updated properties[0] not correct", structs[0],
&testPropertiesStruct{
private: "common",
Public_Kept: "common",
S_Common: "",
S_Different: "upper",
A_Common: nil,
A_Different: []string{"alpha", "beta"},
F_Common: nil,
F_Different: proptools.BoolPtr(false),
EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
S_Embedded_Common: "",
S_Embedded_Different: "embedded_upper",
},
})
h.AssertDeepEquals("updated properties[1] not correct", structs[1],
&testPropertiesStruct{
private: "common",
Public_Kept: "common",
S_Common: "",
S_Different: "lower",
A_Common: nil,
A_Different: []string{"alpha", "delta"},
F_Common: nil,
F_Different: proptools.BoolPtr(true),
EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
S_Embedded_Common: "",
S_Embedded_Different: "embedded_lower",
},
})
}

View File

@@ -19,6 +19,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"strings" "strings"
"testing" "testing"
@@ -176,6 +177,13 @@ func (h *TestHelper) AssertTrimmedStringEquals(message string, expected string,
h.AssertStringEquals(message, strings.TrimSpace(expected), strings.TrimSpace(actual)) h.AssertStringEquals(message, strings.TrimSpace(expected), strings.TrimSpace(actual))
} }
func (h *TestHelper) AssertDeepEquals(message string, expected interface{}, actual interface{}) {
h.t.Helper()
if !reflect.DeepEqual(actual, expected) {
h.t.Errorf("%s: expected %#v, actual %#v", message, expected, actual)
}
}
// Encapsulates result of processing an SDK definition. Provides support for // Encapsulates result of processing an SDK definition. Provides support for
// checking the state of the build structures. // checking the state of the build structures.
type testSdkResult struct { type testSdkResult struct {

View File

@@ -951,7 +951,8 @@ func (s *sdk) getPossibleOsTypes() []android.OsType {
return osTypes return osTypes
} }
// Given a struct value, access a field within that struct. // Given a struct value, access a field within that struct (or one of its embedded
// structs).
type fieldAccessorFunc func(structValue reflect.Value) reflect.Value type fieldAccessorFunc func(structValue reflect.Value) reflect.Value
// Supports extracting common values from a number of instances of a properties // Supports extracting common values from a number of instances of a properties
@@ -969,13 +970,18 @@ type commonValueExtractor struct {
func newCommonValueExtractor(propertiesStruct interface{}) *commonValueExtractor { func newCommonValueExtractor(propertiesStruct interface{}) *commonValueExtractor {
structType := getStructValue(reflect.ValueOf(propertiesStruct)).Type() structType := getStructValue(reflect.ValueOf(propertiesStruct)).Type()
extractor := &commonValueExtractor{} extractor := &commonValueExtractor{}
extractor.gatherFields(structType) extractor.gatherFields(structType, nil)
return extractor return extractor
} }
// Gather the fields from the supplied structure type from which common values will // Gather the fields from the supplied structure type from which common values will
// be extracted. // be extracted.
func (e *commonValueExtractor) gatherFields(structType reflect.Type) { //
// This is recursive function. If it encounters an embedded field (no field name)
// that is a struct then it will recurse into that struct passing in the accessor
// for the field. That will then be used in the accessors for the fields in the
// embedded struct.
func (e *commonValueExtractor) gatherFields(structType reflect.Type, containingStructAccessor fieldAccessorFunc) {
for f := 0; f < structType.NumField(); f++ { for f := 0; f < structType.NumField(); f++ {
field := structType.Field(f) field := structType.Field(f)
if field.PkgPath != "" { if field.PkgPath != "" {
@@ -983,14 +989,20 @@ func (e *commonValueExtractor) gatherFields(structType reflect.Type) {
continue continue
} }
// Ignore embedded structures. // Ignore fields whose value should be kept.
if field.Type.Kind() == reflect.Struct && field.Anonymous { if proptools.HasTag(field, "sdk", "keep") {
continue continue
} }
// Save a copy of the field index for use in the function. // Save a copy of the field index for use in the function.
fieldIndex := f fieldIndex := f
fieldGetter := func(value reflect.Value) reflect.Value { fieldGetter := func(value reflect.Value) reflect.Value {
if containingStructAccessor != nil {
// This is an embedded structure so first access the field for the embedded
// structure.
value = containingStructAccessor(value)
}
// Skip through interface and pointer values to find the structure. // Skip through interface and pointer values to find the structure.
value = getStructValue(value) value = getStructValue(value)
@@ -998,7 +1010,12 @@ func (e *commonValueExtractor) gatherFields(structType reflect.Type) {
return value.Field(fieldIndex) return value.Field(fieldIndex)
} }
e.fieldGetters = append(e.fieldGetters, fieldGetter) if field.Type.Kind() == reflect.Struct && field.Anonymous {
// Gather fields from the embedded structure.
e.gatherFields(field.Type, fieldGetter)
} else {
e.fieldGetters = append(e.fieldGetters, fieldGetter)
}
} }
} }