diff --git a/android/sdk.go b/android/sdk.go index 533bd0ee6..7956434c6 100644 --- a/android/sdk.go +++ b/android/sdk.go @@ -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, + } +} diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go index 6b09c127c..931907068 100644 --- a/cc/library_sdk_member.go +++ b/cc/library_sdk_member.go @@ -24,17 +24,28 @@ import ( // This file contains support for using cc library modules within an sdk. -var SharedLibrarySdkMemberType = &librarySdkMemberType{ - prebuiltModuleType: "cc_prebuilt_library_shared", - linkTypes: []string{"shared"}, -} +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{ - prebuiltModuleType: "cc_prebuilt_library_static", - linkTypes: []string{"static"}, + 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". diff --git a/java/droiddoc.go b/java/droiddoc.go index 76cdaeac6..92f92465b 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -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) { diff --git a/java/java.go b/java/java.go index f58e5ba24..d8db5f8a4 100644 --- a/java/java.go +++ b/java/java.go @@ -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 } diff --git a/sdk/sdk.go b/sdk/sdk.go index cd9aafa39..62bc06fd5 100644 --- a/sdk/sdk.go +++ b/sdk/sdk.go @@ -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. + // + // 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. -// 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, - }, - { - 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, - }, +// 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 + // is the name of an SdkMemberType.SdkPropertyName(). + // properties *struct { []string, ....} + // + // Although it accesses the field by index the following reflection code is equivalent to: + // *properties. + // + 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.) // 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) } diff --git a/sdk/update.go b/sdk/update.go index 52d21eda6..d31fa3002 100644 --- a/sdk/update.go +++ b/sdk/update.go @@ -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)