68a7023436
Device overlays are passed to Soong in highest priority to lowest priority order, but aapt2 keeps the last value provided on the command line. Reverse the order that overlayGlobResults are collected. Bug: 71595164 Test: app_test.go Change-Id: Ibc6b8297f1548766c5c92f3be969b89736dc8dff
451 lines
14 KiB
Go
451 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/proptools"
|
|
|
|
"android/soong/android"
|
|
)
|
|
|
|
func init() {
|
|
android.RegisterPreSingletonType("overlay", OverlaySingletonFactory)
|
|
android.RegisterModuleType("android_app", AndroidAppFactory)
|
|
}
|
|
|
|
// AAR prebuilts
|
|
// AndroidManifest.xml merging
|
|
// package splits
|
|
|
|
type androidAppProperties struct {
|
|
// path to a certificate, or the name of a certificate in the default
|
|
// certificate directory, or blank to use the default product certificate
|
|
Certificate *string
|
|
|
|
// paths to extra certificates to sign the apk with
|
|
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
|
|
|
|
// flags passed to aapt when creating the apk
|
|
Aaptflags []string
|
|
|
|
// list of resource labels to generate individual resource packages
|
|
Package_splits []string
|
|
|
|
// list of directories relative to the Blueprints file containing assets.
|
|
// Defaults to "assets"
|
|
Asset_dirs []string
|
|
|
|
// list of directories relative to the Blueprints file containing
|
|
// Android resources
|
|
Resource_dirs []string
|
|
|
|
Instrumentation_for *string
|
|
|
|
// 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
|
|
}
|
|
|
|
type AndroidApp struct {
|
|
Module
|
|
|
|
appProperties androidAppProperties
|
|
|
|
aaptSrcJar android.Path
|
|
exportPackage android.Path
|
|
rroDirs android.Paths
|
|
manifestPath android.Path
|
|
certificate certificate
|
|
}
|
|
|
|
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) {
|
|
switch String(a.deviceProperties.Sdk_version) { // TODO: Res_sdk_version?
|
|
case "current", "system_current", "test_current", "":
|
|
ctx.AddDependency(ctx.Module(), frameworkResTag, "framework-res")
|
|
default:
|
|
// We'll already have a dependency on an sdk prebuilt android.jar
|
|
}
|
|
}
|
|
}
|
|
|
|
func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
|
linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, manifestPath := a.aapt2Flags(ctx)
|
|
|
|
packageRes := android.PathForModuleOut(ctx, "package-res.apk")
|
|
srcJar := android.PathForModuleGen(ctx, "R.jar")
|
|
proguardOptionsFile := android.PathForModuleGen(ctx, "proguard.options")
|
|
|
|
var compiledRes, compiledOverlay android.Paths
|
|
for _, dir := range resDirs {
|
|
compiledRes = append(compiledRes, aapt2Compile(ctx, dir.dir, dir.files).Paths()...)
|
|
}
|
|
for _, dir := range overlayDirs {
|
|
compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files).Paths()...)
|
|
}
|
|
|
|
aapt2Link(ctx, packageRes, srcJar, proguardOptionsFile,
|
|
linkFlags, linkDeps, compiledRes, compiledOverlay)
|
|
|
|
a.exportPackage = packageRes
|
|
a.aaptSrcJar = srcJar
|
|
|
|
ctx.CheckbuildFile(proguardOptionsFile)
|
|
ctx.CheckbuildFile(a.exportPackage)
|
|
ctx.CheckbuildFile(a.aaptSrcJar)
|
|
|
|
// apps manifests are handled by aapt, don't let Module see them
|
|
a.properties.Manifest = nil
|
|
|
|
if String(a.appProperties.Instrumentation_for) == "" {
|
|
a.properties.Instrument = true
|
|
}
|
|
|
|
a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles,
|
|
proguardOptionsFile)
|
|
|
|
if ctx.ModuleName() != "framework-res" {
|
|
a.Module.compile(ctx, a.aaptSrcJar)
|
|
}
|
|
|
|
c := String(a.appProperties.Certificate)
|
|
switch {
|
|
case c == "":
|
|
pem, key := ctx.Config().DefaultAppCertificate(ctx)
|
|
a.certificate = certificate{pem, key}
|
|
case strings.ContainsRune(c, '/'):
|
|
a.certificate = certificate{
|
|
android.PathForSource(ctx, c+".x509.pem"),
|
|
android.PathForSource(ctx, c+".pk8"),
|
|
}
|
|
default:
|
|
defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
|
|
a.certificate = certificate{
|
|
defaultDir.Join(ctx, c+".x509.pem"),
|
|
defaultDir.Join(ctx, c+".pk8"),
|
|
}
|
|
}
|
|
|
|
certificates := []certificate{a.certificate}
|
|
for _, c := range a.appProperties.Additional_certificates {
|
|
certificates = append(certificates, certificate{
|
|
android.PathForSource(ctx, c+".x509.pem"),
|
|
android.PathForSource(ctx, c+".pk8"),
|
|
})
|
|
}
|
|
|
|
packageFile := android.PathForModuleOut(ctx, "package.apk")
|
|
|
|
CreateAppPackage(ctx, packageFile, a.exportPackage, a.outputFile, certificates)
|
|
|
|
a.outputFile = packageFile
|
|
a.rroDirs = rroDirs
|
|
a.manifestPath = manifestPath
|
|
|
|
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()+".apk", a.outputFile)
|
|
} else {
|
|
ctx.InstallFile(android.PathForModuleInstall(ctx, "app"), ctx.ModuleName()+".apk", a.outputFile)
|
|
}
|
|
}
|
|
|
|
var aaptIgnoreFilenames = []string{
|
|
".svn",
|
|
".git",
|
|
".ds_store",
|
|
"*.scc",
|
|
".*",
|
|
"CVS",
|
|
"thumbs.db",
|
|
"picasa.ini",
|
|
"*~",
|
|
}
|
|
|
|
type globbedResourceDir struct {
|
|
dir android.Path
|
|
files android.Paths
|
|
}
|
|
|
|
func (a *AndroidApp) aapt2Flags(ctx android.ModuleContext) (flags []string, deps android.Paths,
|
|
resDirs, overlayDirs []globbedResourceDir, rroDirs android.Paths, manifestPath android.Path) {
|
|
|
|
hasVersionCode := false
|
|
hasVersionName := false
|
|
hasProduct := false
|
|
for _, f := range a.appProperties.Aaptflags {
|
|
if strings.HasPrefix(f, "--version-code") {
|
|
hasVersionCode = true
|
|
} else if strings.HasPrefix(f, "--version-name") {
|
|
hasVersionName = true
|
|
} else if strings.HasPrefix(f, "--product") {
|
|
hasProduct = true
|
|
}
|
|
}
|
|
|
|
var linkFlags []string
|
|
|
|
// Flags specified in Android.bp
|
|
linkFlags = append(linkFlags, a.appProperties.Aaptflags...)
|
|
|
|
linkFlags = append(linkFlags, "--no-static-lib-packages")
|
|
|
|
// Find implicit or explicit asset and resource dirs
|
|
assetDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.appProperties.Asset_dirs, "assets")
|
|
resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.appProperties.Resource_dirs, "res")
|
|
|
|
var linkDeps android.Paths
|
|
|
|
// Glob directories into lists of paths
|
|
for _, dir := range resourceDirs {
|
|
resDirs = append(resDirs, globbedResourceDir{
|
|
dir: dir,
|
|
files: resourceGlob(ctx, dir),
|
|
})
|
|
resOverlayDirs, resRRODirs := overlayResourceGlob(ctx, dir)
|
|
overlayDirs = append(overlayDirs, resOverlayDirs...)
|
|
rroDirs = append(rroDirs, resRRODirs...)
|
|
}
|
|
|
|
var assetFiles android.Paths
|
|
for _, dir := range assetDirs {
|
|
assetFiles = append(assetFiles, resourceGlob(ctx, dir)...)
|
|
}
|
|
|
|
// App manifest file
|
|
var manifestFile string
|
|
if a.properties.Manifest == nil {
|
|
manifestFile = "AndroidManifest.xml"
|
|
} else {
|
|
manifestFile = *a.properties.Manifest
|
|
}
|
|
|
|
manifestPath = android.PathForModuleSrc(ctx, manifestFile)
|
|
linkFlags = append(linkFlags, "--manifest "+manifestPath.String())
|
|
linkDeps = append(linkDeps, manifestPath)
|
|
|
|
linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirs.Strings(), "-A "))
|
|
linkDeps = append(linkDeps, assetFiles...)
|
|
|
|
// Include dirs
|
|
ctx.VisitDirectDeps(func(module android.Module) {
|
|
var depFiles android.Paths
|
|
if javaDep, ok := module.(Dependency); ok {
|
|
// TODO: shared android libraries
|
|
if ctx.OtherModuleName(module) == "framework-res" {
|
|
depFiles = android.Paths{javaDep.(*AndroidApp).exportPackage}
|
|
}
|
|
}
|
|
|
|
for _, dep := range depFiles {
|
|
linkFlags = append(linkFlags, "-I "+dep.String())
|
|
}
|
|
linkDeps = append(linkDeps, depFiles...)
|
|
})
|
|
|
|
// SDK version flags
|
|
sdkVersion := String(a.deviceProperties.Sdk_version)
|
|
switch sdkVersion {
|
|
case "", "current", "system_current", "test_current":
|
|
sdkVersion = proptools.NinjaEscape([]string{ctx.Config().AppsDefaultVersionName()})[0]
|
|
}
|
|
|
|
linkFlags = append(linkFlags, "--min-sdk-version "+sdkVersion)
|
|
linkFlags = append(linkFlags, "--target-sdk-version "+sdkVersion)
|
|
|
|
// Product characteristics
|
|
if !hasProduct && len(ctx.Config().ProductAAPTCharacteristics()) > 0 {
|
|
linkFlags = append(linkFlags, "--product", ctx.Config().ProductAAPTCharacteristics())
|
|
}
|
|
|
|
// 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())
|
|
}
|
|
|
|
// Version code
|
|
if !hasVersionCode {
|
|
linkFlags = append(linkFlags, "--version-code", ctx.Config().PlatformSdkVersion())
|
|
}
|
|
|
|
if !hasVersionName {
|
|
versionName := proptools.NinjaEscape([]string{ctx.Config().AppsDefaultVersionName()})[0]
|
|
linkFlags = append(linkFlags, "--version-name ", versionName)
|
|
}
|
|
|
|
if String(a.appProperties.Instrumentation_for) != "" {
|
|
linkFlags = append(linkFlags,
|
|
"--rename-instrumentation-target-package",
|
|
String(a.appProperties.Instrumentation_for))
|
|
}
|
|
|
|
// TODO: LOCAL_PACKAGE_OVERRIDES
|
|
// $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \
|
|
|
|
return linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, manifestPath
|
|
}
|
|
|
|
func AndroidAppFactory() android.Module {
|
|
module := &AndroidApp{}
|
|
|
|
module.Module.deviceProperties.Optimize.Enabled = proptools.BoolPtr(true)
|
|
module.Module.deviceProperties.Optimize.Shrink = proptools.BoolPtr(true)
|
|
|
|
module.AddProperties(
|
|
&module.Module.properties,
|
|
&module.Module.deviceProperties,
|
|
&module.appProperties)
|
|
|
|
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
|
|
return module
|
|
}
|
|
|
|
func resourceGlob(ctx android.ModuleContext, dir android.Path) android.Paths {
|
|
var ret android.Paths
|
|
files := ctx.Glob(filepath.Join(dir.String(), "**/*"), aaptIgnoreFilenames)
|
|
for _, f := range files {
|
|
if isDir, err := ctx.Fs().IsDir(f.String()); err != nil {
|
|
ctx.ModuleErrorf("error in IsDir(%s): %s", f.String(), err.Error())
|
|
return nil
|
|
} else if !isDir {
|
|
ret = append(ret, f)
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
type overlayGlobResult struct {
|
|
dir string
|
|
paths android.DirectorySortedPaths
|
|
|
|
// Set to true of the product has selected that values in this overlay should not be moved to
|
|
// Runtime Resource Overlay (RRO) packages.
|
|
excludeFromRRO bool
|
|
}
|
|
|
|
const overlayDataKey = "overlayDataKey"
|
|
|
|
func overlayResourceGlob(ctx android.ModuleContext, dir android.Path) (res []globbedResourceDir,
|
|
rroDirs android.Paths) {
|
|
|
|
overlayData := ctx.Config().Get(overlayDataKey).([]overlayGlobResult)
|
|
|
|
// Runtime resource overlays (RRO) may be turned on by the product config for some modules
|
|
rroEnabled := false
|
|
enforceRROTargets := ctx.Config().ProductVariables.EnforceRROTargets
|
|
if enforceRROTargets != nil {
|
|
if len(*enforceRROTargets) == 1 && (*enforceRROTargets)[0] == "*" {
|
|
rroEnabled = true
|
|
} else if inList(ctx.ModuleName(), *enforceRROTargets) {
|
|
rroEnabled = true
|
|
}
|
|
}
|
|
|
|
for _, data := range overlayData {
|
|
files := data.paths.PathsInDirectory(filepath.Join(data.dir, dir.String()))
|
|
if len(files) > 0 {
|
|
overlayModuleDir := android.PathForSource(ctx, data.dir, dir.String())
|
|
// If enforce RRO is enabled for this module and this overlay is not in the
|
|
// exclusion list, ignore the overlay. The list of ignored overlays will be
|
|
// passed to Make to be turned into an RRO package.
|
|
if rroEnabled && !data.excludeFromRRO {
|
|
rroDirs = append(rroDirs, overlayModuleDir)
|
|
} else {
|
|
res = append(res, globbedResourceDir{
|
|
dir: overlayModuleDir,
|
|
files: files,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
return res, rroDirs
|
|
}
|
|
|
|
func OverlaySingletonFactory() android.Singleton {
|
|
return overlaySingleton{}
|
|
}
|
|
|
|
type overlaySingleton struct{}
|
|
|
|
func (overlaySingleton) GenerateBuildActions(ctx android.SingletonContext) {
|
|
|
|
// Specific overlays may be excluded from Runtime Resource Overlays by the product config
|
|
var rroExcludedOverlays []string
|
|
if ctx.Config().ProductVariables.EnforceRROExcludedOverlays != nil {
|
|
rroExcludedOverlays = *ctx.Config().ProductVariables.EnforceRROExcludedOverlays
|
|
}
|
|
|
|
var overlayData []overlayGlobResult
|
|
overlayDirs := ctx.Config().ResourceOverlays()
|
|
for i := range overlayDirs {
|
|
// Iterate backwards through the list of overlay directories so that the later, lower-priority
|
|
// directories in the list show up earlier in the command line to aapt2.
|
|
overlay := overlayDirs[len(overlayDirs)-1-i]
|
|
var result overlayGlobResult
|
|
result.dir = overlay
|
|
|
|
// Mark overlays that will not have Runtime Resource Overlays enforced on them
|
|
for _, exclude := range rroExcludedOverlays {
|
|
if strings.HasPrefix(overlay, exclude) {
|
|
result.excludeFromRRO = true
|
|
}
|
|
}
|
|
|
|
files, err := ctx.GlobWithDeps(filepath.Join(overlay, "**/*"), aaptIgnoreFilenames)
|
|
if err != nil {
|
|
ctx.Errorf("failed to glob resource dir %q: %s", overlay, err.Error())
|
|
continue
|
|
}
|
|
var paths android.Paths
|
|
for _, f := range files {
|
|
if isDir, err := ctx.Fs().IsDir(f); err != nil {
|
|
ctx.Errorf("error in IsDir(%s): %s", f, err.Error())
|
|
return
|
|
} else if !isDir {
|
|
paths = append(paths, android.PathForSource(ctx, f))
|
|
}
|
|
}
|
|
result.paths = android.PathsToDirectorySortedPaths(paths)
|
|
overlayData = append(overlayData, result)
|
|
}
|
|
|
|
ctx.Config().Once(overlayDataKey, func() interface{} {
|
|
return overlayData
|
|
})
|
|
}
|