From 1c93c299fb0036b823e821237a8e52febe82679a Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Sat, 15 Feb 2020 10:38:00 -0800 Subject: [PATCH 1/2] Require apps built against the SDK to use JNI built against the NDK Apps that expect to run on older platforms should use JNI libraries that will also run on older platforms. Require that apps that set sdk_version have jni_libs modules that also set sdk_version, or set jni_uses_platform_apis: true to bypass the check. Fixes: 149591057 Test: app_test.go Change-Id: I76b9b45fb5773bc4dfc10520108f4f3578723909 Merged-In: I76b9b45fb5773bc4dfc10520108f4f3578723909 --- apex/apex_test.go | 6 ++++-- java/app.go | 21 +++++++++++++++++---- java/app_test.go | 2 ++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/apex/apex_test.go b/apex/apex_test.go index 350579ea9..056153f78 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -3323,18 +3323,20 @@ func TestApexWithApps(t *testing.T) { android_app { name: "AppFoo", srcs: ["foo/bar/MyClass.java"], - sdk_version: "none", + sdk_version: "current", system_modules: "none", jni_libs: ["libjni"], + stl: "none", apex_available: [ "myapex" ], } android_app { name: "AppFooPriv", srcs: ["foo/bar/MyClass.java"], - sdk_version: "none", + sdk_version: "current", system_modules: "none", privileged: true, + stl: "none", apex_available: [ "myapex" ], } diff --git a/java/app.go b/java/app.go index 0ec2502ba..d93765407 100755 --- a/java/app.go +++ b/java/app.go @@ -542,7 +542,7 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { dexJarFile := a.dexBuildActions(ctx) - jniLibs, certificateDeps := collectAppDeps(ctx, a.shouldEmbedJnis(ctx)) + jniLibs, certificateDeps := collectAppDeps(ctx, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis)) jniJarFile := a.jniBuildActions(jniLibs, ctx) if ctx.Failed() { @@ -592,7 +592,8 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { } } -func collectAppDeps(ctx android.ModuleContext, shouldCollectRecursiveNativeDeps bool) ([]jniLib, []Certificate) { +func collectAppDeps(ctx android.ModuleContext, shouldCollectRecursiveNativeDeps bool, + checkNativeSdkVersion bool) ([]jniLib, []Certificate) { var jniLibs []jniLib var certificates []Certificate seenModulePaths := make(map[string]bool) @@ -614,6 +615,18 @@ func collectAppDeps(ctx android.ModuleContext, shouldCollectRecursiveNativeDeps } seenModulePaths[path.String()] = true + if checkNativeSdkVersion { + if app, ok := ctx.Module().(interface{ sdkVersion() sdkSpec }); ok { + if app.sdkVersion().specified() && + app.sdkVersion().kind != sdkCorePlatform && + dep.SdkVersion() == "" { + ctx.PropertyErrorf("jni_libs", + "JNI dependency %q uses platform APIs, but this module does not", + otherName) + } + } + } + if lib.Valid() { jniLibs = append(jniLibs, jniLib{ name: ctx.OtherModuleName(module), @@ -1133,7 +1146,7 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext ctx.ModuleErrorf("One and only one of certficate, presigned, and default_dev_cert properties must be set") } - _, certificates := collectAppDeps(ctx, false) + _, certificates := collectAppDeps(ctx, false, false) // TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK // TODO: LOCAL_PACKAGE_SPLITS @@ -1405,7 +1418,7 @@ func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleC r.aapt.buildActions(ctx, r, "--no-resource-deduping", "--no-resource-removal") // Sign the built package - _, certificates := collectAppDeps(ctx, false) + _, certificates := collectAppDeps(ctx, false, false) certificates = processMainCert(r.ModuleBase, String(r.properties.Certificate), certificates, ctx) signed := android.PathForModuleOut(ctx, "signed", r.Name()+".apk") SignAppPackage(ctx, signed, r.aapt.exportPackage, certificates, nil) diff --git a/java/app_test.go b/java/app_test.go index 39460dce6..388014f46 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -1005,6 +1005,7 @@ func TestJNIPackaging(t *testing.T) { name: "libjni", system_shared_libs: [], stl: "none", + sdk_version: "current", } android_app { @@ -2274,6 +2275,7 @@ func TestEmbedNotice(t *testing.T) { system_shared_libs: [], stl: "none", notice: "LIB_NOTICE", + sdk_version: "current", } java_library { From 01fd7ccbc71c9eee7c56c73f05c4e7833a795aac Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 19 Feb 2020 16:54:04 -0800 Subject: [PATCH 2/2] Add sdk mutator for native modules Compiling native modules against the NDK disables platform features like ASAN. For anything shipped on the system image there is no reason to compile against the NDK. Add a new mutator to Soong that creates a platform and an SDK variant for modules that set sdk_version, and ignore sdk_version for the platform variant. The SDK variant will be used for embedding in APKs that may be installed on older platforms. Apexes use their own variants that enforce backwards compatibility. Test: sdk_test.go Test: TestJNIPackaging Bug: 149591340 Change-Id: I7d72934aaee2e1326cc0ba5f29f51f14feec4521 Merged-In: I7d72934aaee2e1326cc0ba5f29f51f14feec4521 (cherry picked from commit 82e192c3aeae55337e335101ba83126decd4ddac) --- Android.bp | 1 + android/neverallow.go | 44 ++++++++++++++++ android/neverallow_test.go | 50 ++++++++++++++++++ apex/apex_test.go | 2 +- apex/vndk_test.go | 2 + cc/androidmk.go | 26 ++++++++++ cc/cc.go | 46 ++++++++++++++--- cc/library.go | 2 +- cc/linkable.go | 2 + cc/linker.go | 11 ++++ cc/ndk_library.go | 3 ++ cc/ndk_prebuilt.go | 12 +++-- cc/sdk.go | 65 +++++++++++++++++++++++ cc/sdk_test.go | 102 +++++++++++++++++++++++++++++++++++++ cc/stl.go | 6 ++- cc/testing.go | 15 ++++++ cc/toolchain_library.go | 1 + java/app.go | 7 +++ java/app_test.go | 13 +++-- rust/rust.go | 8 +++ 20 files changed, 400 insertions(+), 18 deletions(-) create mode 100644 cc/sdk.go create mode 100644 cc/sdk_test.go diff --git a/Android.bp b/Android.bp index 062147526..21f29f5b4 100644 --- a/Android.bp +++ b/Android.bp @@ -186,6 +186,7 @@ bootstrap_go_package { "cc/rs.go", "cc/sanitize.go", "cc/sabi.go", + "cc/sdk.go", "cc/snapshot_utils.go", "cc/stl.go", "cc/strip.go", diff --git a/android/neverallow.go b/android/neverallow.go index 4d3a16fb0..cf09792e0 100644 --- a/android/neverallow.go +++ b/android/neverallow.go @@ -54,6 +54,7 @@ func init() { AddNeverAllowRules(createLibcoreRules()...) AddNeverAllowRules(createMediaRules()...) AddNeverAllowRules(createJavaDeviceForHostRules()...) + AddNeverAllowRules(createCcSdkVariantRules()...) } // Add a NeverAllow rule to the set of rules to apply. @@ -177,6 +178,37 @@ func createJavaDeviceForHostRules() []Rule { } } +func createCcSdkVariantRules() []Rule { + sdkVersionOnlyWhitelist := []string{ + // derive_sdk_prefer32 has stem: "derive_sdk" which conflicts with the derive_sdk. + // This sometimes works because the APEX modules that contain derive_sdk and + // derive_sdk_prefer32 suppress the platform installation rules, but fails when + // the APEX modules contain the SDK variant and the platform variant still exists. + "frameworks/base/apex/sdkextensions/derive_sdk", + } + + platformVariantPropertiesWhitelist := []string{ + // android_native_app_glue and libRSSupport use native_window.h but target old + // sdk versions (minimum and 9 respectively) where libnativewindow didn't exist, + // so they can't add libnativewindow to shared_libs to get the header directory + // for the platform variant. Allow them to use the platform variant + // property to set shared_libs. + "prebuilts/ndk", + "frameworks/rs", + } + + return []Rule{ + NeverAllow(). + NotIn(sdkVersionOnlyWhitelist...). + WithMatcher("sdk_variant_only", isSetMatcherInstance). + Because("sdk_variant_only can only be used in whitelisted projects"), + NeverAllow(). + NotIn(platformVariantPropertiesWhitelist...). + WithMatcher("platform.shared_libs", isSetMatcherInstance). + Because("platform variant properties can only be used in whitelisted projects"), + } +} + func neverallowMutator(ctx BottomUpMutatorContext) { m, ok := ctx.Module().(Module) if !ok { @@ -268,6 +300,18 @@ func (m *regexMatcher) String() string { return ".regexp(" + m.re.String() + ")" } +type isSetMatcher struct{} + +func (m *isSetMatcher) Test(value string) bool { + return value != "" +} + +func (m *isSetMatcher) String() string { + return ".is-set" +} + +var isSetMatcherInstance = &isSetMatcher{} + type ruleProperty struct { fields []string // e.x.: Vndk.Enabled matcher ValueMatcher diff --git a/android/neverallow_test.go b/android/neverallow_test.go index 83b225090..2fc42e31f 100644 --- a/android/neverallow_test.go +++ b/android/neverallow_test.go @@ -259,6 +259,50 @@ var neverallowTests = []struct { }`), }, }, + // CC sdk rule tests + { + name: `"sdk_variant_only" outside whitelist`, + fs: map[string][]byte{ + "Android.bp": []byte(` + cc_library { + name: "outside_whitelist", + sdk_version: "current", + sdk_variant_only: true, + }`), + }, + expectedErrors: []string{ + `module "outside_whitelist": violates neverallow`, + }, + }, + { + name: `"sdk_variant_only: false" outside whitelist`, + fs: map[string][]byte{ + "Android.bp": []byte(` + cc_library { + name: "outside_whitelist", + sdk_version: "current", + sdk_variant_only: false, + }`), + }, + expectedErrors: []string{ + `module "outside_whitelist": violates neverallow`, + }, + }, + { + name: `"platform" outside whitelist`, + fs: map[string][]byte{ + "Android.bp": []byte(` + cc_library { + name: "outside_whitelist", + platform: { + shared_libs: ["libfoo"], + }, + }`), + }, + expectedErrors: []string{ + `module "outside_whitelist": violates neverallow`, + }, + }, } func TestNeverallow(t *testing.T) { @@ -299,6 +343,8 @@ type mockCcLibraryProperties struct { Include_dirs []string Vendor_available *bool Static_libs []string + Sdk_version *string + Sdk_variant_only *bool Vndk struct { Enabled *bool @@ -315,6 +361,10 @@ type mockCcLibraryProperties struct { Cflags []string } } + + Platform struct { + Shared_libs []string + } } type mockCcLibraryModule struct { diff --git a/apex/apex_test.go b/apex/apex_test.go index 056153f78..4c0eadab0 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -3373,7 +3373,7 @@ func TestApexWithApps(t *testing.T) { } // JNI libraries including transitive deps are for _, jni := range []string{"libjni", "libfoo"} { - jniOutput := ctx.ModuleForTests(jni, "android_arm64_armv8-a_shared_myapex").Module().(*cc.Module).OutputFile() + jniOutput := ctx.ModuleForTests(jni, "android_arm64_armv8-a_sdk_shared_myapex").Module().(*cc.Module).OutputFile() // ... embedded inside APK (jnilibs.zip) ensureListContains(t, appZipRule.Implicits.Strings(), jniOutput.String()) // ... and not directly inside the APEX diff --git a/apex/vndk_test.go b/apex/vndk_test.go index 54447fa79..523ac2630 100644 --- a/apex/vndk_test.go +++ b/apex/vndk_test.go @@ -143,6 +143,7 @@ func TestVndkApexUsesVendorVariant(t *testing.T) { system_shared_libs: [], stl: "none", notice: "custom_notice", + sdk_version: "current", } cc_library { name: "libprofile-clang-extras_ndk", @@ -151,6 +152,7 @@ func TestVndkApexUsesVendorVariant(t *testing.T) { system_shared_libs: [], stl: "none", notice: "custom_notice", + sdk_version: "current", } `, func(fs map[string][]byte, config android.Config) { config.TestProductVariables.Native_coverage = proptools.BoolPtr(true) diff --git a/cc/androidmk.go b/cc/androidmk.go index 1c90aaf79..5438b149d 100644 --- a/cc/androidmk.go +++ b/cc/androidmk.go @@ -29,6 +29,7 @@ var ( vendorSuffix = ".vendor" ramdiskSuffix = ".ramdisk" recoverySuffix = ".recovery" + sdkSuffix = ".sdk" ) type AndroidMkContext interface { @@ -103,6 +104,28 @@ func (c *Module) AndroidMkEntries() []android.AndroidMkEntries { } } } + if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake { + // Make the SDK variant uninstallable so that there are not two rules to install + // to the same location. + entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true) + // Add the unsuffixed name to SOONG_SDK_VARIANT_MODULES so that Make can rewrite + // dependencies to the .sdk suffix when building a module that uses the SDK. + entries.SetString("SOONG_SDK_VARIANT_MODULES", + "$(SOONG_SDK_VARIANT_MODULES) $(patsubst %.sdk,%,$(LOCAL_MODULE))") + } + }, + }, + ExtraFooters: []android.AndroidMkExtraFootersFunc{ + func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) { + if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake && + c.CcLibraryInterface() && c.Shared() { + // Using the SDK variant as a JNI library needs a copy of the .so that + // is not named .sdk.so so that it can be packaged into the APK with + // the right name. + fmt.Fprintln(w, "$(eval $(call copy-one-file,", + "$(LOCAL_BUILT_MODULE),", + "$(patsubst %.sdk.so,%.so,$(LOCAL_BUILT_MODULE))))") + } }, }, } @@ -397,6 +420,9 @@ func (library *toolchainLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, } func (installer *baseInstaller) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) { + if installer.path == (android.InstallPath{}) { + return + } // Soong installation is only supported for host modules. Have Make // installation trigger Soong installation. if ctx.Target().Os.Class == android.Host { diff --git a/cc/cc.go b/cc/cc.go index 9baecebdb..39848092a 100644 --- a/cc/cc.go +++ b/cc/cc.go @@ -42,6 +42,7 @@ func RegisterCCBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("cc_defaults", defaultsFactory) ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) { + ctx.BottomUp("sdk", sdkMutator).Parallel() ctx.BottomUp("vndk", VndkMutator).Parallel() ctx.BottomUp("link", LinkageMutator).Parallel() ctx.BottomUp("ndk_api", NdkApiMutator).Parallel() @@ -208,12 +209,16 @@ type BaseProperties struct { // Deprecated. true is the default, false is invalid. Clang *bool `android:"arch_variant"` - // Minimum sdk version supported when compiling against the ndk + // Minimum sdk version supported when compiling against the ndk. Setting this property causes + // two variants to be built, one for the platform and one for apps. Sdk_version *string // Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX). Min_sdk_version *string + // If true, always create an sdk variant and don't create a platform variant. + Sdk_variant_only *bool + AndroidMkSharedLibs []string `blueprint:"mutated"` AndroidMkStaticLibs []string `blueprint:"mutated"` AndroidMkRuntimeLibs []string `blueprint:"mutated"` @@ -255,6 +260,16 @@ type BaseProperties struct { SnapshotRuntimeLibs []string `blueprint:"mutated"` Installable *bool + + // Set by factories of module types that can only be referenced from variants compiled against + // the SDK. + AlwaysSdk bool `blueprint:"mutated"` + + // Variant is an SDK variant created by sdkMutator + IsSdkVariant bool `blueprint:"mutated"` + // Set when both SDK and platform variants are exported to Make to trigger renaming the SDK + // variant to have a ".sdk" suffix. + SdkAndPlatformVariantVisibleToMake bool `blueprint:"mutated"` } type VendorProperties struct { @@ -534,7 +549,10 @@ func (c *Module) Shared() bool { } func (c *Module) SelectedStl() string { - return c.stl.Properties.SelectedStl + if c.stl != nil { + return c.stl.Properties.SelectedStl + } + return "" } func (c *Module) ToolchainLibrary() bool { @@ -562,6 +580,10 @@ func (c *Module) SdkVersion() string { return String(c.Properties.Sdk_version) } +func (c *Module) AlwaysSdk() bool { + return c.Properties.AlwaysSdk || Bool(c.Properties.Sdk_variant_only) +} + func (c *Module) IncludeDirs() android.Paths { if c.linker != nil { if library, ok := c.linker.(exportedFlagsProducer); ok { @@ -821,6 +843,17 @@ func (c *Module) UseVndk() bool { return c.Properties.VndkVersion != "" } +func (c *Module) canUseSdk() bool { + return c.Os() == android.Android && !c.UseVndk() && !c.InRamdisk() && !c.InRecovery() +} + +func (c *Module) UseSdk() bool { + if c.canUseSdk() { + return String(c.Properties.Sdk_version) != "" + } + return false +} + func (c *Module) isCoverageVariant() bool { return c.coverage.Properties.IsCoverageVariant } @@ -1078,14 +1111,11 @@ func (ctx *moduleContextImpl) header() bool { } func (ctx *moduleContextImpl) canUseSdk() bool { - return ctx.ctx.Device() && !ctx.useVndk() && !ctx.inRamdisk() && !ctx.inRecovery() && !ctx.ctx.Fuchsia() + return ctx.mod.canUseSdk() } func (ctx *moduleContextImpl) useSdk() bool { - if ctx.canUseSdk() { - return String(ctx.mod.Properties.Sdk_version) != "" - } - return false + return ctx.mod.UseSdk() } func (ctx *moduleContextImpl) sdkVersion() string { @@ -1404,6 +1434,8 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { c.Properties.SubName += ramdiskSuffix } else if c.InRecovery() && !c.OnlyInRecovery() { c.Properties.SubName += recoverySuffix + } else if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake { + c.Properties.SubName += sdkSuffix } ctx := &moduleContext{ diff --git a/cc/library.go b/cc/library.go index c3b20b699..ce814b11c 100644 --- a/cc/library.go +++ b/cc/library.go @@ -1224,7 +1224,7 @@ func (library *libraryDecorator) install(ctx ModuleContext, file android.Path) { if Bool(library.Properties.Static_ndk_lib) && library.static() && !ctx.useVndk() && !ctx.inRamdisk() && !ctx.inRecovery() && ctx.Device() && library.baseLinker.sanitize.isUnsanitizedVariant() && - !library.buildStubs() { + !library.buildStubs() && ctx.sdkVersion() == "" { installPath := getNdkSysrootBase(ctx).Join( ctx, "usr/lib", config.NDKTriple(ctx.toolchain()), file.Base()) diff --git a/cc/linkable.go b/cc/linkable.go index 9147681d0..4a70d48f7 100644 --- a/cc/linkable.go +++ b/cc/linkable.go @@ -47,12 +47,14 @@ type LinkableInterface interface { InRecovery() bool OnlyInRecovery() bool + UseSdk() bool UseVndk() bool MustUseVendorVariant() bool IsVndk() bool HasVendorVariant() bool SdkVersion() string + AlwaysSdk() bool ToolchainLibrary() bool NdkPrebuiltStl() bool diff --git a/cc/linker.go b/cc/linker.go index aa2d0ab24..9b2c1e71e 100644 --- a/cc/linker.go +++ b/cc/linker.go @@ -158,6 +158,13 @@ type BaseLinkerProperties struct { // the ramdisk variant of the C/C++ module. Exclude_static_libs []string } + Platform struct { + // list of shared libs that should be use to build the platform variant + // of a module that sets sdk_version. This should rarely be necessary, + // in most cases the same libraries are available for the SDK and platform + // variants. + Shared_libs []string + } } // make android::build:GetBuildNumber() available containing the build ID. @@ -255,6 +262,10 @@ func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs) } + if !ctx.useSdk() { + deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Target.Platform.Shared_libs...) + } + if ctx.toolchain().Bionic() { // libclang_rt.builtins and libatomic have to be last on the command line if !Bool(linker.Properties.No_libcrt) { diff --git a/cc/ndk_library.go b/cc/ndk_library.go index eb8e9d3be..119ca403d 100644 --- a/cc/ndk_library.go +++ b/cc/ndk_library.go @@ -385,6 +385,9 @@ func newStubLibrary() *Module { module.linker = stub module.installer = stub + module.Properties.AlwaysSdk = true + module.Properties.Sdk_version = StringPtr("current") + module.AddProperties(&stub.properties, &library.MutatedProperties) return module diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go index 3a630d2c0..c4d770837 100644 --- a/cc/ndk_prebuilt.go +++ b/cc/ndk_prebuilt.go @@ -76,6 +76,8 @@ func NdkPrebuiltObjectFactory() android.Module { baseLinker: NewBaseLinker(nil), }, } + module.Properties.AlwaysSdk = true + module.Properties.Sdk_version = StringPtr("current") module.Properties.HideFromMake = true return module.Init() } @@ -125,10 +127,9 @@ func NdkPrebuiltSharedStlFactory() android.Module { libraryDecorator: library, } module.installer = nil - minVersionString := "minimum" - noStlString := "none" - module.Properties.Sdk_version = &minVersionString - module.stl.Properties.Stl = &noStlString + module.Properties.Sdk_version = StringPtr("minimum") + module.Properties.AlwaysSdk = true + module.stl.Properties.Stl = StringPtr("none") return module.Init() } @@ -145,6 +146,9 @@ func NdkPrebuiltStaticStlFactory() android.Module { } module.installer = nil module.Properties.HideFromMake = true + module.Properties.AlwaysSdk = true + module.Properties.Sdk_version = StringPtr("current") + module.stl.Properties.Stl = StringPtr("none") module.ModuleBase.EnableNativeBridgeSupportByDefault() return module.Init() } diff --git a/cc/sdk.go b/cc/sdk.go new file mode 100644 index 000000000..d05a04a12 --- /dev/null +++ b/cc/sdk.go @@ -0,0 +1,65 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cc + +import ( + "android/soong/android" + "android/soong/genrule" +) + +// sdkMutator sets a creates a platform and an SDK variant for modules +// that set sdk_version, and ignores sdk_version for the platform +// variant. The SDK variant will be used for embedding in APKs +// that may be installed on older platforms. Apexes use their own +// variants that enforce backwards compatibility. +func sdkMutator(ctx android.BottomUpMutatorContext) { + if ctx.Os() != android.Android { + return + } + + switch m := ctx.Module().(type) { + case LinkableInterface: + if m.AlwaysSdk() { + if !m.UseSdk() { + ctx.ModuleErrorf("UseSdk() must return true when AlwaysSdk is set, did the factory forget to set Sdk_version?") + } + ctx.CreateVariations("sdk") + } else if m.UseSdk() { + modules := ctx.CreateVariations("", "sdk") + modules[0].(*Module).Properties.Sdk_version = nil + modules[1].(*Module).Properties.IsSdkVariant = true + + if ctx.Config().UnbundledBuild() { + modules[0].(*Module).Properties.HideFromMake = true + } else { + modules[1].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true + modules[1].(*Module).Properties.PreventInstall = true + } + ctx.AliasVariation("") + } else { + ctx.CreateVariations("") + ctx.AliasVariation("") + } + case *genrule.Module: + if p, ok := m.Extra.(*GenruleExtraProperties); ok { + if String(p.Sdk_version) != "" { + ctx.CreateVariations("", "sdk") + } else { + ctx.CreateVariations("") + } + ctx.AliasVariation("") + } + } +} diff --git a/cc/sdk_test.go b/cc/sdk_test.go new file mode 100644 index 000000000..5a3c181dc --- /dev/null +++ b/cc/sdk_test.go @@ -0,0 +1,102 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cc + +import ( + "testing" + + "android/soong/android" +) + +func TestSdkMutator(t *testing.T) { + bp := ` + cc_library { + name: "libsdk", + shared_libs: ["libsdkdep"], + sdk_version: "current", + stl: "c++_shared", + } + + cc_library { + name: "libsdkdep", + sdk_version: "current", + stl: "c++_shared", + } + + cc_library { + name: "libplatform", + shared_libs: ["libsdk"], + stl: "libc++", + } + + cc_binary { + name: "platformbinary", + shared_libs: ["libplatform"], + stl: "libc++", + } + + cc_binary { + name: "sdkbinary", + shared_libs: ["libsdk"], + sdk_version: "current", + stl: "libc++", + } + ` + + assertDep := func(t *testing.T, from, to android.TestingModule) { + t.Helper() + found := false + + var toFile android.Path + m := to.Module().(*Module) + if toc := m.Toc(); toc.Valid() { + toFile = toc.Path() + } else { + toFile = m.outputFile.Path() + } + + rule := from.Description("link") + for _, dep := range rule.Implicits { + if dep.String() == toFile.String() { + found = true + } + } + if !found { + t.Errorf("expected %q in %q", toFile.String(), rule.Implicits.Strings()) + } + } + + ctx := testCc(t, bp) + + libsdkNDK := ctx.ModuleForTests("libsdk", "android_arm64_armv8-a_sdk_shared") + libsdkPlatform := ctx.ModuleForTests("libsdk", "android_arm64_armv8-a_shared") + libsdkdepNDK := ctx.ModuleForTests("libsdkdep", "android_arm64_armv8-a_sdk_shared") + libsdkdepPlatform := ctx.ModuleForTests("libsdkdep", "android_arm64_armv8-a_shared") + libplatform := ctx.ModuleForTests("libplatform", "android_arm64_armv8-a_shared") + platformbinary := ctx.ModuleForTests("platformbinary", "android_arm64_armv8-a") + sdkbinary := ctx.ModuleForTests("sdkbinary", "android_arm64_armv8-a_sdk") + + libcxxNDK := ctx.ModuleForTests("ndk_libc++_shared", "android_arm64_armv8-a_sdk_shared") + libcxxPlatform := ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared") + + assertDep(t, libsdkNDK, libsdkdepNDK) + assertDep(t, libsdkPlatform, libsdkdepPlatform) + assertDep(t, libplatform, libsdkPlatform) + assertDep(t, platformbinary, libplatform) + assertDep(t, sdkbinary, libsdkNDK) + + assertDep(t, libsdkNDK, libcxxNDK) + assertDep(t, libsdkPlatform, libcxxPlatform) +} diff --git a/cc/stl.go b/cc/stl.go index eda8a4ffc..34ff30c52 100644 --- a/cc/stl.go +++ b/cc/stl.go @@ -115,9 +115,13 @@ func (stl *stl) begin(ctx BaseModuleContext) { switch s { case "libc++", "libc++_static": return s + case "c++_shared": + return "libc++" + case "c++_static": + return "libc++_static" case "none": return "" - case "": + case "", "system": if ctx.static() { return "libc++_static" } else { diff --git a/cc/testing.go b/cc/testing.go index f85795b33..53f09955a 100644 --- a/cc/testing.go +++ b/cc/testing.go @@ -152,6 +152,7 @@ func GatherRequiredDepsForTest(oses ...android.OsType) string { name: "libgcc_stripped", vendor_available: true, recovery_available: true, + sdk_version: "current", src: "", } @@ -169,6 +170,7 @@ func GatherRequiredDepsForTest(oses ...android.OsType) string { llndk_library { name: "libc", symbol_file: "", + sdk_version: "current", } cc_library { name: "libm", @@ -188,6 +190,7 @@ func GatherRequiredDepsForTest(oses ...android.OsType) string { llndk_library { name: "libm", symbol_file: "", + sdk_version: "current", } cc_library { name: "libdl", @@ -207,6 +210,7 @@ func GatherRequiredDepsForTest(oses ...android.OsType) string { llndk_library { name: "libdl", symbol_file: "", + sdk_version: "current", } cc_library { name: "libft2", @@ -219,6 +223,7 @@ func GatherRequiredDepsForTest(oses ...android.OsType) string { name: "libft2", symbol_file: "", vendor_available: false, + sdk_version: "current", } cc_library { name: "libc++_static", @@ -375,6 +380,16 @@ func GatherRequiredDepsForTest(oses ...android.OsType) string { sdk_version: "27", } + ndk_prebuilt_object { + name: "ndk_crtbegin_dynamic.27", + sdk_version: "27", + } + + ndk_prebuilt_object { + name: "ndk_crtend_android.27", + sdk_version: "27", + } + ndk_prebuilt_shared_stl { name: "ndk_libc++_shared", } diff --git a/cc/toolchain_library.go b/cc/toolchain_library.go index dfc6f7679..042e012d9 100644 --- a/cc/toolchain_library.go +++ b/cc/toolchain_library.go @@ -67,6 +67,7 @@ func ToolchainLibraryFactory() android.Module { module.stl = nil module.sanitize = nil module.installer = nil + module.Properties.Sdk_version = StringPtr("current") return module.Init() } diff --git a/java/app.go b/java/app.go index d93765407..f81a7646b 100755 --- a/java/app.go +++ b/java/app.go @@ -218,6 +218,13 @@ func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) { for _, jniTarget := range ctx.MultiTargets() { variation := append(jniTarget.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"}) + + // If the app builds against an Android SDK use the SDK variant of JNI dependencies + // unless jni_uses_platform_apis is set. + if a.sdkVersion().specified() && a.sdkVersion().kind != sdkCorePlatform && + !Bool(a.appProperties.Jni_uses_platform_apis) { + variation = append(variation, blueprint.Variation{Mutator: "sdk", Variation: "sdk"}) + } ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...) } diff --git a/java/app_test.go b/java/app_test.go index 388014f46..49dbd1270 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -893,6 +893,7 @@ func TestJNIABI(t *testing.T) { cc_library { name: "libjni", system_shared_libs: [], + sdk_version: "current", stl: "none", } @@ -1030,26 +1031,26 @@ func TestJNIPackaging(t *testing.T) { android_test { name: "test", - sdk_version: "core_platform", + sdk_version: "current", jni_libs: ["libjni"], } android_test { name: "test_noembed", - sdk_version: "core_platform", + sdk_version: "current", jni_libs: ["libjni"], use_embedded_native_libs: false, } android_test_helper_app { name: "test_helper", - sdk_version: "core_platform", + sdk_version: "current", jni_libs: ["libjni"], } android_test_helper_app { name: "test_helper_noembed", - sdk_version: "core_platform", + sdk_version: "current", jni_libs: ["libjni"], use_embedded_native_libs: false, } @@ -1081,6 +1082,10 @@ func TestJNIPackaging(t *testing.T) { if g, w := !strings.Contains(jniLibZip.Args["jarArgs"], "-L 0"), test.compressed; g != w { t.Errorf("expected jni compressed %v, got %v", w, g) } + + if !strings.Contains(jniLibZip.Implicits[0].String(), "_sdk_") { + t.Errorf("expected input %q to use sdk variant", jniLibZip.Implicits[0].String()) + } } }) } diff --git a/rust/rust.go b/rust/rust.go index 17734f9d3..5cc884572 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -164,6 +164,10 @@ func (mod *Module) OnlyInRecovery() bool { return false } +func (mod *Module) UseSdk() bool { + return false +} + func (mod *Module) UseVndk() bool { return false } @@ -184,6 +188,10 @@ func (mod *Module) SdkVersion() string { return "" } +func (mod *Module) AlwaysSdk() bool { + return false +} + func (mod *Module) ToolchainLibrary() bool { return false }