From a7bc8ad0b9ec3f762e45af4d60b96922782616cf Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Tue, 15 Oct 2019 15:20:07 +0900 Subject: [PATCH] Prohibit dependencies outside of uses_sdks When an APEX is built with uses_sdks, any depedndency from the APEX to the outside of the APEX should be from the SDKs that the APEX is built against. Bug: 138182343 Test: m Change-Id: I1c2ffe8d28ccf648d928ea59652c2d0070bf10eb --- android/apex.go | 11 ++++++++ android/sdk.go | 11 ++++++++ apex/apex.go | 5 ++++ cc/cc.go | 10 +++++++ cc/testing.go | 2 ++ java/java.go | 6 ++++ sdk/sdk.go | 29 +++++++++++++++++++ sdk/sdk_test.go | 74 +++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 148 insertions(+) diff --git a/android/apex.go b/android/apex.go index d3c071056..5118a0abb 100644 --- a/android/apex.go +++ b/android/apex.go @@ -77,6 +77,10 @@ type ApexModule interface { // Tests if this module is available for the specified APEX or ":platform" AvailableFor(what string) bool + + // DepIsInSameApex tests if the other module 'dep' is installed to the same + // APEX as this module + DepIsInSameApex(ctx BaseModuleContext, dep Module) bool } type ApexProperties struct { @@ -154,6 +158,13 @@ func (m *ApexModuleBase) AvailableFor(what string) bool { return CheckAvailableForApex(what, m.ApexProperties.Apex_available) } +func (m *ApexModuleBase) DepIsInSameApex(ctx BaseModuleContext, dep Module) bool { + // By default, if there is a dependency from A to B, we try to include both in the same APEX, + // unless B is explicitly from outside of the APEX (i.e. a stubs lib). Thus, returning true. + // This is overridden by some module types like apex.ApexBundle, cc.Module, java.Module, etc. + return true +} + func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) { for _, n := range m.ApexProperties.Apex_available { if n == availableToPlatform || n == availableToAnyApex { diff --git a/android/sdk.go b/android/sdk.go index 616fbe19d..8e1e106a1 100644 --- a/android/sdk.go +++ b/android/sdk.go @@ -44,6 +44,17 @@ func (s SdkRef) Unversioned() bool { return s.Version == "" } +// String returns string representation of this SdkRef for debugging purpose +func (s SdkRef) String() string { + if s.Name == "" { + return "(No Sdk)" + } + if s.Unversioned() { + return s.Name + } + return s.Name + string(SdkVersionSeparator) + s.Version +} + // SdkVersionSeparator is a character used to separate an sdk name and its version const SdkVersionSeparator = '@' diff --git a/apex/apex.go b/apex/apex.go index 9382c191b..3c32004ed 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -791,6 +791,11 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) { } } +func (a *apexBundle) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool { + // direct deps of an APEX bundle are all part of the APEX bundle + return true +} + func (a *apexBundle) getCertString(ctx android.BaseModuleContext) string { certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(ctx.ModuleName()) if overridden { diff --git a/cc/cc.go b/cc/cc.go index f6f8abbf8..ef8906a83 100644 --- a/cc/cc.go +++ b/cc/cc.go @@ -2182,6 +2182,16 @@ func (c *Module) AndroidMkWriteAdditionalDependenciesForSourceAbiDiff(w io.Write } } +func (c *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool { + if depTag, ok := ctx.OtherModuleDependencyTag(dep).(dependencyTag); ok { + if cc, ok := dep.(*Module); ok && cc.IsStubs() && depTag.shared { + // dynamic dep to a stubs lib crosses APEX boundary + return false + } + } + return true +} + // // Defaults // diff --git a/cc/testing.go b/cc/testing.go index 11a5e3bd9..6fa6ea7af 100644 --- a/cc/testing.go +++ b/cc/testing.go @@ -190,6 +190,7 @@ func GatherRequiredDepsForTest(os android.OsType) string { name: "crtbegin_so", recovery_available: true, vendor_available: true, + stl: "none", } cc_object { @@ -208,6 +209,7 @@ func GatherRequiredDepsForTest(os android.OsType) string { name: "crtend_so", recovery_available: true, vendor_available: true, + stl: "none", } cc_object { diff --git a/java/java.go b/java/java.go index 52f3c11aa..d39c0d20c 100644 --- a/java/java.go +++ b/java/java.go @@ -1567,6 +1567,12 @@ func (j *Module) hasCode(ctx android.ModuleContext) bool { return len(srcFiles) > 0 || len(ctx.GetDirectDepsWithTag(staticLibTag)) > 0 } +func (j *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool { + depTag := ctx.OtherModuleDependencyTag(dep) + // dependencies other than the static linkage are all considered crossing APEX boundary + return depTag == staticLibTag +} + // // Java libraries (.jar file) // diff --git a/sdk/sdk.go b/sdk/sdk.go index d122cda56..7668d9188 100644 --- a/sdk/sdk.go +++ b/sdk/sdk.go @@ -122,6 +122,7 @@ func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) { // should have been mutated for the apex before the SDK requirements are set. ctx.TopDown("SdkDepsMutator", sdkDepsMutator).Parallel() ctx.BottomUp("SdkDepsReplaceMutator", sdkDepsReplaceMutator).Parallel() + ctx.TopDown("SdkRequirementCheck", sdkRequirementsMutator).Parallel() } type dependencyTag struct { @@ -229,3 +230,31 @@ func sdkDepsReplaceMutator(mctx android.BottomUpMutatorContext) { } } } + +// Step 6: ensure that the dependencies from outside of the APEX are all from the required SDKs +func sdkRequirementsMutator(mctx android.TopDownMutatorContext) { + if m, ok := mctx.Module().(interface { + DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool + RequiredSdks() android.SdkRefs + }); ok { + requiredSdks := m.RequiredSdks() + if len(requiredSdks) == 0 { + return + } + mctx.VisitDirectDeps(func(dep android.Module) { + if mctx.OtherModuleDependencyTag(dep) == android.DefaultsDepTag { + // dependency to defaults is always okay + return + } + + // If the dep is from 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 { + 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) + } + } + }) + } +} diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index 942556a32..664bb7c98 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -115,6 +115,23 @@ func testSdk(t *testing.T, bp string) (*android.TestContext, android.Config) { return ctx, config } +func testSdkError(t *testing.T, pattern, bp string) { + t.Helper() + ctx, config := testSdkContext(t, bp) + _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) + if len(errs) > 0 { + android.FailIfNoMatchingErrors(t, pattern, errs) + return + } + _, errs = ctx.PrepareBuildActions(config) + if len(errs) > 0 { + android.FailIfNoMatchingErrors(t, pattern, errs) + return + } + + t.Fatalf("missing expected error %q (0 errors are returned)", pattern) +} + // ensure that 'result' contains 'expected' func ensureContains(t *testing.T, result string, expected string) { t.Helper() @@ -303,6 +320,63 @@ func TestBasicSdkWithCc(t *testing.T) { ensureListContains(t, pathsToStrings(cpplibForMyApex2.Rule("ld").Implicits), sdkMemberV2.String()) } +func TestDepNotInRequiredSdks(t *testing.T) { + testSdkError(t, `module "myjavalib".*depends on "otherlib".*that isn't part of the required SDKs:.*`, ` + sdk { + name: "mysdk", + java_libs: ["sdkmember"], + } + + sdk_snapshot { + name: "mysdk@1", + java_libs: ["sdkmember_mysdk_1"], + } + + java_import { + name: "sdkmember", + prefer: false, + host_supported: true, + } + + java_import { + name: "sdkmember_mysdk_1", + sdk_member_name: "sdkmember", + host_supported: true, + } + + java_library { + name: "myjavalib", + srcs: ["Test.java"], + libs: [ + "sdkmember", + "otherlib", + ], + system_modules: "none", + sdk_version: "none", + compile_dex: true, + host_supported: true, + } + + // this lib is no in mysdk + java_library { + name: "otherlib", + srcs: ["Test.java"], + system_modules: "none", + sdk_version: "none", + compile_dex: true, + host_supported: true, + } + + apex { + name: "myapex", + java_libs: ["myjavalib"], + uses_sdks: ["mysdk@1"], + key: "myapex.key", + certificate: ":myapex.cert", + } + `) +} + var buildDir string func setUp() {