Merge "Decouple addition of new sdk member types from sdk code"

am: 0558ecce51

Change-Id: Ic1da6424bffada70a0e69bb6605495f45a2f71b2
This commit is contained in:
Paul Duffin
2019-12-14 07:00:58 -08:00
committed by android-build-merger
6 changed files with 249 additions and 102 deletions

View File

@@ -15,6 +15,7 @@
package android
import (
"sort"
"strings"
"github.com/google/blueprint"
@@ -218,18 +219,24 @@ type SdkMember interface {
// The basic implementation should look something like this, where ModuleType is
// the name of the module type being supported.
//
// var ModuleTypeSdkMemberType = newModuleTypeSdkMemberType()
//
// func newModuleTypeSdkMemberType() android.SdkMemberType {
// return &moduleTypeSdkMemberType{}
// type moduleTypeSdkMemberType struct {
// android.SdkMemberTypeBase
// }
//
// type moduleTypeSdkMemberType struct {
// func init() {
// android.RegisterSdkMemberType(&moduleTypeSdkMemberType{
// SdkMemberTypeBase: android.SdkMemberTypeBase{
// PropertyName: "module_types",
// },
// }
// }
//
// ...methods...
//
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
// 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
@@ -254,3 +261,66 @@ type SdkMemberType interface {
// IsInstance(Module) method returned true.
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.
var SharedLibrarySdkMemberType = &librarySdkMemberType{
func init() {
// Register sdk member types.
android.RegisterSdkMemberType(&librarySdkMemberType{
SdkMemberTypeBase: android.SdkMemberTypeBase{
PropertyName: "native_shared_libs",
},
prebuiltModuleType: "cc_prebuilt_library_shared",
linkTypes: []string{"shared"},
}
})
var StaticLibrarySdkMemberType = &librarySdkMemberType{
android.RegisterSdkMemberType(&librarySdkMemberType{
SdkMemberTypeBase: android.SdkMemberTypeBase{
PropertyName: "native_static_libs",
},
prebuiltModuleType: "cc_prebuilt_library_static",
linkTypes: []string{"static"},
})
}
type librarySdkMemberType struct {
android.SdkMemberTypeBase
prebuiltModuleType string
// 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("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory)
// Register sdk member type.
android.RegisterSdkMemberType(&droidStubsSdkMemberType{
SdkMemberTypeBase: android.SdkMemberTypeBase{
PropertyName: "stubs_sources",
},
})
}
var (
@@ -1974,9 +1981,8 @@ func PrebuiltStubsSourcesFactory() android.Module {
return module
}
var DroidStubsSdkMemberType = &droidStubsSdkMemberType{}
type droidStubsSdkMemberType struct {
android.SdkMemberTypeBase
}
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("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) {
@@ -1721,6 +1738,7 @@ func (j *Library) sdkSnapshotFilePathForJar() string {
}
type librarySdkMemberType struct {
android.SdkMemberTypeBase
}
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})
}
var HeaderLibrarySdkMemberType = &headerLibrarySdkMemberType{}
type headerLibrarySdkMemberType struct {
librarySdkMemberType
}
@@ -1781,8 +1797,6 @@ func (mt *headerLibrarySdkMemberType) BuildSnapshot(sdkModuleContext android.Mod
})
}
var ImplLibrarySdkMemberType = &implLibrarySdkMemberType{}
type implLibrarySdkMemberType struct {
librarySdkMemberType
}

View File

@@ -17,6 +17,7 @@ package sdk
import (
"fmt"
"io"
"reflect"
"strconv"
"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
// registered before mutators in this package. See RegisterPostDepsMutators for more details.
_ "android/soong/apex"
"android/soong/cc"
"android/soong/java"
)
func init() {
@@ -38,20 +37,19 @@ func init() {
android.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
android.PreDepsMutators(RegisterPreDepsMutators)
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 {
android.ModuleBase
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
snapshotFile android.OptionalPath
@@ -61,92 +59,141 @@ type sdk 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"`
}
type sdkMemberDependencyTag struct {
blueprint.BaseDependencyTag
memberListProperty *sdkMemberListProperty
memberType android.SdkMemberType
}
// Contains information about the sdk properties that list sdk members, e.g.
// Java_header_libs.
type sdkMemberListProperty struct {
// the name of the property as used in a .bp file
name string
// 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
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
}
// 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.
//
// It is organized first by package and then by name within the package.
// Packages are in alphabetical order and properties are in alphabetical order
// within each package.
var sdkMemberListProperties = []*sdkMemberListProperty{
// Members from cc package.
{
name: "native_shared_libs",
getter: func(properties *sdkProperties) []string { return properties.Native_shared_libs },
memberType: cc.SharedLibrarySdkMemberType,
// 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.
//
// A struct is created which contains one exported field per member type corresponding to
// the SdkMemberType.SdkPropertyName() value.
//
// A list of sdkMemberListProperty instances is created, one per member type that provides:
// * a reference to the member type.
// * a getter for the corresponding field in the properties struct.
// * a dependency tag that identifies the member type of a resolved dependency.
//
func createDynamicSdkMemberTypes(sdkMemberTypes []android.SdkMemberType) *dynamicSdkMemberTypes {
var listProperties []*sdkMemberListProperty
var fields []reflect.StructField
// Iterate over the member types creating StructField and sdkMemberListProperty objects.
for f, memberType := range sdkMemberTypes {
p := memberType.SdkPropertyName()
// Create a dynamic exported field for the member type's property.
fields = append(fields, reflect.StructField{
Name: proptools.FieldNameForProperty(p),
Type: reflect.TypeOf([]string{}),
})
// Copy the field index for use in the getter func as using the loop variable directly will
// cause all funcs to use the last value.
fieldIndex := f
// Create an sdkMemberListProperty for the member type.
memberListProperty := &sdkMemberListProperty{
getter: func(properties interface{}) []string {
// 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
},
{
name: "native_static_libs",
getter: func(properties *sdkProperties) []string { return properties.Native_static_libs },
memberType: cc.StaticLibrarySdkMemberType,
},
// Members from java package.
{
name: "java_header_libs",
getter: func(properties *sdkProperties) []string { return properties.Java_header_libs },
memberType: java.HeaderLibrarySdkMemberType,
},
{
name: "java_libs",
getter: func(properties *sdkProperties) []string { return properties.Java_libs },
memberType: java.ImplLibrarySdkMemberType,
},
{
name: "stubs_sources",
getter: func(properties *sdkProperties) []string { return properties.Stubs_sources },
memberType: java.DroidStubsSdkMemberType,
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.)
// which Mainline modules like APEX can choose to build with.
func ModuleFactory() android.Module {
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.InitDefaultableModule(s)
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.
func memberMutator(mctx android.BottomUpMutatorContext) {
if m, ok := mctx.Module().(*sdk); ok {
for _, memberListProperty := range sdkMemberListProperties {
names := memberListProperty.getter(&m.properties)
if s, ok := mctx.Module().(*sdk); ok {
for _, memberListProperty := range s.dynamicSdkMemberTypes.memberListProperties {
names := memberListProperty.getter(s.dynamicMemberTypeListProperties)
tag := memberListProperty.dependencyTag
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.
//
// 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.
func collectMembers(ctx android.ModuleContext) []*sdkMember {
func (s *sdk) collectMembers(ctx android.ModuleContext) []*sdkMember {
byType := make(map[android.SdkMemberType][]*sdkMember)
byName := make(map[string]*sdkMember)
ctx.VisitDirectDeps(func(m android.Module) {
tag := ctx.OtherModuleDependencyTag(m)
if memberTag, ok := tag.(*sdkMemberDependencyTag); ok {
memberListProperty := memberTag.memberListProperty
memberType := memberListProperty.memberType
memberType := memberTag.memberType
// Make sure that the resolved module is allowed in the member list property.
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)
@@ -136,7 +135,7 @@ func collectMembers(ctx android.ModuleContext) []*sdkMember {
})
var members []*sdkMember
for _, memberListProperty := range sdkMemberListProperties {
for _, memberListProperty := range s.dynamicSdkMemberTypes.memberListProperties {
membersOfType := byType[memberListProperty.memberType]
members = append(members, membersOfType...)
}
@@ -191,7 +190,7 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath {
}
s.builderForTests = builder
for _, member := range collectMembers(ctx) {
for _, member := range s.collectMembers(ctx) {
member.memberType.BuildSnapshot(ctx, builder, member)
}
@@ -220,10 +219,10 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath {
}
addHostDeviceSupportedProperties(&s.ModuleBase, snapshotModule)
for _, memberListProperty := range sdkMemberListProperties {
names := memberListProperty.getter(&s.properties)
for _, memberListProperty := range s.dynamicSdkMemberTypes.memberListProperties {
names := memberListProperty.getter(s.dynamicMemberTypeListProperties)
if len(names) > 0 {
snapshotModule.AddProperty(memberListProperty.name, builder.versionedSdkMemberNames(names))
snapshotModule.AddProperty(memberListProperty.propertyName(), builder.versionedSdkMemberNames(names))
}
}
bpFile.AddModule(snapshotModule)