Ensure that DepIsInSameApex is not called for ExcludeFromApexContentsTag
The ExcludeFromApexContentsTag marker interface was added to avoid
every implementation of DepIsInSameApex() from having to deal with the
special tags, like PrebuiltDepTag. Unfortunately, when adding that
not all calls to DepIsInSameApex() were protected which meant that the
BootImageModule, which panics if it doesn't recognize a tag, was
causing failures. This change documents the need and improves the
consistency.
A follow up change will add a test for this.
Bug: 182992071
Test: m nothing
Change-Id: If0bf9a7447ebf7a0bb0c88e91951a7220d4af45c
diff --git a/android/apex.go b/android/apex.go
index 0d5cac8..a5ff442 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -140,9 +140,24 @@
// DepIsInSameApex tests if the other module 'dep' is considered as part of the same APEX as
// this module. For example, a static lib dependency usually returns true here, while a
// shared lib dependency to a stub library returns false.
+ //
+ // This method must not be called directly without first ignoring dependencies whose tags
+ // implement ExcludeFromApexContentsTag. Calls from within the func passed to WalkPayloadDeps()
+ // are fine as WalkPayloadDeps() will ignore those dependencies automatically. Otherwise, use
+ // IsDepInSameApex instead.
DepIsInSameApex(ctx BaseModuleContext, dep Module) bool
}
+func IsDepInSameApex(ctx BaseModuleContext, module, dep Module) bool {
+ depTag := ctx.OtherModuleDependencyTag(dep)
+ if _, ok := depTag.(ExcludeFromApexContentsTag); ok {
+ // The tag defines a dependency that never requires the child module to be part of the same
+ // apex as the parent.
+ return false
+ }
+ return module.(DepIsInSameApex).DepIsInSameApex(ctx, dep)
+}
+
// ApexModule is the interface that a module type is expected to implement if the module has to be
// built differently depending on whether the module is destined for an APEX or not (i.e., installed
// to one of the regular partitions).
@@ -260,6 +275,10 @@
//
// Unless the tag also implements the AlwaysRequireApexVariantTag this will prevent an apex variant
// from being created for the module.
+//
+// At the moment the sdk.sdkRequirementsMutator relies on the fact that the existing tags which
+// implement this interface do not define dependencies onto members of an sdk_snapshot. If that
+// changes then sdk.sdkRequirementsMutator will need fixing.
type ExcludeFromApexContentsTag interface {
blueprint.DependencyTag
diff --git a/apex/apex.go b/apex/apex.go
index a12f3d2..a67fe1f 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -854,12 +854,7 @@
if required, ok := depTag.(android.AlwaysRequireApexVariantTag); ok && required.AlwaysRequireApexVariant() {
return true
}
- if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
- // The tag defines a dependency that never requires the child module to be part of the same
- // apex as the parent so it does not need an apex variant created.
- return false
- }
- if !parent.(android.DepIsInSameApex).DepIsInSameApex(mctx, child) {
+ if !android.IsDepInSameApex(mctx, parent, child) {
return false
}
if excludeVndkLibs {
@@ -1003,11 +998,7 @@
// If any of the dep is not available to platform, this module is also considered as being
// not available to platform even if it has "//apex_available:platform"
mctx.VisitDirectDeps(func(child android.Module) {
- depTag := mctx.OtherModuleDependencyTag(child)
- if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
- return
- }
- if !am.DepIsInSameApex(mctx, child) {
+ if !android.IsDepInSameApex(mctx, am, child) {
// if the dependency crosses apex boundary, don't consider it
return
}
@@ -1872,7 +1863,10 @@
// like to record requiredNativeLibs even when
// DepIsInSameAPex is false. We also shouldn't do
// this for host.
- if !am.DepIsInSameApex(ctx, am) {
+ //
+ // TODO(jiyong): explain why the same module is passed in twice.
+ // Switching the first am to parent breaks lots of tests.
+ if !android.IsDepInSameApex(ctx, am, am) {
return false
}
@@ -2195,6 +2189,8 @@
// If `to` is not actually in the same APEX as `from` then it does not need
// apex_available and neither do any of its dependencies.
+ //
+ // It is ok to call DepIsInSameApex() directly from within WalkPayloadDeps().
if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
// As soon as the dependency graph crosses the APEX boundary, don't go further.
return false
@@ -2278,6 +2274,8 @@
// If `to` is not actually in the same APEX as `from` then it does not need
// apex_available and neither do any of its dependencies.
+ //
+ // It is ok to call DepIsInSameApex() directly from within WalkPayloadDeps().
if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
// As soon as the dependency graph crosses the APEX boundary, don't go
// further.
diff --git a/sdk/sdk.go b/sdk/sdk.go
index 6ca8512..e561529 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -445,20 +445,26 @@
}
}
+// 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 parent, ok := mctx.Module().(interface {
- android.DepIsInSameApex
- android.RequiredSdks
- }); 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 := parent.RequiredSdks()
if len(requiredSdks) > 0 {
mctx.VisitDirectDeps(func(m android.Module) {
// Only propagate required sdks from the apex onto its contents.
- if dep, ok := m.(android.SdkAware); ok && parent.DepIsInSameApex(mctx, dep) {
+ if dep, ok := m.(android.SdkAware); ok && android.IsDepInSameApex(mctx, parent, dep) {
dep.BuildWithSdks(requiredSdks)
}
})
@@ -497,10 +503,7 @@
// 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
@@ -519,9 +522,18 @@
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)