Merge "Add jni_uses_sdk_apis" into rvc-dev
diff --git a/android/defaults.go b/android/defaults.go
index 6a908ea..81e340e 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -35,6 +35,9 @@
defaultsProperties defaultsProperties
defaultableProperties []interface{}
defaultableVariableProperties interface{}
+
+ // The optional hook to call after any defaults have been applied.
+ hook DefaultableHook
}
func (d *DefaultableModuleBase) defaults() *defaultsProperties {
@@ -46,6 +49,16 @@
d.defaultableVariableProperties = variableProperties
}
+func (d *DefaultableModuleBase) SetDefaultableHook(hook DefaultableHook) {
+ d.hook = hook
+}
+
+func (d *DefaultableModuleBase) callHookIfAvailable(ctx DefaultableHookContext) {
+ if d.hook != nil {
+ d.hook(ctx)
+ }
+}
+
// Interface that must be supported by any module to which defaults can be applied.
type Defaultable interface {
// Get a pointer to the struct containing the Defaults property.
@@ -57,6 +70,15 @@
// Apply defaults from the supplied Defaults to the property structures supplied to
// setProperties(...).
applyDefaults(TopDownMutatorContext, []Defaults)
+
+ // Set the hook to be called after any defaults have been applied.
+ //
+ // Should be used in preference to a AddLoadHook when the behavior of the load
+ // hook is dependent on properties supplied in the Android.bp file.
+ SetDefaultableHook(hook DefaultableHook)
+
+ // Call the hook if specified.
+ callHookIfAvailable(context DefaultableHookContext)
}
type DefaultableModule interface {
@@ -75,6 +97,15 @@
module.AddProperties(module.defaults())
}
+// A restricted subset of context methods, similar to LoadHookContext.
+type DefaultableHookContext interface {
+ EarlyModuleContext
+
+ CreateModule(ModuleFactory, ...interface{}) Module
+}
+
+type DefaultableHook func(ctx DefaultableHookContext)
+
// The Defaults_visibility property.
type DefaultsVisibilityProperties struct {
@@ -268,25 +299,29 @@
}
func defaultsMutator(ctx TopDownMutatorContext) {
- if defaultable, ok := ctx.Module().(Defaultable); ok && len(defaultable.defaults().Defaults) > 0 {
- var defaultsList []Defaults
- seen := make(map[Defaults]bool)
+ if defaultable, ok := ctx.Module().(Defaultable); ok {
+ if len(defaultable.defaults().Defaults) > 0 {
+ var defaultsList []Defaults
+ seen := make(map[Defaults]bool)
- ctx.WalkDeps(func(module, parent Module) bool {
- if ctx.OtherModuleDependencyTag(module) == DefaultsDepTag {
- if defaults, ok := module.(Defaults); ok {
- if !seen[defaults] {
- seen[defaults] = true
- defaultsList = append(defaultsList, defaults)
- return len(defaults.defaults().Defaults) > 0
+ ctx.WalkDeps(func(module, parent Module) bool {
+ if ctx.OtherModuleDependencyTag(module) == DefaultsDepTag {
+ if defaults, ok := module.(Defaults); ok {
+ if !seen[defaults] {
+ seen[defaults] = true
+ defaultsList = append(defaultsList, defaults)
+ return len(defaults.defaults().Defaults) > 0
+ }
+ } else {
+ ctx.PropertyErrorf("defaults", "module %s is not an defaults module",
+ ctx.OtherModuleName(module))
}
- } else {
- ctx.PropertyErrorf("defaults", "module %s is not an defaults module",
- ctx.OtherModuleName(module))
}
- }
- return false
- })
- defaultable.applyDefaults(ctx, defaultsList)
+ return false
+ })
+ defaultable.applyDefaults(ctx, defaultsList)
+ }
+
+ defaultable.callHookIfAvailable(ctx)
}
}
diff --git a/android/hooks.go b/android/hooks.go
index 47f69d1..f43a007 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -39,6 +39,12 @@
moduleFactories() map[string]blueprint.ModuleFactory
}
+// Add a hook that will be called once the module has been loaded, i.e. its
+// properties have been initialized from the Android.bp file.
+//
+// Consider using SetDefaultableHook to register a hook for any module that implements
+// DefaultableModule as the hook is called after any defaults have been applied to the
+// module which could reduce duplication and make it easier to use.
func AddLoadHook(m blueprint.Module, hook func(LoadHookContext)) {
blueprint.AddLoadHook(m, func(ctx blueprint.LoadHookContext) {
actx := &loadHookContext{
diff --git a/android/mutator.go b/android/mutator.go
index 247eb4d..77d5f43 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -82,18 +82,6 @@
var preArch = []RegisterMutatorFunc{
RegisterNamespaceMutator,
- // Rename package module types.
- //
- // The package module type does not have a name property, instead its name is determined
- // by the location of the containing .bp file relative to the root of the file structure
- // being built by Soong. Unfortunately, due to limitations in LoadHook the module has to
- // be given a synthetic temporary name which is then fixed up by these mutators.
- RegisterPackageRenamer,
-
- // Create an association between prebuilt modules and their corresponding source
- // modules (if any).
- RegisterPrebuiltsPreArchMutators,
-
// Check the visibility rules are valid.
//
// This must run after the package renamer mutators so that any issues found during
@@ -122,8 +110,19 @@
RegisterVisibilityRuleChecker,
// Apply properties from defaults modules to the referencing modules.
+ //
+ // Any mutators that are added before this will not see any modules created by
+ // a DefaultableHook.
RegisterDefaultsPreArchMutators,
+ // Create an association between prebuilt modules and their corresponding source
+ // modules (if any).
+ //
+ // Must be run after defaults mutators to ensure that any modules created by
+ // a DefaultableHook can be either a prebuilt or a source module with a matching
+ // prebuilt.
+ RegisterPrebuiltsPreArchMutators,
+
// Gather the visibility rules for all modules for us during visibility enforcement.
//
// This must come after the defaults mutators to ensure that any visibility supplied
diff --git a/android/package.go b/android/package.go
index bb5f4e7..182b3ed 100644
--- a/android/package.go
+++ b/android/package.go
@@ -15,43 +15,20 @@
package android
import (
- "fmt"
- "sync/atomic"
-
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
)
func init() {
RegisterPackageBuildComponents(InitRegistrationContext)
}
-// Register the package module type and supporting mutators.
-//
-// This must be called in the correct order (relative to other methods that also
-// register mutators) to match the order of mutator registration in mutator.go.
-// Failing to do so will result in an unrealistic test environment.
+// Register the package module type.
func RegisterPackageBuildComponents(ctx RegistrationContext) {
ctx.RegisterModuleType("package", PackageFactory)
-
- // Register mutators that are hard coded in to mutator.go.
- ctx.HardCodedPreArchMutators(RegisterPackageRenamer)
-}
-
-// The information maintained about each package.
-type packageInfo struct {
- // The module from which this information was populated. If `duplicated` = true then this is the
- // module that has been renamed and must be used to report errors.
- module *packageModule
-
- // If true this indicates that there are two package statements in the same package which is not
- // allowed and will cause the build to fail. This flag is set by packageRenamer and checked in
- // packageErrorReporter
- duplicated bool
}
type packageProperties struct {
- Name string `blueprint:"mutated"`
-
// Specifies the default visibility for all modules defined in this package.
Default_visibility []string
}
@@ -59,8 +36,7 @@
type packageModule struct {
ModuleBase
- properties packageProperties
- packageInfo *packageInfo
+ properties packageProperties
}
func (p *packageModule) GenerateAndroidBuildActions(ModuleContext) {
@@ -76,126 +52,21 @@
return newPackageId(ctx.ModuleDir())
}
-func (p *packageModule) Name() string {
- return p.properties.Name
-}
-
-func (p *packageModule) setName(name string) {
- p.properties.Name = name
-}
-
-// Counter to ensure package modules are created with a unique name within whatever namespace they
-// belong.
-var packageCount uint32 = 0
-
func PackageFactory() Module {
module := &packageModule{}
- // Get a unique if for the package. Has to be done atomically as the creation of the modules are
- // done in parallel.
- id := atomic.AddUint32(&packageCount, 1)
- name := fmt.Sprintf("soong_package_%d", id)
-
- module.properties.Name = name
-
module.AddProperties(&module.properties)
+ // The name is the relative path from build root to the directory containing this
+ // module. Set that name at the earliest possible moment that information is available
+ // which is in a LoadHook.
+ AddLoadHook(module, func(ctx LoadHookContext) {
+ module.nameProperties.Name = proptools.StringPtr("//" + ctx.ModuleDir())
+ })
+
// The default_visibility property needs to be checked and parsed by the visibility module during
// its checking and parsing phases so make it the primary visibility property.
setPrimaryVisibilityProperty(module, "default_visibility", &module.properties.Default_visibility)
return module
}
-
-// Registers the function that renames the packages.
-func RegisterPackageRenamer(ctx RegisterMutatorsContext) {
- ctx.BottomUp("packageRenamer", packageRenamer).Parallel()
- ctx.BottomUp("packageErrorReporter", packageErrorReporter).Parallel()
-}
-
-// Renames the package to match the package directory.
-//
-// This also creates a PackageInfo object for each package and uses that to detect and remember
-// duplicates for later error reporting.
-func packageRenamer(ctx BottomUpMutatorContext) {
- m, ok := ctx.Module().(*packageModule)
- if !ok {
- return
- }
-
- packageName := "//" + ctx.ModuleDir()
-
- pi := newPackageInfo(ctx, packageName, m)
- if pi.module != m {
- // Remember that the package was duplicated but do not rename as that will cause an error to
- // be logged with the generated name. Similarly, reporting the error here will use the generated
- // name as renames are only processed after this phase.
- pi.duplicated = true
- } else {
- // This is the first package module in this package so rename it to match the package name.
- m.setName(packageName)
- ctx.Rename(packageName)
-
- // Store a package info reference in the module.
- m.packageInfo = pi
- }
-}
-
-// Logs any deferred errors.
-func packageErrorReporter(ctx BottomUpMutatorContext) {
- m, ok := ctx.Module().(*packageModule)
- if !ok {
- return
- }
-
- packageDir := ctx.ModuleDir()
- packageName := "//" + packageDir
-
- // Get the PackageInfo for the package. Should have been populated in the packageRenamer phase.
- pi := findPackageInfo(ctx, packageName)
- if pi == nil {
- ctx.ModuleErrorf("internal error, expected package info to be present for package '%s'",
- packageName)
- return
- }
-
- if pi.module != m {
- // The package module has been duplicated but this is not the module that has been renamed so
- // ignore it. An error will be logged for the renamed module which will ensure that the error
- // message uses the correct name.
- return
- }
-
- // Check to see whether there are duplicate package modules in the package.
- if pi.duplicated {
- ctx.ModuleErrorf("package {...} specified multiple times")
- return
- }
-}
-
-type defaultPackageInfoKey string
-
-func newPackageInfo(
- ctx BaseModuleContext, packageName string, module *packageModule) *packageInfo {
- key := NewCustomOnceKey(defaultPackageInfoKey(packageName))
-
- return ctx.Config().Once(key, func() interface{} {
- return &packageInfo{module: module}
- }).(*packageInfo)
-}
-
-// Get the PackageInfo for the package name (starts with //, no trailing /), is nil if no package
-// module type was specified.
-func findPackageInfo(ctx BaseModuleContext, packageName string) *packageInfo {
- key := NewCustomOnceKey(defaultPackageInfoKey(packageName))
-
- pi := ctx.Config().Once(key, func() interface{} {
- return nil
- })
-
- if pi == nil {
- return nil
- } else {
- return pi.(*packageInfo)
- }
-}
diff --git a/android/package_test.go b/android/package_test.go
index 4710e39..04dfc08 100644
--- a/android/package_test.go
+++ b/android/package_test.go
@@ -20,7 +20,7 @@
}`),
},
expectedErrors: []string{
- `top/Blueprints:3:10: mutated field name cannot be set in a Blueprint file`,
+ `top/Blueprints:3:10: unrecognized property "name"`,
`top/Blueprints:4:16: unrecognized property "visibility"`,
},
},
@@ -50,7 +50,7 @@
}`),
},
expectedErrors: []string{
- `module "//top": package {...} specified multiple times`,
+ `module "//top" already defined`,
},
},
}
diff --git a/android/visibility_test.go b/android/visibility_test.go
index 8dd6a8f..4cf41a6 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -903,6 +903,69 @@
}`),
},
},
+ {
+ name: "ensure visibility properties are checked for correctness",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ mock_parent {
+ name: "parent",
+ visibility: ["//top/nested"],
+ child: {
+ name: "libchild",
+ visibility: ["top/other"],
+ },
+ }`),
+ },
+ expectedErrors: []string{
+ `module "parent": child.visibility: invalid visibility pattern "top/other"`,
+ },
+ },
+ {
+ name: "invalid visibility added to child detected during gather phase",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ mock_parent {
+ name: "parent",
+ visibility: ["//top/nested"],
+ child: {
+ name: "libchild",
+ invalid_visibility: ["top/other"],
+ },
+ }`),
+ },
+ expectedErrors: []string{
+ // That this error is reported against the child not the parent shows it was
+ // not being detected in the parent which is correct as invalid_visibility is
+ // purposely not added to the list of visibility properties to check, and was
+ // in fact detected in the child in the gather phase. Contrast this error message
+ // with the preceding one.
+ `module "libchild" \(created by module "parent"\): visibility: invalid visibility pattern "top/other"`,
+ },
+ },
+ {
+ name: "automatic visibility inheritance enabled",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ mock_parent {
+ name: "parent",
+ visibility: ["//top/nested"],
+ child: {
+ name: "libchild",
+ visibility: ["//top/other"],
+ },
+ }`),
+ "top/nested/Blueprints": []byte(`
+ mock_library {
+ name: "libnested",
+ deps: ["libchild"],
+ }`),
+ "top/other/Blueprints": []byte(`
+ mock_library {
+ name: "libother",
+ deps: ["libchild"],
+ }`),
+ },
+ },
}
func TestVisibility(t *testing.T) {
@@ -936,6 +999,7 @@
ctx := NewTestArchContext()
ctx.RegisterModuleType("mock_library", newMockLibraryModule)
+ ctx.RegisterModuleType("mock_parent", newMockParentFactory)
ctx.RegisterModuleType("mock_defaults", defaultsFactory)
// Order of the following method calls is significant.
@@ -996,3 +1060,42 @@
InitDefaultsModule(m)
return m
}
+
+type mockParentProperties struct {
+ Child struct {
+ Name *string
+
+ // Visibility to pass to the child module.
+ Visibility []string
+
+ // Purposely not validated visibility to pass to the child.
+ Invalid_visibility []string
+ }
+}
+
+type mockParent struct {
+ ModuleBase
+ DefaultableModuleBase
+ properties mockParentProperties
+}
+
+func (p *mockParent) GenerateAndroidBuildActions(ModuleContext) {
+}
+
+func newMockParentFactory() Module {
+ m := &mockParent{}
+ m.AddProperties(&m.properties)
+ InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon)
+ InitDefaultableModule(m)
+ AddVisibilityProperty(m, "child.visibility", &m.properties.Child.Visibility)
+
+ m.SetDefaultableHook(func(ctx DefaultableHookContext) {
+ visibility := m.properties.Child.Visibility
+ visibility = append(visibility, m.properties.Child.Invalid_visibility...)
+ ctx.CreateModule(newMockLibraryModule, &struct {
+ Name *string
+ Visibility []string
+ }{m.properties.Child.Name, visibility})
+ })
+ return m
+}
diff --git a/apex/apex.go b/apex/apex.go
index 45ba908..57fc650 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -226,7 +226,6 @@
"netd_aidl_interface-unstable-java",
"netd_event_listener_interface-java",
"netlink-client",
- "networkstack-aidl-interfaces-unstable-java",
"networkstack-client",
"sap-api-java-static",
"services.net",
@@ -682,7 +681,6 @@
"netd_aidl_interface-unstable-java",
"netd_event_listener_interface-java",
"netlink-client",
- "networkstack-aidl-interfaces-unstable-java",
"networkstack-client",
"services.net",
"wifi-lite-protos",
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 11a8bd2..a99d2ea 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4361,12 +4361,12 @@
ctx.RegisterModuleType("apex", BundleFactory)
ctx.RegisterModuleType("apex_key", ApexKeyFactory)
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+ ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
cc.RegisterRequiredBuildComponentsForTest(ctx)
java.RegisterJavaBuildComponents(ctx)
java.RegisterSystemModulesBuildComponents(ctx)
java.RegisterAppBuildComponents(ctx)
java.RegisterDexpreoptBootJarsComponents(ctx)
- ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
ctx.PreDepsMutators(RegisterPreDepsMutators)
ctx.PostDepsMutators(RegisterPostDepsMutators)
diff --git a/cc/testing.go b/cc/testing.go
index 53f0995..6119920 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -485,8 +485,8 @@
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
ctx.RegisterModuleType("vndk_libraries_txt", VndkLibrariesTxtFactory)
- RegisterRequiredBuildComponentsForTest(ctx)
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+ RegisterRequiredBuildComponentsForTest(ctx)
ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
diff --git a/java/app.go b/java/app.go
index c068c46..4c4b83c 100755
--- a/java/app.go
+++ b/java/app.go
@@ -1394,7 +1394,8 @@
module.processVariants(ctx)
})
- InitJavaModule(module, android.DeviceSupported)
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
return module
diff --git a/java/sdk_library.go b/java/sdk_library.go
index d9c948f..3a6c5de 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -67,6 +67,9 @@
// The name of the api scope, e.g. public, system, test
name string
+ // The api scope that this scope extends.
+ extends *apiScope
+
// The name of the field in the dynamically created structure.
fieldName string
@@ -134,6 +137,7 @@
})
apiScopeSystem = initApiScope(&apiScope{
name: "system",
+ extends: apiScopePublic,
apiFilePrefix: "system-",
moduleSuffix: sdkSystemApiSuffix,
sdkVersion: "system_current",
@@ -141,6 +145,7 @@
})
apiScopeTest = initApiScope(&apiScope{
name: "test",
+ extends: apiScopePublic,
apiFilePrefix: "test-",
moduleSuffix: sdkTestApiSuffix,
sdkVersion: "test_current",
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index fde9230..2f125e2 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -242,9 +242,13 @@
EmbeddedPropertiesStruct
}
+func (p *testPropertiesStruct) optimizableProperties() interface{} {
+ return p
+}
+
func TestCommonValueOptimization(t *testing.T) {
common := &testPropertiesStruct{}
- structs := []*testPropertiesStruct{
+ structs := []propertiesContainer{
&testPropertiesStruct{
private: "common",
Public_Kept: "common",
diff --git a/sdk/update.go b/sdk/update.go
index e14347f..ae74b9d 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -301,18 +301,18 @@
addHostDeviceSupportedProperties(s.ModuleBase.DeviceSupported(), s.ModuleBase.HostSupported(), snapshotModule)
- var dynamicMemberPropertiesList []interface{}
+ var dynamicMemberPropertiesContainers []propertiesContainer
osTypeToMemberProperties := make(map[android.OsType]*sdk)
for _, sdkVariant := range sdkVariants {
properties := sdkVariant.dynamicMemberTypeListProperties
osTypeToMemberProperties[sdkVariant.Target().Os] = sdkVariant
- dynamicMemberPropertiesList = append(dynamicMemberPropertiesList, properties)
+ dynamicMemberPropertiesContainers = append(dynamicMemberPropertiesContainers, &dynamicMemberPropertiesContainer{properties})
}
// Extract the common lists of members into a separate struct.
commonDynamicMemberProperties := s.dynamicSdkMemberTypes.createMemberListProperties()
extractor := newCommonValueExtractor(commonDynamicMemberProperties)
- extractor.extractCommonProperties(commonDynamicMemberProperties, dynamicMemberPropertiesList)
+ extractor.extractCommonProperties(commonDynamicMemberProperties, dynamicMemberPropertiesContainers)
// Add properties common to all os types.
s.addMemberPropertiesToPropertySet(builder, snapshotModule, commonDynamicMemberProperties)
@@ -814,6 +814,10 @@
Properties android.SdkMemberProperties
}
+func (b *baseInfo) optimizableProperties() interface{} {
+ return b.Properties
+}
+
type osTypeSpecificInfo struct {
baseInfo
@@ -889,17 +893,14 @@
}
multilib := multilibNone
- var archPropertiesList []android.SdkMemberProperties
for _, archInfo := range osInfo.archInfos {
multilib = multilib.addArchType(archInfo.archType)
// Optimize the arch properties first.
archInfo.optimizeProperties(commonValueExtractor)
-
- archPropertiesList = append(archPropertiesList, archInfo.Properties)
}
- commonValueExtractor.extractCommonProperties(osInfo.Properties, archPropertiesList)
+ commonValueExtractor.extractCommonProperties(osInfo.Properties, osInfo.archInfos)
// Choose setting for compile_multilib that is appropriate for the arch variants supplied.
osInfo.Properties.Base().Compile_multilib = multilib.String()
@@ -1011,6 +1012,10 @@
return archInfo
}
+func (archInfo *archTypeSpecificInfo) optimizableProperties() interface{} {
+ return archInfo.Properties
+}
+
// Get the link type of the variant
//
// If the variant is not differentiated by link type then it returns "",
@@ -1038,12 +1043,7 @@
return
}
- var propertiesList []android.SdkMemberProperties
- for _, linkInfo := range archInfo.linkInfos {
- propertiesList = append(propertiesList, linkInfo.Properties)
- }
-
- commonValueExtractor.extractCommonProperties(archInfo.Properties, propertiesList)
+ commonValueExtractor.extractCommonProperties(archInfo.Properties, archInfo.linkInfos)
}
// Add the properties for an arch type to a property set.
@@ -1133,21 +1133,21 @@
// The list of property structures which are os type specific but common across
// architectures within that os type.
- var osSpecificPropertiesList []android.SdkMemberProperties
+ var osSpecificPropertiesContainers []*osTypeSpecificInfo
for osType, osTypeVariants := range variantsByOsType {
osInfo := newOsTypeSpecificInfo(ctx, osType, variantPropertiesFactory, osTypeVariants)
osTypeToInfo[osType] = osInfo
// Add the os specific properties to a list of os type specific yet architecture
// independent properties structs.
- osSpecificPropertiesList = append(osSpecificPropertiesList, osInfo.Properties)
+ osSpecificPropertiesContainers = append(osSpecificPropertiesContainers, osInfo)
// Optimize the properties across all the variants for a specific os type.
osInfo.optimizeProperties(commonValueExtractor)
}
// Extract properties which are common across all architectures and os types.
- commonValueExtractor.extractCommonProperties(commonProperties, osSpecificPropertiesList)
+ commonValueExtractor.extractCommonProperties(commonProperties, osSpecificPropertiesContainers)
// Add the common properties to the module.
commonProperties.AddToPropertySet(ctx, bpModule)
@@ -1186,15 +1186,24 @@
return osTypes
}
-// Given a struct value, access a field within that struct (or one of its embedded
-// structs).
+// Given a set of properties (struct value), return the value of the field within that
+// struct (or one of its embedded structs).
type fieldAccessorFunc func(structValue reflect.Value) reflect.Value
+// A property that can be optimized by the commonValueExtractor.
+type extractorProperty struct {
+ // Retrieves the value on which common value optimization will be performed.
+ getter fieldAccessorFunc
+
+ // The empty value for the field.
+ emptyValue reflect.Value
+}
+
// Supports extracting common values from a number of instances of a properties
// structure into a separate common set of properties.
type commonValueExtractor struct {
- // The getters for every field from which common values can be extracted.
- fieldGetters []fieldAccessorFunc
+ // The properties that the extractor can optimize.
+ properties []extractorProperty
}
// Create a new common value extractor for the structure type for the supplied
@@ -1249,7 +1258,11 @@
// Gather fields from the embedded structure.
e.gatherFields(field.Type, fieldGetter)
} else {
- e.fieldGetters = append(e.fieldGetters, fieldGetter)
+ property := extractorProperty{
+ fieldGetter,
+ reflect.Zero(field.Type),
+ }
+ e.properties = append(e.properties, property)
}
}
}
@@ -1270,11 +1283,29 @@
return value
}
+// A container of properties to be optimized.
+//
+// Allows additional information to be associated with the properties, e.g. for
+// filtering.
+type propertiesContainer interface {
+ // Get the properties that need optimizing.
+ optimizableProperties() interface{}
+}
+
+// A wrapper for dynamic member properties to allow them to be optimized.
+type dynamicMemberPropertiesContainer struct {
+ dynamicMemberProperties interface{}
+}
+
+func (c dynamicMemberPropertiesContainer) optimizableProperties() interface{} {
+ return c.dynamicMemberProperties
+}
+
// Extract common properties from a slice of property structures of the same type.
//
// All the property structures must be of the same type.
// commonProperties - must be a pointer to the structure into which common properties will be added.
-// inputPropertiesSlice - must be a slice of input properties structures.
+// inputPropertiesSlice - must be a slice of propertiesContainer interfaces.
//
// Iterates over each exported field (capitalized name) and checks to see whether they
// have the same value (using DeepEquals) across all the input properties. If it does not then no
@@ -1284,15 +1315,19 @@
commonPropertiesValue := reflect.ValueOf(commonProperties)
commonStructValue := commonPropertiesValue.Elem()
- for _, fieldGetter := range e.fieldGetters {
+ sliceValue := reflect.ValueOf(inputPropertiesSlice)
+
+ for _, property := range e.properties {
+ fieldGetter := property.getter
+
// Check to see if all the structures have the same value for the field. The commonValue
// is nil on entry to the loop and if it is nil on exit then there is no common value,
// otherwise it points to the common value.
var commonValue *reflect.Value
- sliceValue := reflect.ValueOf(inputPropertiesSlice)
for i := 0; i < sliceValue.Len(); i++ {
- itemValue := sliceValue.Index(i)
+ container := sliceValue.Index(i).Interface().(propertiesContainer)
+ itemValue := reflect.ValueOf(container.optimizableProperties())
fieldValue := fieldGetter(itemValue)
if commonValue == nil {
@@ -1311,10 +1346,11 @@
// If the fields all have a common value then store it in the common struct field
// and set the input struct's field to the empty value.
if commonValue != nil {
- emptyValue := reflect.Zero(commonValue.Type())
+ emptyValue := property.emptyValue
fieldGetter(commonStructValue).Set(*commonValue)
for i := 0; i < sliceValue.Len(); i++ {
- itemValue := sliceValue.Index(i)
+ container := sliceValue.Index(i).Interface().(propertiesContainer)
+ itemValue := reflect.ValueOf(container.optimizableProperties())
fieldValue := fieldGetter(itemValue)
fieldValue.Set(emptyValue)
}