summaryrefslogtreecommitdiff
path: root/sdk/sdk.go
diff options
context:
space:
mode:
Diffstat (limited to 'sdk/sdk.go')
-rw-r--r--sdk/sdk.go223
1 files changed, 164 insertions, 59 deletions
diff --git a/sdk/sdk.go b/sdk/sdk.go
index cb5a6053d..b1c8aebf9 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -33,10 +33,14 @@ func init() {
pctx.Import("android/soong/android")
pctx.Import("android/soong/java/config")
- android.RegisterModuleType("sdk", SdkModuleFactory)
- android.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
- android.PreDepsMutators(RegisterPreDepsMutators)
- android.PostDepsMutators(RegisterPostDepsMutators)
+ registerSdkBuildComponents(android.InitRegistrationContext)
+}
+
+func registerSdkBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("sdk", SdkModuleFactory)
+ ctx.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
+ ctx.PreDepsMutators(RegisterPreDepsMutators)
+ ctx.PostDepsMutators(RegisterPostDepsMutators)
}
type sdk struct {
@@ -50,14 +54,14 @@ type sdk struct {
// list properties, e.g. java_libs.
dynamicMemberTypeListProperties interface{}
- // Information about the OsType specific member variants associated with this variant.
+ // Information about the OsType specific member variants depended upon by this variant.
//
// Set by OsType specific variants in the collectMembers() method and used by the
// CommonOS variant when building the snapshot. That work is all done on separate
// calls to the sdk.GenerateAndroidBuildActions method which is guaranteed to be
// called for the OsType specific variants before the CommonOS variant (because
// the latter depends on the former).
- memberRefs []sdkMemberRef
+ memberVariantDeps []sdkMemberVariantDep
// The multilib variants that are used by this sdk variant.
multilibUsages multilibUsage
@@ -75,6 +79,20 @@ type sdkProperties struct {
// True if this is a module_exports (or module_exports_snapshot) module type.
Module_exports bool `blueprint:"mutated"`
+
+ // The additional visibility to add to the prebuilt modules to allow them to
+ // reference each other.
+ //
+ // This can only be used to widen the visibility of the members:
+ //
+ // * Specifying //visibility:public here will make all members visible and
+ // essentially ignore their own visibility.
+ // * Specifying //visibility:private here is an error.
+ // * Specifying any other rule here will add it to the members visibility and
+ // be output to the member prebuilt in the snapshot. Duplicates will be
+ // dropped. Adding a rule to members that have //visibility:private will
+ // cause the //visibility:private to be discarded.
+ Prebuilt_visibility []string
}
// Contains information about the sdk properties that list sdk members, e.g.
@@ -83,6 +101,9 @@ type sdkMemberListProperty struct {
// getter for the list of member names
getter func(properties interface{}) []string
+ // setter for the list of member names
+ setter func(properties interface{}, list []string)
+
// the type of member referenced in the list
memberType android.SdkMemberType
@@ -109,6 +130,8 @@ type dynamicSdkMemberTypes struct {
// Information about each of the member type specific list properties.
memberListProperties []*sdkMemberListProperty
+
+ memberTypeToProperty map[android.SdkMemberType]*sdkMemberListProperty
}
func (d *dynamicSdkMemberTypes) createMemberListProperties() interface{} {
@@ -142,26 +165,31 @@ func getDynamicSdkMemberTypes(registry *android.SdkMemberTypesRegistry) *dynamic
func createDynamicSdkMemberTypes(sdkMemberTypes []android.SdkMemberType) *dynamicSdkMemberTypes {
var listProperties []*sdkMemberListProperty
+ memberTypeToProperty := map[android.SdkMemberType]*sdkMemberListProperty{}
var fields []reflect.StructField
// Iterate over the member types creating StructField and sdkMemberListProperty objects.
- for f, memberType := range sdkMemberTypes {
+ nextFieldIndex := 0
+ for _, 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{}),
- Tag: `android:"arch_variant"`,
- })
+ var getter func(properties interface{}) []string
+ var setter func(properties interface{}, list []string)
+ if memberType.RequiresBpProperty() {
+ // Create a dynamic exported field for the member type's property.
+ fields = append(fields, reflect.StructField{
+ Name: proptools.FieldNameForProperty(p),
+ Type: reflect.TypeOf([]string{}),
+ Tag: `android:"arch_variant"`,
+ })
- // 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
+ // 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 := nextFieldIndex
+ nextFieldIndex += 1
- // Create an sdkMemberListProperty for the member type.
- memberListProperty := &sdkMemberListProperty{
- getter: func(properties interface{}) []string {
+ 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, ....}
@@ -171,13 +199,31 @@ func createDynamicSdkMemberTypes(sdkMemberTypes []android.SdkMemberType) *dynami
//
list := reflect.ValueOf(properties).Elem().Field(fieldIndex).Interface().([]string)
return list
- },
+ }
+
+ setter = func(properties interface{}, list []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).Set(reflect.ValueOf(list))
+ }
+ }
+ // Create an sdkMemberListProperty for the member type.
+ memberListProperty := &sdkMemberListProperty{
+ getter: getter,
+ setter: setter,
memberType: memberType,
- dependencyTag: android.DependencyTagForSdkMemberType(memberType),
+ // Dependencies added directly from member properties are always exported.
+ dependencyTag: android.DependencyTagForSdkMemberType(memberType, true),
}
+ memberTypeToProperty[memberType] = memberListProperty
listProperties = append(listProperties, memberListProperty)
}
@@ -186,6 +232,7 @@ func createDynamicSdkMemberTypes(sdkMemberTypes []android.SdkMemberType) *dynami
return &dynamicSdkMemberTypes{
memberListProperties: listProperties,
+ memberTypeToProperty: memberTypeToProperty,
propertiesStructType: propertiesStructType,
}
}
@@ -211,6 +258,9 @@ func newSdkModule(moduleExports bool) *sdk {
// properties for the member type specific list properties.
s.dynamicMemberTypeListProperties = s.dynamicSdkMemberTypes.createMemberListProperties()
s.AddProperties(&s.properties, s.dynamicMemberTypeListProperties)
+
+ // Make sure that the prebuilt visibility property is verified for errors.
+ android.AddVisibilityProperty(s, "prebuilt_visibility", &s.properties.Prebuilt_visibility)
android.InitCommonOSAndroidMultiTargetsArchModule(s, android.HostAndDeviceSupported, android.MultilibCommon)
android.InitDefaultableModule(s)
android.AddLoadHook(s, func(ctx android.LoadHookContext) {
@@ -218,7 +268,7 @@ func newSdkModule(moduleExports bool) *sdk {
Compile_multilib *string
}
p := &props{Compile_multilib: proptools.StringPtr("both")}
- ctx.AppendProperties(p)
+ ctx.PrependProperties(p)
})
return s
}
@@ -234,20 +284,8 @@ func (s *sdk) memberListProperties() []*sdkMemberListProperty {
return s.dynamicSdkMemberTypes.memberListProperties
}
-func (s *sdk) getExportedMembers() map[string]struct{} {
- // Collect all the exported members.
- exportedMembers := make(map[string]struct{})
-
- for _, memberListProperty := range s.memberListProperties() {
- names := memberListProperty.getter(s.dynamicMemberTypeListProperties)
-
- // Every member specified explicitly in the properties is exported by the sdk.
- for _, name := range names {
- exportedMembers[name] = struct{}{}
- }
- }
-
- return exportedMembers
+func (s *sdk) memberListProperty(memberType android.SdkMemberType) *sdkMemberListProperty {
+ return s.dynamicSdkMemberTypes.memberTypeToProperty[memberType]
}
func (s *sdk) snapshot() bool {
@@ -278,8 +316,8 @@ func (s *sdk) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// Generate the snapshot from the member info.
p := s.buildSnapshot(ctx, sdkVariants)
- s.snapshotFile = android.OptionalPathForPath(p)
- ctx.InstallFile(android.PathForMainlineSdksInstall(ctx), s.Name()+"-current.zip", p)
+ zip := ctx.InstallFile(android.PathForMainlineSdksInstall(ctx), p.Base(), p)
+ s.snapshotFile = android.OptionalPathForPath(zip)
}
}
@@ -291,10 +329,10 @@ func (s *sdk) AndroidMkEntries() []android.AndroidMkEntries {
return []android.AndroidMkEntries{android.AndroidMkEntries{
Class: "FAKE",
OutputFile: s.snapshotFile,
- DistFile: s.snapshotFile,
+ DistFiles: android.MakeDefaultDistFiles(s.snapshotFile.Path()),
Include: "$(BUILD_PHONY_PACKAGE)",
ExtraFooters: []android.AndroidMkExtraFootersFunc{
- func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+ func(w io.Writer, name, prefix, moduleDir string) {
// Allow the sdk to be built by simply passing its name on the command line.
fmt.Fprintln(w, ".PHONY:", s.Name())
fmt.Fprintln(w, s.Name()+":", s.snapshotFile.String())
@@ -330,23 +368,52 @@ type dependencyTag struct {
blueprint.BaseDependencyTag
}
+// Mark this tag so dependencies that use it are excluded from APEX contents.
+func (t dependencyTag) ExcludeFromApexContents() {}
+
+var _ android.ExcludeFromApexContentsTag = dependencyTag{}
+
// For dependencies from an in-development version of an SDK member to frozen versions of the same member
// e.g. libfoo -> libfoo.mysdk.11 and libfoo.mysdk.12
+//
+// The dependency represented by this tag requires that for every APEX variant created for the
+// `from` module that an equivalent APEX variant is created for the 'to' module. This is because an
+// APEX that requires a specific version of an sdk (via the `uses_sdks` property will replace
+// dependencies on the unversioned sdk member with a dependency on the appropriate versioned sdk
+// member. In order for that to work the versioned sdk member needs to have a variant for that APEX.
+// As it is not known at the time that the APEX variants are created which specific APEX variants of
+// a versioned sdk members will be required it is necessary for the versioned sdk members to have
+// variants for any APEX that it could be used within.
+//
+// If the APEX selects a versioned sdk member then it will not have a dependency on the `from`
+// module at all so any dependencies of that module will not affect the APEX. However, if the APEX
+// selects the unversioned sdk member then it must exclude all the versioned sdk members. In no
+// situation would this dependency cause the `to` module to be added to the APEX hence why this tag
+// also excludes the `to` module from being added to the APEX contents.
type sdkMemberVersionedDepTag struct {
dependencyTag
member string
version string
}
+func (t sdkMemberVersionedDepTag) AlwaysRequireApexVariant() bool {
+ return true
+}
+
// Mark this tag so dependencies that use it are excluded from visibility enforcement.
func (t sdkMemberVersionedDepTag) ExcludeFromVisibilityEnforcement() {}
+var _ android.AlwaysRequireApexVariantTag = sdkMemberVersionedDepTag{}
+
// Step 1: create dependencies from an SDK module to its members.
func memberMutator(mctx android.BottomUpMutatorContext) {
if s, ok := mctx.Module().(*sdk); ok {
// Add dependencies from enabled and non CommonOS variants to the sdk member variants.
if s.Enabled() && !s.IsCommonOSVariant() {
for _, memberListProperty := range s.memberListProperties() {
+ if memberListProperty.getter == nil {
+ continue
+ }
names := memberListProperty.getter(s.dynamicMemberTypeListProperties)
if len(names) > 0 {
tag := memberListProperty.dependencyTag
@@ -389,7 +456,7 @@ func memberDepsMutator(mctx android.TopDownMutatorContext) {
// built with libfoo.mysdk.11 and libfoo.mysdk.12, respectively depending on which sdk they are
// using.
func memberInterVersionMutator(mctx android.BottomUpMutatorContext) {
- if m, ok := mctx.Module().(android.SdkAware); ok && m.IsInAnySdk() {
+ if m, ok := mctx.Module().(android.SdkAware); ok && m.IsInAnySdk() && m.IsVersioned() {
if !m.ContainingSdk().Unversioned() {
memberName := m.MemberName()
tag := sdkMemberVersionedDepTag{member: memberName, version: m.ContainingSdk().Version}
@@ -398,16 +465,26 @@ func memberInterVersionMutator(mctx android.BottomUpMutatorContext) {
}
}
+// An interface that encapsulates all the functionality needed to manage the sdk dependencies.
+//
+// It is a mixture of apex and sdk module functionality.
+type sdkAndApexModule interface {
+ android.Module
+ android.DepIsInSameApex
+ android.RequiredSdks
+}
+
// Step 4: transitively ripple down the SDK requirements from the root modules like APEX to its
// descendants
func sdkDepsMutator(mctx android.TopDownMutatorContext) {
- if m, ok := mctx.Module().(android.SdkAware); ok {
+ if parent, ok := mctx.Module().(sdkAndApexModule); ok {
// Module types for Mainline modules (e.g. APEX) are expected to implement RequiredSdks()
// by reading its own properties like `uses_sdks`.
- requiredSdks := m.RequiredSdks()
+ requiredSdks := parent.RequiredSdks()
if len(requiredSdks) > 0 {
mctx.VisitDirectDeps(func(m android.Module) {
- if dep, ok := m.(android.SdkAware); ok {
+ // Only propagate required sdks from the apex onto its contents.
+ if dep, ok := m.(android.SdkAware); ok && android.IsDepInSameApex(mctx, parent, dep) {
dep.BuildWithSdks(requiredSdks)
}
})
@@ -418,25 +495,44 @@ func sdkDepsMutator(mctx android.TopDownMutatorContext) {
// Step 5: if libfoo.mysdk.11 is in the context where version 11 of mysdk is requested, the
// versioned module is used instead of the un-versioned (in-development) module libfoo
func sdkDepsReplaceMutator(mctx android.BottomUpMutatorContext) {
- if m, ok := mctx.Module().(android.SdkAware); ok && m.IsInAnySdk() {
- if sdk := m.ContainingSdk(); !sdk.Unversioned() {
- if m.RequiredSdks().Contains(sdk) {
- // Note that this replacement is done only for the modules that have the same
- // variations as the current module. Since current module is already mutated for
- // apex references in other APEXes are not affected by this replacement.
- memberName := m.MemberName()
- mctx.ReplaceDependencies(memberName)
- }
+ if versionedSdkMember, ok := mctx.Module().(android.SdkAware); ok && versionedSdkMember.IsInAnySdk() && versionedSdkMember.IsVersioned() {
+ if sdk := versionedSdkMember.ContainingSdk(); !sdk.Unversioned() {
+ // Only replace dependencies to <sdkmember> with <sdkmember@required-version>
+ // if the depending module requires it. e.g.
+ // foo -> sdkmember
+ // will be transformed to:
+ // foo -> sdkmember@1
+ // if and only if foo is a member of an APEX that requires version 1 of the
+ // sdk containing sdkmember.
+ memberName := versionedSdkMember.MemberName()
+
+ // Convert a panic into a normal error to allow it to be more easily tested for. This is a
+ // temporary workaround, once http://b/183204176 has been fixed this can be removed.
+ // TODO(b/183204176): Remove this after fixing.
+ defer func() {
+ if r := recover(); r != nil {
+ mctx.ModuleErrorf("sdkDepsReplaceMutator %s", r)
+ }
+ }()
+
+ // Replace dependencies on sdkmember with a dependency on the current module which
+ // is a versioned prebuilt of the sdkmember if required.
+ mctx.ReplaceDependenciesIf(memberName, func(from blueprint.Module, tag blueprint.DependencyTag, to blueprint.Module) bool {
+ // from - foo
+ // to - sdkmember
+ replace := false
+ if parent, ok := from.(android.RequiredSdks); ok {
+ replace = parent.RequiredSdks().Contains(sdk)
+ }
+ return replace
+ })
}
}
}
// Step 6: ensure that the dependencies outside of the APEX are all from the required SDKs
func sdkRequirementsMutator(mctx android.TopDownMutatorContext) {
- if m, ok := mctx.Module().(interface {
- android.DepIsInSameApex
- android.RequiredSdks
- }); ok {
+ if m, ok := mctx.Module().(sdkAndApexModule); ok {
requiredSdks := m.RequiredSdks()
if len(requiredSdks) == 0 {
return
@@ -455,9 +551,18 @@ func sdkRequirementsMutator(mctx android.TopDownMutatorContext) {
return
}
- // If the dep is outside of the APEX, but is not in any of the
- // required SDKs, we know that the dep is a violation.
+ // If the dep is outside of the APEX, but is not in any of the required SDKs, we know that the
+ // dep is a violation.
if sa, ok := dep.(android.SdkAware); ok {
+ // It is not an error if a dependency that is excluded from the apex due to the tag is not
+ // in one of the required SDKs. That is because all of the existing tags that implement it
+ // do not depend on modules which can or should belong to an sdk_snapshot.
+ if _, ok := tag.(android.ExcludeFromApexContentsTag); ok {
+ // The tag defines a dependency that never requires the child module to be part of the
+ // same apex.
+ return
+ }
+
if !m.DepIsInSameApex(mctx, dep) && !requiredSdks.Contains(sa.ContainingSdk()) {
mctx.ModuleErrorf("depends on %q (in SDK %q) that isn't part of the required SDKs: %v",
sa.Name(), sa.ContainingSdk(), requiredSdks)