Decouple addition of new sdk member types from sdk code

Previously, adding a new SdkMemberType would require adding a new
sdkMemberListProperty instance to the sdkMemberListProperties as well
as adding a new property into the sdkProperties struct. They are
potential sources of conflict and couple the sdk code with all the
packages that add members to it. This change switched to a
registration model that allows each package to register its sdk
member types decoupling them from the sdk code.

Adds an SdkPropertyName() method to SdkMemberType that specifies the
name of the property to use in the sdk/sdk_snapshot. Also provides
an SdkMemberTypeBase struct to be used by providers of SdkMemberType
implementations.

SdkMemberType instances are registered using the
RegisterSdkMemberType() func which sorts the registered instances
by their SdkPropertyName() to ensure the behavior is consistent and
not affected by order of registration.

When creating a new sdk module a dynamicSdkMemberTypes instance is
created that contains the following:

* A properties struct is created dynamically that contains a field for
  each registered SdkMemberType, corresponding to that type's
  SdkPropertyName().

* A list of sdkMemberListProperty instances is also created, one for
  each registered SdkMemberType.

The dynamicSdkMemberTypes instance is cached using a key that uniquely
identifies the set of registered types just in case new types are
registered after one has been created, e.g. by tests.

Bug: 142918168
Test: m checkbuild
Change-Id: I4bf2bf56a2a49025aa41454048bc1e8ccc6baca2
This commit is contained in:
Paul Duffin
2019-12-13 11:22:16 +00:00
parent 2f6bc09fee
commit 255f18e584
6 changed files with 249 additions and 102 deletions

View File

