Optionally embed NOTICE files in apks.

am: 5b425e2e20

Change-Id: I0397838781d5ce7ba20778706d3b0ee4936080e8
This commit is contained in:
Jaewoong Jung
2019-06-25 09:59:31 -07:00
committed by android-build-merger
9 changed files with 274 additions and 42 deletions

View File

@@ -17,6 +17,7 @@ package java
import (
"android/soong/android"
"fmt"
"path/filepath"
"strings"
"github.com/google/blueprint"
@@ -80,6 +81,7 @@ type aapt struct {
rTxt android.Path
extraAaptPackagesFile android.Path
mergedManifestFile android.Path
noticeFile android.OptionalPath
isLibrary bool
useEmbeddedNativeLibs bool
useEmbeddedDex bool
@@ -151,10 +153,16 @@ func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext sdkContext,
assetFiles = append(assetFiles, androidResourceGlob(ctx, dir)...)
}
assetDirStrings := assetDirs.Strings()
if a.noticeFile.Valid() {
assetDirStrings = append(assetDirStrings, filepath.Dir(a.noticeFile.Path().String()))
assetFiles = append(assetFiles, a.noticeFile.Path())
}
linkFlags = append(linkFlags, "--manifest "+manifestPath.String())
linkDeps = append(linkDeps, manifestPath)
linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirs.Strings(), "-A "))
linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirStrings, "-A "))
linkDeps = append(linkDeps, assetFiles...)
// SDK version flags

View File

