platform_build_soong/java/app.go
Colin Cross 43f08db29e Dexpreopt soong modules inside soong
Port the dexpreopt logic from Make to the dexpreopt package in Soong,
and use it to dexpreopt Soong modules.  The same package is also
compiled into the dexpreopt_gen binary to generate dexpreopt scripts
for Make modules.

This relands Ib67e2febf9ed921f06e8a86b9ec945c80dff35eb and
I462182638bd57b1367b5bfb0718e975c11ae66f7, along with multiple fixes
to depsfile generation in dexpreopt_gen that caused .odex files for
modules in defined make to be missing dependencies on boot.art, and
a fix to not dexpreopt and strip tests.

Bug: 119412419
Bug: 120273280
Test: no differences to dexpreopt outputs on aosp_sailfish system/,
      only expected changes to dexpreopt outputs on system_other
      (.vdex files for privileged Soong modules no longer incorrectly
      contain .dex contents).
Test: OUT_DIR=$PWD/out m
Test: NINJA_ARGS="-t deps out/target/product/sailfish/obj/APPS/Contacts_intermediates/dexpreopt.zip" m
Change-Id: I6bb2c971cee65d2338839753aa0d84939f335b1b
2018-12-15 19:07:54 -08:00

463 lines
14 KiB
Go