@@ -15,6 +15,7 @@
package android package android
import ( import (
"sort"
"strings" "strings"
"github.com/google/blueprint" "github.com/google/blueprint"
@@ -218,18 +219,24 @@ type SdkMember interface {
// The basic implementation should look something like this, where ModuleType is // The basic implementation should look something like this, where ModuleType is
// the name of the module type being supported. // the name of the module type being supported.
// //
// var ModuleTypeSdkMemberType = newModuleTypeSdkMemberType() // type moduleTypeSdkMemberType struct {
// // android.SdkMemberTypeBase
// func newModuleTypeSdkMemberType() android.SdkMemberType {
// return &moduleTypeSdkMemberType{}
// } // }
// //
// type moduleTypeSdkMemberType struct { // func init() {
// android.RegisterSdkMemberType(&moduleTypeSdkMemberType{
// SdkMemberTypeBase: android.SdkMemberTypeBase{
// PropertyName: "module_types",
// },
// }
// } // }
// //
// ...methods... // ...methods...
// //
type SdkMemberType interface { type SdkMemberType interface {
// The name of the member type property on an sdk module.
SdkPropertyName() string
// Add dependencies from the SDK module to all the variants the member // Add dependencies from the SDK module to all the variants the member
// contributes to the SDK. The exact set of variants required is determined // contributes to the SDK. The exact set of variants required is determined
// by the SDK and its properties. The dependencies must be added with the // by the SDK and its properties. The dependencies must be added with the
@@ -254,3 +261,66 @@ type SdkMemberType interface {
// IsInstance(Module) method returned true. // IsInstance(Module) method returned true.
BuildSnapshot(sdkModuleContext ModuleContext, builder SnapshotBuilder, member SdkMember) BuildSnapshot(sdkModuleContext ModuleContext, builder SnapshotBuilder, member SdkMember)
} }
type SdkMemberTypeBase struct {
PropertyName string
}
func (b *SdkMemberTypeBase) SdkPropertyName() string {
return b.PropertyName
}
// Encapsulates the information about registered SdkMemberTypes.
type SdkMemberTypesRegistry struct {
// The list of types sorted by property name.
list []SdkMemberType
// The key that uniquely identifies this registry instance.
key OnceKey
}
func (r *SdkMemberTypesRegistry) RegisteredTypes() []SdkMemberType {
return r.list
}
func (r *SdkMemberTypesRegistry) UniqueOnceKey() OnceKey {
// Use the pointer to the registry as the unique key.
return NewCustomOnceKey(r)
}
// The set of registered SdkMemberTypes.
var SdkMemberTypes = &SdkMemberTypesRegistry{}
// Register an SdkMemberType object to allow them to be used in the sdk and sdk_snapshot module
// types.
func RegisterSdkMemberType(memberType SdkMemberType) {
oldList := SdkMemberTypes.list
// Copy the slice just in case this is being read while being modified, e.g. when testing.
list := make([]SdkMemberType, 0, len(oldList)+1)
list = append(list, oldList...)
list = append(list, memberType)
// Sort the member types by their property name to ensure that registry order has no effect
// on behavior.
sort.Slice(list, func(i1, i2 int) bool {
t1 := list[i1]
t2 := list[i2]
return t1.SdkPropertyName() < t2.SdkPropertyName()
})
// Generate a key that identifies the slice of SdkMemberTypes by joining the property names
// from all the SdkMemberType .
var properties []string
for _, t := range list {
properties = append(properties, t.SdkPropertyName())
}
key := NewOnceKey(strings.Join(properties, "|"))
// Create a new registry so the pointer uniquely identifies the set of registered types.
SdkMemberTypes = &SdkMemberTypesRegistry{
list: list,
key: key,
}
}

View File

@@ -24,17 +24,28 @@ import (
// This file contains support for using cc library modules within an sdk. // This file contains support for using cc library modules within an sdk.
var SharedLibrarySdkMemberType = &librarySdkMemberType{ func init() {
prebuiltModuleType: "cc_prebuilt_library_shared", // Register sdk member types.
linkTypes: []string{"shared"}, android.RegisterSdkMemberType(&librarySdkMemberType{
} SdkMemberTypeBase: android.SdkMemberTypeBase{
PropertyName: "native_shared_libs",
},
prebuiltModuleType: "cc_prebuilt_library_shared",
linkTypes: []string{"shared"},
})
var StaticLibrarySdkMemberType = &librarySdkMemberType{ android.RegisterSdkMemberType(&librarySdkMemberType{
prebuiltModuleType: "cc_prebuilt_library_static", SdkMemberTypeBase: android.SdkMemberTypeBase{
linkTypes: []string{"static"}, PropertyName: "native_static_libs",
},
prebuiltModuleType: "cc_prebuilt_library_static",
linkTypes: []string{"static"},
})
} }
type librarySdkMemberType struct { type librarySdkMemberType struct {
android.SdkMemberTypeBase
prebuiltModuleType string prebuiltModuleType string
// The set of link types supported, set of "static", "shared". // The set of link types supported, set of "static", "shared".

View File

@@ -40,6 +40,13 @@ func init() {
android.RegisterModuleType("droidstubs_host", DroidstubsHostFactory) android.RegisterModuleType("droidstubs_host", DroidstubsHostFactory)
android.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory) android.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory)
// Register sdk member type.
android.RegisterSdkMemberType(&droidStubsSdkMemberType{
SdkMemberTypeBase: android.SdkMemberTypeBase{
PropertyName: "stubs_sources",
},
})
} }
var ( var (
@@ -1974,9 +1981,8 @@ func PrebuiltStubsSourcesFactory() android.Module {
return module return module
} }
var DroidStubsSdkMemberType = &droidStubsSdkMemberType{}
type droidStubsSdkMemberType struct { type droidStubsSdkMemberType struct {
android.SdkMemberTypeBase
} }
func (mt *droidStubsSdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) { func (mt *droidStubsSdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {

View File

@@ -52,6 +52,23 @@ func init() {
android.RegisterSingletonType("logtags", LogtagsSingleton) android.RegisterSingletonType("logtags", LogtagsSingleton)
android.RegisterSingletonType("kythe_java_extract", kytheExtractJavaFactory) android.RegisterSingletonType("kythe_java_extract", kytheExtractJavaFactory)
// Register sdk member types.
android.RegisterSdkMemberType(&headerLibrarySdkMemberType{
librarySdkMemberType{
android.SdkMemberTypeBase{
PropertyName: "java_header_libs",
},
},
})
android.RegisterSdkMemberType(&implLibrarySdkMemberType{
librarySdkMemberType{
android.SdkMemberTypeBase{
PropertyName: "java_libs",
},
},
})
} }
func (j *Module) checkSdkVersion(ctx android.ModuleContext) { func (j *Module) checkSdkVersion(ctx android.ModuleContext) {
@@ -1721,6 +1738,7 @@ func (j *Library) sdkSnapshotFilePathForJar() string {
} }
type librarySdkMemberType struct { type librarySdkMemberType struct {
android.SdkMemberTypeBase
} }
func (mt *librarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) { func (mt *librarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
@@ -1764,8 +1782,6 @@ func (mt *librarySdkMemberType) buildSnapshot(
module.AddProperty("jars", []string{snapshotRelativeJavaLibPath}) module.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
} }
var HeaderLibrarySdkMemberType = &headerLibrarySdkMemberType{}
type headerLibrarySdkMemberType struct { type headerLibrarySdkMemberType struct {
librarySdkMemberType librarySdkMemberType
} }
@@ -1781,8 +1797,6 @@ func (mt *headerLibrarySdkMemberType) BuildSnapshot(sdkModuleContext android.Mod
}) })
} }
var ImplLibrarySdkMemberType = &implLibrarySdkMemberType{}
type implLibrarySdkMemberType struct { type implLibrarySdkMemberType struct {
librarySdkMemberType librarySdkMemberType
} }

View File

@@ -17,6 +17,7 @@ package sdk
import ( import (
"fmt" "fmt"
"io" "io"
"reflect"
"strconv" "strconv"
"github.com/google/blueprint" "github.com/google/blueprint"
@@ -26,8 +27,6 @@ import (
// This package doesn't depend on the apex package, but import it to make its mutators to be // This package doesn't depend on the apex package, but import it to make its mutators to be
// registered before mutators in this package. See RegisterPostDepsMutators for more details. // registered before mutators in this package. See RegisterPostDepsMutators for more details.
_ "android/soong/apex" _ "android/soong/apex"
"android/soong/cc"
"android/soong/java"
) )
func init() { func init() {
@@ -38,20 +37,19 @@ func init() {
android.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory) android.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
android.PreDepsMutators(RegisterPreDepsMutators) android.PreDepsMutators(RegisterPreDepsMutators)
android.PostDepsMutators(RegisterPostDepsMutators) android.PostDepsMutators(RegisterPostDepsMutators)
// Populate the dependency tags for each member list property. This needs to
// be done here to break an initialization cycle.
for _, memberListProperty := range sdkMemberListProperties {
memberListProperty.dependencyTag = &sdkMemberDependencyTag{
memberListProperty: memberListProperty,
}
}
} }
type sdk struct { type sdk struct {
android.ModuleBase android.ModuleBase
android.DefaultableModuleBase android.DefaultableModuleBase
// The dynamically generated information about the registered SdkMemberType
dynamicSdkMemberTypes *dynamicSdkMemberTypes
// The dynamically created instance of the properties struct containing the sdk member
// list properties, e.g. java_libs.
dynamicMemberTypeListProperties interface{}
properties sdkProperties properties sdkProperties
snapshotFile android.OptionalPath snapshotFile android.OptionalPath
@@ -61,92 +59,141 @@ type sdk struct {
} }
type sdkProperties struct { type sdkProperties struct {
// For module types from the cc package
// The list of shared native libraries in this SDK
Native_shared_libs []string
// The list of static native libraries in this SDK
Native_static_libs []string
// For module types from the java package
// The list of java header libraries in this SDK
//
// This should be used for java libraries that are provided separately at runtime,
// e.g. through an APEX.
Java_header_libs []string
// The list of java implementation libraries in this SDK
Java_libs []string
// The list of stub sources in this SDK
Stubs_sources []string
Snapshot bool `blueprint:"mutated"` Snapshot bool `blueprint:"mutated"`
} }
type sdkMemberDependencyTag struct { type sdkMemberDependencyTag struct {
blueprint.BaseDependencyTag blueprint.BaseDependencyTag
memberListProperty *sdkMemberListProperty memberType android.SdkMemberType
} }
// Contains information about the sdk properties that list sdk members, e.g. // Contains information about the sdk properties that list sdk members, e.g.
// Java_header_libs. // Java_header_libs.
type sdkMemberListProperty struct { type sdkMemberListProperty struct {
// the name of the property as used in a .bp file
name string
// getter for the list of member names // getter for the list of member names
getter func(properties *sdkProperties) []string getter func(properties interface{}) []string
// the type of member referenced in the list // the type of member referenced in the list
memberType android.SdkMemberType memberType android.SdkMemberType
// the dependency tag used for items in this list. // the dependency tag used for items in this list that can be used to determine the memberType
// for a resolved dependency.
dependencyTag *sdkMemberDependencyTag dependencyTag *sdkMemberDependencyTag
} }
// Information about how to handle each member list property. func (p *sdkMemberListProperty) propertyName() string {
return p.memberType.SdkPropertyName()
}
// Cache of dynamically generated dynamicSdkMemberTypes objects. The key is the pointer
// to a slice of SdkMemberType instances held in android.SdkMemberTypes.
var dynamicSdkMemberTypesMap android.OncePer
// A dynamically generated set of member list properties and associated structure type.
type dynamicSdkMemberTypes struct {
// The dynamically generated structure type.
//
// Contains one []string exported field for each android.SdkMemberTypes. The name of the field
// is the exported form of the value returned by SdkMemberType.SdkPropertyName().
propertiesStructType reflect.Type
// Information about each of the member type specific list properties.
memberListProperties []*sdkMemberListProperty
}
func (d *dynamicSdkMemberTypes) createMemberListProperties() interface{} {
return reflect.New(d.propertiesStructType).Interface()
}
func getDynamicSdkMemberTypes(registry *android.SdkMemberTypesRegistry) *dynamicSdkMemberTypes {
// Get a key that uniquely identifies the registry contents.
key := registry.UniqueOnceKey()
// Get the registered types.
registeredTypes := registry.RegisteredTypes()
// Get the cached value, creating new instance if necessary.
return dynamicSdkMemberTypesMap.Once(key, func() interface{} {
return createDynamicSdkMemberTypes(registeredTypes)
}).(*dynamicSdkMemberTypes)
}
// Create the dynamicSdkMemberTypes from the list of registered member types.
// //
// It is organized first by package and then by name within the package. // A struct is created which contains one exported field per member type corresponding to
// Packages are in alphabetical order and properties are in alphabetical order // the SdkMemberType.SdkPropertyName() value.
// within each package. //
var sdkMemberListProperties = []*sdkMemberListProperty{ // A list of sdkMemberListProperty instances is created, one per member type that provides:
// Members from cc package. // * a reference to the member type.
{ // * a getter for the corresponding field in the properties struct.
name: "native_shared_libs", // * a dependency tag that identifies the member type of a resolved dependency.
getter: func(properties *sdkProperties) []string { return properties.Native_shared_libs }, //
memberType: cc.SharedLibrarySdkMemberType, func createDynamicSdkMemberTypes(sdkMemberTypes []android.SdkMemberType) *dynamicSdkMemberTypes {
}, var listProperties []*sdkMemberListProperty
{ var fields []reflect.StructField
name: "native_static_libs",
getter: func(properties *sdkProperties) []string { return properties.Native_static_libs }, // Iterate over the member types creating StructField and sdkMemberListProperty objects.
memberType: cc.StaticLibrarySdkMemberType, for f, memberType := range sdkMemberTypes {
}, p := memberType.SdkPropertyName()
// Members from java package.
{ // Create a dynamic exported field for the member type's property.
name: "java_header_libs", fields = append(fields, reflect.StructField{
getter: func(properties *sdkProperties) []string { return properties.Java_header_libs }, Name: proptools.FieldNameForProperty(p),
memberType: java.HeaderLibrarySdkMemberType, Type: reflect.TypeOf([]string{}),
}, })
{
name: "java_libs", // Copy the field index for use in the getter func as using the loop variable directly will
getter: func(properties *sdkProperties) []string { return properties.Java_libs }, // cause all funcs to use the last value.
memberType: java.ImplLibrarySdkMemberType, fieldIndex := f
},
{ // Create an sdkMemberListProperty for the member type.
name: "stubs_sources", memberListProperty := &sdkMemberListProperty{
getter: func(properties *sdkProperties) []string { return properties.Stubs_sources }, getter: func(properties interface{}) []string {
memberType: java.DroidStubsSdkMemberType, // The properties is expected to be of the following form (where
}, // <Module_types> is the name of an SdkMemberType.SdkPropertyName().
// properties *struct {<Module_types> []string, ....}
//
// Although it accesses the field by index the following reflection code is equivalent to:
// *properties.<Module_types>
//
list := reflect.ValueOf(properties).Elem().Field(fieldIndex).Interface().([]string)
return list
},
memberType: memberType,
dependencyTag: &sdkMemberDependencyTag{
memberType: memberType,
},
}
listProperties = append(listProperties, memberListProperty)
}
// Create a dynamic struct from the collated fields.
propertiesStructType := reflect.StructOf(fields)
return &dynamicSdkMemberTypes{
memberListProperties: listProperties,
propertiesStructType: propertiesStructType,
}
} }
// sdk defines an SDK which is a logical group of modules (e.g. native libs, headers, java libs, etc.) // sdk defines an SDK which is a logical group of modules (e.g. native libs, headers, java libs, etc.)
// which Mainline modules like APEX can choose to build with. // which Mainline modules like APEX can choose to build with.
func ModuleFactory() android.Module { func ModuleFactory() android.Module {
s := &sdk{} s := &sdk{}
s.AddProperties(&s.properties)
// Get the dynamic sdk member type data for the currently registered sdk member types.
s.dynamicSdkMemberTypes = getDynamicSdkMemberTypes(android.SdkMemberTypes)
// Create an instance of the dynamically created struct that contains all the
// properties for the member type specific list properties.
s.dynamicMemberTypeListProperties = s.dynamicSdkMemberTypes.createMemberListProperties()
s.AddProperties(&s.properties, s.dynamicMemberTypeListProperties)
android.InitAndroidMultiTargetsArchModule(s, android.HostAndDeviceSupported, android.MultilibCommon) android.InitAndroidMultiTargetsArchModule(s, android.HostAndDeviceSupported, android.MultilibCommon)
android.InitDefaultableModule(s) android.InitDefaultableModule(s)
android.AddLoadHook(s, func(ctx android.LoadHookContext) { android.AddLoadHook(s, func(ctx android.LoadHookContext) {
@@ -235,9 +282,9 @@ type sdkMemberVesionedDepTag struct {
// Step 1: create dependencies from an SDK module to its members. // Step 1: create dependencies from an SDK module to its members.
func memberMutator(mctx android.BottomUpMutatorContext) { func memberMutator(mctx android.BottomUpMutatorContext) {
if m, ok := mctx.Module().(*sdk); ok { if s, ok := mctx.Module().(*sdk); ok {
for _, memberListProperty := range sdkMemberListProperties { for _, memberListProperty := range s.dynamicSdkMemberTypes.memberListProperties {
names := memberListProperty.getter(&m.properties) names := memberListProperty.getter(s.dynamicMemberTypeListProperties)
tag := memberListProperty.dependencyTag tag := memberListProperty.dependencyTag
memberListProperty.memberType.AddDependencies(mctx, tag, names) memberListProperty.memberType.AddDependencies(mctx, tag, names)
} }

View File

@@ -105,21 +105,20 @@ func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderC
// Collect all the members. // Collect all the members.
// //
// The members are first grouped by type and then grouped by name. The order of // The members are first grouped by type and then grouped by name. The order of
// the types is the order they are referenced in sdkMemberListProperties. The // the types is the order they are referenced in android.SdkMemberTypes. The
// names are in order in which the dependencies were added. // names are in order in which the dependencies were added.
func collectMembers(ctx android.ModuleContext) []*sdkMember { func (s *sdk) collectMembers(ctx android.ModuleContext) []*sdkMember {
byType := make(map[android.SdkMemberType][]*sdkMember) byType := make(map[android.SdkMemberType][]*sdkMember)
byName := make(map[string]*sdkMember) byName := make(map[string]*sdkMember)
ctx.VisitDirectDeps(func(m android.Module) { ctx.VisitDirectDeps(func(m android.Module) {
tag := ctx.OtherModuleDependencyTag(m) tag := ctx.OtherModuleDependencyTag(m)
if memberTag, ok := tag.(*sdkMemberDependencyTag); ok { if memberTag, ok := tag.(*sdkMemberDependencyTag); ok {
memberListProperty := memberTag.memberListProperty memberType := memberTag.memberType
memberType := memberListProperty.memberType
// Make sure that the resolved module is allowed in the member list property. // Make sure that the resolved module is allowed in the member list property.
if !memberType.IsInstance(m) { if !memberType.IsInstance(m) {
ctx.ModuleErrorf("module %q is not valid in property %s", ctx.OtherModuleName(m), memberListProperty.name) ctx.ModuleErrorf("module %q is not valid in property %s", ctx.OtherModuleName(m), memberType.SdkPropertyName())
} }
name := ctx.OtherModuleName(m) name := ctx.OtherModuleName(m)
@@ -136,7 +135,7 @@ func collectMembers(ctx android.ModuleContext) []*sdkMember {
}) })
var members []*sdkMember var members []*sdkMember
for _, memberListProperty := range sdkMemberListProperties { for _, memberListProperty := range s.dynamicSdkMemberTypes.memberListProperties {
membersOfType := byType[memberListProperty.memberType] membersOfType := byType[memberListProperty.memberType]
members = append(members, membersOfType...) members = append(members, membersOfType...)
} }
@@ -191,7 +190,7 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath {
} }
s.builderForTests = builder s.builderForTests = builder
for _, member := range collectMembers(ctx) { for _, member := range s.collectMembers(ctx) {
member.memberType.BuildSnapshot(ctx, builder, member) member.memberType.BuildSnapshot(ctx, builder, member)
} }
@@ -220,10 +219,10 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath {
} }
addHostDeviceSupportedProperties(&s.ModuleBase, snapshotModule) addHostDeviceSupportedProperties(&s.ModuleBase, snapshotModule)
for _, memberListProperty := range sdkMemberListProperties { for _, memberListProperty := range s.dynamicSdkMemberTypes.memberListProperties {
names := memberListProperty.getter(&s.properties) names := memberListProperty.getter(s.dynamicMemberTypeListProperties)
if len(names) > 0 { if len(names) > 0 {
snapshotModule.AddProperty(memberListProperty.name, builder.versionedSdkMemberNames(names)) snapshotModule.AddProperty(memberListProperty.propertyName(), builder.versionedSdkMemberNames(names))
} }
} }
bpFile.AddModule(snapshotModule) bpFile.AddModule(snapshotModule)