@@ -19,6 +19,7 @@ package java
import (
"path/filepath"
"reflect"
"sort"
"strings"
"github.com/google/blueprint"
@@ -103,6 +104,10 @@ type appProperties struct {
// Use_embedded_native_libs still selects whether they are stored uncompressed and aligned or compressed.
// True for android_test* modules.
AlwaysPackageNativeLibs bool `blueprint:"mutated"`
// If set, find and merge all NOTICE files that this module and its dependencies have and store
// it in the APK as an asset.
Embed_notices *bool
}
// android_app properties that can be overridden by override_android_app
@@ -351,6 +356,54 @@ func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext
return jniJarFile
}
func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext, installDir android.OutputPath) android.OptionalPath {
if !Bool(a.appProperties.Embed_notices) && !ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") {
return android.OptionalPath{}
}
// Collect NOTICE files from all dependencies.
seenModules := make(map[android.Module]bool)
noticePathSet := make(map[android.Path]bool)
ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
// Have we already seen this?
if _, ok := seenModules[child]; ok {
return false
}
seenModules[child] = true
// Skip host modules.
if child.Target().Os.Class == android.Host || child.Target().Os.Class == android.HostCross {
return false
}
path := child.(android.Module).NoticeFile()
if path.Valid() {
noticePathSet[path.Path()] = true
}
return true
})
// If the app has one, add it too.
if a.NoticeFile().Valid() {
noticePathSet[a.NoticeFile().Path()] = true
}
if len(noticePathSet) == 0 {
return android.OptionalPath{}
}
var noticePaths []android.Path
for path := range noticePathSet {
noticePaths = append(noticePaths, path)
}
sort.Slice(noticePaths, func(i, j int) bool {
return noticePaths[i].String() < noticePaths[j].String()
})
noticeFile := android.BuildNoticeOutput(ctx, installDir, a.installApkName+".apk", noticePaths)
return android.OptionalPathForPath(noticeFile)
}
// Reads and prepends a main cert from the default cert dir if it hasn't been set already, i.e. it
// isn't a cert module reference. Also checks and enforces system cert restriction if applicable.
func processMainCert(m android.ModuleBase, certPropValue string, certificates []Certificate, ctx android.ModuleContext) []Certificate {
@@ -391,6 +444,18 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
// Check if the install APK name needs to be overridden.
a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Name())
var installDir android.OutputPath
if ctx.ModuleName() == "framework-res" {
// framework-res.apk is installed as system/framework/framework-res.apk
installDir = android.PathForModuleInstall(ctx, "framework")
} else if Bool(a.appProperties.Privileged) {
installDir = android.PathForModuleInstall(ctx, "priv-app", a.installApkName)
} else {
installDir = android.PathForModuleInstall(ctx, "app", a.installApkName)
}
a.aapt.noticeFile = a.noticeBuildActions(ctx, installDir)
// Process all building blocks, from AAPT to certificates.
a.aaptBuildActions(ctx)
@@ -432,16 +497,6 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
a.bundleFile = bundleFile
// Install the app package.
var installDir android.OutputPath
if ctx.ModuleName() == "framework-res" {
// framework-res.apk is installed as system/framework/framework-res.apk
installDir = android.PathForModuleInstall(ctx, "framework")
} else if Bool(a.appProperties.Privileged) {
installDir = android.PathForModuleInstall(ctx, "priv-app", a.installApkName)
} else {
installDir = android.PathForModuleInstall(ctx, "app", a.installApkName)
}
ctx.InstallFile(installDir, a.installApkName+".apk", a.outputFile)
for _, split := range a.aapt.splits {
ctx.InstallFile(installDir, a.installApkName+"_"+split.suffix+".apk", split.path)

View File

@@ -1396,3 +1396,98 @@ func TestCodelessApp(t *testing.T) {
})
}
}
func TestEmbedNotice(t *testing.T) {
ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
android_app {
name: "foo",
srcs: ["a.java"],
static_libs: ["javalib"],
jni_libs: ["libjni"],
notice: "APP_NOTICE",
embed_notices: true,
}
// No embed_notice flag
android_app {
name: "bar",
srcs: ["a.java"],
jni_libs: ["libjni"],
notice: "APP_NOTICE",
}
// No NOTICE files
android_app {
name: "baz",
srcs: ["a.java"],
embed_notices: true,
}
cc_library {
name: "libjni",
system_shared_libs: [],
stl: "none",
notice: "LIB_NOTICE",
}
java_library {
name: "javalib",
srcs: [
":gen",
],
}
genrule {
name: "gen",
tools: ["gentool"],
out: ["gen.java"],
notice: "GENRULE_NOTICE",
}
java_binary_host {
name: "gentool",
srcs: ["b.java"],
notice: "TOOL_NOTICE",
}
`)
// foo has NOTICE files to process, and embed_notices is true.
foo := ctx.ModuleForTests("foo", "android_common")
// verify merge notices rule.
mergeNotices := foo.Rule("mergeNoticesRule")
noticeInputs := mergeNotices.Inputs.Strings()
// TOOL_NOTICE should be excluded as it's a host module.
if len(mergeNotices.Inputs) != 3 {
t.Errorf("number of input notice files: expected = 3, actual = %q", noticeInputs)
}
if !inList("APP_NOTICE", noticeInputs) {
t.Errorf("APP_NOTICE is missing from notice files, %q", noticeInputs)
}
if !inList("LIB_NOTICE", noticeInputs) {
t.Errorf("LIB_NOTICE is missing from notice files, %q", noticeInputs)
}
if !inList("GENRULE_NOTICE", noticeInputs) {
t.Errorf("GENRULE_NOTICE is missing from notice files, %q", noticeInputs)
}
// aapt2 flags should include -A <NOTICE dir> so that its contents are put in the APK's /assets.
res := foo.Output("package-res.apk")
aapt2Flags := res.Args["flags"]
e := "-A " + buildDir + "/.intermediates/foo/android_common/NOTICE"
if !strings.Contains(aapt2Flags, e) {
t.Errorf("asset dir flag for NOTICE, %q is missing in aapt2 link flags, %q", e, aapt2Flags)
}
// bar has NOTICE files to process, but embed_notices is not set.
bar := ctx.ModuleForTests("bar", "android_common")
mergeNotices = bar.MaybeRule("mergeNoticesRule")
if mergeNotices.Rule != nil {
t.Errorf("mergeNotices shouldn't have run for bar")
}
// baz's embed_notice is true, but it doesn't have any NOTICE files.
baz := ctx.ModuleForTests("baz", "android_common")
mergeNotices = baz.MaybeRule("mergeNoticesRule")
if mergeNotices.Rule != nil {
t.Errorf("mergeNotices shouldn't have run for baz")
}
}

View File

@@ -121,6 +121,10 @@ func testContext(config android.Config, bp string,
"b.kt": nil,
"a.jar": nil,
"b.jar": nil,
"APP_NOTICE": nil,
"GENRULE_NOTICE": nil,
"LIB_NOTICE": nil,
"TOOL_NOTICE": nil,
"java-res/a/a": nil,
"java-res/b/b": nil,
"java-res2/a": nil,