// Copyright 2015 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 java
// This file contains the module types for compiling Android apps.
import (
"path/filepath"
"strings"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
"android/soong/android"
"android/soong/cc"
"android/soong/tradefed"
)
func init() {
android.RegisterModuleType("android_app", AndroidAppFactory)
android.RegisterModuleType("android_test", AndroidTestFactory)
android.RegisterModuleType("android_test_helper_app", AndroidTestHelperAppFactory)
android.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory)
}
// AndroidManifest.xml merging
// package splits
type appProperties struct {
// The name of a certificate in the default certificate directory, blank to use the default product certificate,
// or an android_app_certificate module name in the form ":module".
Certificate *string
// Names of extra android_app_certificate modules to sign the apk with in the form ":module".
Additional_certificates []string
// If set, create package-export.apk, which other packages can
// use to get PRODUCT-agnostic resource data like IDs and type definitions.
Export_package_resources *bool
// Specifies that this app should be installed to the priv-app directory,
// where the system will grant it additional privileges not available to
// normal apps.
Privileged *bool
// list of resource labels to generate individual resource packages
Package_splits []string
// Names of modules to be overridden. Listed modules can only be other binaries
// (in Make or Soong).
// This does not completely prevent installation of the overridden binaries, but if both
// binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
// from PRODUCT_PACKAGES.
Overrides []string
// list of native libraries that will be provided in or alongside the resulting jar
Jni_libs []string `android:"arch_variant"`
EmbedJNI bool `blueprint:"mutated"`
}
type AndroidApp struct {
Library
aapt
certificate Certificate
appProperties appProperties
extraLinkFlags []string
installJniLibs []jniLib
bundleFile android.Path
}
func (a *AndroidApp) ExportedProguardFlagFiles() android.Paths {
return nil
}
func (a *AndroidApp) ExportedStaticPackages() android.Paths {
return nil
}
func (a *AndroidApp) ExportedManifest() android.Path {
return a.manifestPath
}
var _ AndroidLibraryDependency = (*AndroidApp)(nil)
type Certificate struct {
Pem, Key android.Path
}
func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) {
a.Module.deps(ctx)
if !Bool(a.properties.No_framework_libs) && !Bool(a.properties.No_standard_libs) {
a.aapt.deps(ctx, sdkContext(a))
}
for _, jniTarget := range ctx.MultiTargets() {
variation := []blueprint.Variation{
{Mutator: "arch", Variation: jniTarget.String()},
{Mutator: "link", Variation: "shared"},
}
tag := &jniDependencyTag{
target: jniTarget,
}
ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...)
}
cert := android.SrcIsModule(String(a.appProperties.Certificate))
if cert != "" {
ctx.AddDependency(ctx.Module(), certificateTag, cert)
}
for _, cert := range a.appProperties.Additional_certificates {
cert = android.SrcIsModule(cert)
if cert != "" {
ctx.AddDependency(ctx.Module(), certificateTag, cert)
} else {
ctx.PropertyErrorf("additional_certificates",
`must be names of android_app_certificate modules in the form ":module"`)
}
}
}
func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
a.generateAndroidBuildActions(ctx)
}
// Returns whether this module should have the dex file stored uncompressed in the APK.
func (a *AndroidApp) shouldUncompressDex(ctx android.ModuleContext) bool {
if ctx.Config().UnbundledBuild() {
return false
}
// Uncompress dex in APKs of privileged apps, and modules used by privileged apps.
return ctx.Config().UncompressPrivAppDex() &&
(Bool(a.appProperties.Privileged) ||
inList(ctx.ModuleName(), ctx.Config().ModulesLoadedByPrivilegedModules()))
}
func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
linkFlags := append([]string(nil), a.extraLinkFlags...)
hasProduct := false
for _, f := range a.aaptProperties.Aaptflags {
if strings.HasPrefix(f, "--product") {
hasProduct = true
}
}
// Product characteristics
if !hasProduct && len(ctx.Config().ProductAAPTCharacteristics()) > 0 {
linkFlags = append(linkFlags, "--product", ctx.Config().ProductAAPTCharacteristics())
}
if !Bool(a.aaptProperties.Aapt_include_all_resources) {
// Product AAPT config
for _, aaptConfig := range ctx.Config().ProductAAPTConfig() {
linkFlags = append(linkFlags, "-c", aaptConfig)
}
// Product AAPT preferred config
if len(ctx.Config().ProductAAPTPreferredConfig()) > 0 {
linkFlags = append(linkFlags, "--preferred-density", ctx.Config().ProductAAPTPreferredConfig())
}
}
// TODO: LOCAL_PACKAGE_OVERRIDES
// $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \
a.aapt.buildActions(ctx, sdkContext(a), linkFlags...)
// apps manifests are handled by aapt, don't let Module see them
a.properties.Manifest = nil
var staticLibProguardFlagFiles android.Paths
ctx.VisitDirectDeps(func(m android.Module) {
if lib, ok := m.(AndroidLibraryDependency); ok && ctx.OtherModuleDependencyTag(m) == staticLibTag {
staticLibProguardFlagFiles = append(staticLibProguardFlagFiles, lib.ExportedProguardFlagFiles()...)
}
})
staticLibProguardFlagFiles = android.FirstUniquePaths(staticLibProguardFlagFiles)
a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, staticLibProguardFlagFiles...)
a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, a.proguardOptionsFile)
a.deviceProperties.UncompressDex = a.shouldUncompressDex(ctx)
var installDir string
if ctx.ModuleName() == "framework-res" {
// framework-res.apk is installed as system/framework/framework-res.apk
installDir = "framework"
} else if Bool(a.appProperties.Privileged) {
installDir = filepath.Join("priv-app", ctx.ModuleName())
} else {
installDir = filepath.Join("app", ctx.ModuleName())
}
a.dexpreopter.installPath = android.PathForModuleInstall(ctx, installDir, ctx.ModuleName()+".apk")
a.dexpreopter.isPrivApp = Bool(a.appProperties.Privileged)
if ctx.ModuleName() != "framework-res" {
a.Module.compile(ctx, a.aaptSrcJar)
}
dexJarFile := a.maybeStrippedDexJarFile
var certificates []Certificate
var jniJarFile android.WritablePath
jniLibs, certificateDeps := a.collectAppDeps(ctx)
if len(jniLibs) > 0 {
embedJni := ctx.Config().UnbundledBuild() || a.appProperties.EmbedJNI
if embedJni {
jniJarFile = android.PathForModuleOut(ctx, "jnilibs.zip")
TransformJniLibsToJar(ctx, jniJarFile, jniLibs)
} else {
a.installJniLibs = jniLibs
}
}
if ctx.Failed() {
return
}
cert := String(a.appProperties.Certificate)
certModule := android.SrcIsModule(cert)
if certModule != "" {
a.certificate = certificateDeps[0]
certificateDeps = certificateDeps[1:]
} else if cert != "" {
defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
a.certificate = Certificate{
defaultDir.Join(ctx, cert+".x509.pem"),
defaultDir.Join(ctx, cert+".pk8"),
}
} else {
pem, key := ctx.Config().DefaultAppCertificate(ctx)
a.certificate = Certificate{pem, key}
}
certificates = append([]Certificate{a.certificate}, certificateDeps...)
packageFile := android.PathForModuleOut(ctx, "package.apk")
CreateAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates)
a.outputFile = packageFile
bundleFile := android.PathForModuleOut(ctx, "base.zip")
BuildBundleModule(ctx, bundleFile, a.exportPackage, jniJarFile, dexJarFile)
a.bundleFile = bundleFile
if ctx.ModuleName() == "framework-res" {
// framework-res.apk is installed as system/framework/framework-res.apk
ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"), ctx.ModuleName()+".apk", a.outputFile)
} else if Bool(a.appProperties.Privileged) {
ctx.InstallFile(android.PathForModuleInstall(ctx, "priv-app", ctx.ModuleName()), ctx.ModuleName()+".apk", a.outputFile)
} else {
ctx.InstallFile(android.PathForModuleInstall(ctx, "app", ctx.ModuleName()), ctx.ModuleName()+".apk", a.outputFile)
}
}
func (a *AndroidApp) collectAppDeps(ctx android.ModuleContext) ([]jniLib, []Certificate) {
var jniLibs []jniLib
var certificates []Certificate
ctx.VisitDirectDeps(func(module android.Module) {
otherName := ctx.OtherModuleName(module)
tag := ctx.OtherModuleDependencyTag(module)
if jniTag, ok := tag.(*jniDependencyTag); ok {
if dep, ok := module.(*cc.Module); ok {
lib := dep.OutputFile()
if lib.Valid() {
jniLibs = append(jniLibs, jniLib{
name: ctx.OtherModuleName(module),
path: lib.Path(),
target: jniTag.target,
})
} else {
ctx.ModuleErrorf("dependency %q missing output file", otherName)
}
} else {
ctx.ModuleErrorf("jni_libs dependency %q must be a cc library", otherName)
}
} else if tag == certificateTag {
if dep, ok := module.(*AndroidAppCertificate); ok {
certificates = append(certificates, dep.Certificate)
} else {
ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", otherName)
}
}
})
return jniLibs, certificates
}
func AndroidAppFactory() android.Module {
module := &AndroidApp{}
module.Module.deviceProperties.Optimize.Enabled = proptools.BoolPtr(true)
module.Module.deviceProperties.Optimize.Shrink = proptools.BoolPtr(true)
module.Module.properties.Instrument = true
module.Module.properties.Installable = proptools.BoolPtr(true)
module.AddProperties(
&module.Module.properties,
&module.Module.deviceProperties,
&module.Module.dexpreoptProperties,
&module.Module.protoProperties,
&module.aaptProperties,
&module.appProperties)
module.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase, class android.OsClass) bool {
return class == android.Device && ctx.Config().DevicePrefer32BitApps()
})
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
android.InitDefaultableModule(module)
return module
}
type appTestProperties struct {
Instrumentation_for *string
}
type AndroidTest struct {
AndroidApp
appTestProperties appTestProperties
testProperties testProperties
testConfig android.Path
data android.Paths
}
func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
a.generateAndroidBuildActions(ctx)
a.testConfig = tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config, a.testProperties.Test_config_template, a.manifestPath)
a.data = ctx.ExpandSources(a.testProperties.Data, nil)
}
func (a *AndroidTest) DepsMutator(ctx android.BottomUpMutatorContext) {
android.ExtractSourceDeps(ctx, a.testProperties.Test_config)
android.ExtractSourceDeps(ctx, a.testProperties.Test_config_template)
android.ExtractSourcesDeps(ctx, a.testProperties.Data)
a.AndroidApp.DepsMutator(ctx)
if a.appTestProperties.Instrumentation_for != nil {
// The android_app dependency listed in instrumentation_for needs to be added to the classpath for javac,
// but not added to the aapt2 link includes like a normal android_app or android_library dependency, so
// use instrumentationForTag instead of libTag.
ctx.AddVariationDependencies(nil, instrumentationForTag, String(a.appTestProperties.Instrumentation_for))
}
}
func AndroidTestFactory() android.Module {
module := &AndroidTest{}
module.Module.deviceProperties.Optimize.Enabled = proptools.BoolPtr(true)
module.Module.properties.Instrument = true
module.Module.properties.Installable = proptools.BoolPtr(true)
module.appProperties.EmbedJNI = true
module.Module.dexpreopter.isTest = true
module.AddProperties(
&module.Module.properties,
&module.Module.deviceProperties,
&module.Module.dexpreoptProperties,
&module.Module.protoProperties,
&module.aaptProperties,
&module.appProperties,
&module.appTestProperties,
&module.testProperties)
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
android.InitDefaultableModule(module)
return module
}
type appTestHelperAppProperties struct {
// list of compatibility suites (for example "cts", "vts") that the module should be
// installed into.
Test_suites []string `android:"arch_variant"`
}
type AndroidTestHelperApp struct {
AndroidApp
appTestHelperAppProperties appTestHelperAppProperties
}
func AndroidTestHelperAppFactory() android.Module {
module := &AndroidTestHelperApp{}
module.Module.deviceProperties.Optimize.Enabled = proptools.BoolPtr(true)
module.Module.properties.Installable = proptools.BoolPtr(true)
module.appProperties.EmbedJNI = true
module.Module.dexpreopter.isTest = true
module.AddProperties(
&module.Module.properties,
&module.Module.deviceProperties,
&module.Module.dexpreoptProperties,
&module.Module.protoProperties,
&module.aaptProperties,
&module.appProperties,
&module.appTestHelperAppProperties)
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
android.InitDefaultableModule(module)
return module
}
type AndroidAppCertificate struct {
android.ModuleBase
properties AndroidAppCertificateProperties
Certificate Certificate
}
type AndroidAppCertificateProperties struct {
// Name of the certificate files. Extensions .x509.pem and .pk8 will be added to the name.
Certificate *string
}
func AndroidAppCertificateFactory() android.Module {
module := &AndroidAppCertificate{}
module.AddProperties(&module.properties)
android.InitAndroidModule(module)
return module
}
func (c *AndroidAppCertificate) DepsMutator(ctx android.BottomUpMutatorContext) {
}
func (c *AndroidAppCertificate) GenerateAndroidBuildActions(ctx android.ModuleContext) {
cert := String(c.properties.Certificate)
c.Certificate = Certificate{
android.PathForModuleSrc(ctx, cert+".x509.pem"),
android.PathForModuleSrc(ctx, cert+".pk8"),
}
}