Merge "Restore lld parts of "Remove cortex-a53 linker workaround for new cpus.""
diff --git a/Android.bp b/Android.bp
index aeabb13..eefa149 100644
--- a/Android.bp
+++ b/Android.bp
@@ -219,6 +219,7 @@
"blueprint-pathtools",
"soong",
"soong-android",
+ "soong-cc",
"soong-genrule",
"soong-java-config",
"soong-tradefed",
diff --git a/android/config.go b/android/config.go
index 4b10552..5da1e0b 100644
--- a/android/config.go
+++ b/android/config.go
@@ -232,8 +232,8 @@
config.Targets = map[OsClass][]Target{
Device: []Target{
- {Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Native: true}},
- {Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Native: true}},
+ {Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Native: true, Abi: []string{"arm64-v8a"}}},
+ {Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Native: true, Abi: []string{"armeabi-v7a"}}},
},
Host: []Target{
{BuildOs, Arch{ArchType: X86_64}},
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index 1d50980..10e5b0a 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -140,6 +140,7 @@
"LOCAL_DX_FLAGS": "dxflags",
"LOCAL_JAVA_LIBRARIES": "libs",
"LOCAL_STATIC_JAVA_LIBRARIES": "static_libs",
+ "LOCAL_JNI_SHARED_LIBRARIES": "jni_libs",
"LOCAL_AAPT_FLAGS": "aaptflags",
"LOCAL_PACKAGE_SPLITS": "package_splits",
"LOCAL_COMPATIBILITY_SUITE": "test_suites",
@@ -154,6 +155,7 @@
// java_library_static to android_library.
"LOCAL_SHARED_ANDROID_LIBRARIES": "android_libs",
"LOCAL_STATIC_ANDROID_LIBRARIES": "android_static_libs",
+ "LOCAL_ADDITIONAL_CERTIFICATES": "additional_certificates",
})
addStandardProperties(bpparser.BoolType,
diff --git a/cmd/zip2zip/zip2zip.go b/cmd/zip2zip/zip2zip.go
index d3b349c..c4fb3d6 100644
--- a/cmd/zip2zip/zip2zip.go
+++ b/cmd/zip2zip/zip2zip.go
@@ -41,11 +41,13 @@
staticTime = time.Date(2009, 1, 1, 0, 0, 0, 0, time.UTC)
excludes multiFlag
+ includes multiFlag
uncompress multiFlag
)
func init() {
flag.Var(&excludes, "x", "exclude a filespec from the output")
+ flag.Var(&includes, "X", "include a filespec in the output that was previously excluded")
flag.Var(&uncompress, "0", "convert a filespec to uncompressed in the output")
}
@@ -96,7 +98,7 @@
}()
if err := zip2zip(&reader.Reader, writer, *sortGlobs, *sortJava, *setTime,
- flag.Args(), excludes, uncompress); err != nil {
+ flag.Args(), excludes, includes, uncompress); err != nil {
log.Fatal(err)
}
@@ -109,7 +111,7 @@
}
func zip2zip(reader *zip.Reader, writer *zip.Writer, sortOutput, sortJava, setTime bool,
- includes, excludes, uncompresses []string) error {
+ args []string, excludes, includes multiFlag, uncompresses []string) error {
matches := []pair{}
@@ -125,14 +127,14 @@
}
}
- for _, include := range includes {
+ for _, arg := range args {
// Reserve escaping for future implementation, so make sure no
// one is using \ and expecting a certain behavior.
- if strings.Contains(include, "\\") {
+ if strings.Contains(arg, "\\") {
return fmt.Errorf("\\ characters are not currently supported")
}
- input, output := includeSplit(include)
+ input, output := includeSplit(arg)
var includeMatches []pair
@@ -161,7 +163,7 @@
matches = append(matches, includeMatches...)
}
- if len(includes) == 0 {
+ if len(args) == 0 {
// implicitly match everything
for _, file := range reader.File {
matches = append(matches, pair{file, file.Name, false})
@@ -173,21 +175,18 @@
seen := make(map[string]*zip.File)
for _, match := range matches {
- // Filter out matches whose original file name matches an exclude filter
- excluded := false
- for _, exclude := range excludes {
- if excludeMatch, err := pathtools.Match(exclude, match.File.Name); err != nil {
+ // Filter out matches whose original file name matches an exclude filter, unless it also matches an
+ // include filter
+ if exclude, err := excludes.Match(match.File.Name); err != nil {
+ return err
+ } else if exclude {
+ if include, err := includes.Match(match.File.Name); err != nil {
return err
- } else if excludeMatch {
- excluded = true
- break
+ } else if !include {
+ continue
}
}
- if excluded {
- continue
- }
-
// Check for duplicate output names, ignoring ones that come from the same input zip entry.
if prev, exists := seen[match.newName]; exists {
if prev != match.File {
@@ -256,11 +255,25 @@
type multiFlag []string
-func (e *multiFlag) String() string {
- return strings.Join(*e, " ")
+func (m *multiFlag) String() string {
+ return strings.Join(*m, " ")
}
-func (e *multiFlag) Set(s string) error {
- *e = append(*e, s)
+func (m *multiFlag) Set(s string) error {
+ *m = append(*m, s)
return nil
}
+
+func (m *multiFlag) Match(s string) (bool, error) {
+ if m == nil {
+ return false, nil
+ }
+ for _, f := range *m {
+ if match, err := pathtools.Match(f, s); err != nil {
+ return false, err
+ } else if match {
+ return true, nil
+ }
+ }
+ return false, nil
+}
diff --git a/cmd/zip2zip/zip2zip_test.go b/cmd/zip2zip/zip2zip_test.go
index e032fe6..ae16494 100644
--- a/cmd/zip2zip/zip2zip_test.go
+++ b/cmd/zip2zip/zip2zip_test.go
@@ -31,6 +31,7 @@
sortJava bool
args []string
excludes []string
+ includes []string
uncompresses []string
outputFiles []string
@@ -228,7 +229,7 @@
},
},
{
- name: "excludes with include",
+ name: "excludes with args",
inputFiles: []string{
"a/a",
@@ -242,6 +243,31 @@
},
},
{
+ name: "excludes over args",
+
+ inputFiles: []string{
+ "a/a",
+ "a/b",
+ },
+ args: []string{"a/a"},
+ excludes: []string{"a/*"},
+
+ outputFiles: nil,
+ },
+ {
+ name: "excludes with includes",
+
+ inputFiles: []string{
+ "a/a",
+ "a/b",
+ },
+ args: nil,
+ excludes: []string{"a/*"},
+ includes: []string{"a/b"},
+
+ outputFiles: []string{"a/b"},
+ },
+ {
name: "excludes with glob",
inputFiles: []string{
@@ -358,7 +384,7 @@
outputWriter := zip.NewWriter(outputBuf)
err = zip2zip(inputReader, outputWriter, testCase.sortGlobs, testCase.sortJava, false,
- testCase.args, testCase.excludes, testCase.uncompresses)
+ testCase.args, testCase.excludes, testCase.includes, testCase.uncompresses)
if errorString(testCase.err) != errorString(err) {
t.Fatalf("Unexpected error:\n got: %q\nwant: %q", errorString(err), errorString(testCase.err))
}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index f19e2aa..2824e49 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -52,10 +52,9 @@
type hostToolDependencyTag struct {
blueprint.BaseDependencyTag
+ label string
}
-var hostToolDepTag hostToolDependencyTag
-
type generatorProperties struct {
// The command to run on one or more input files. Cmd supports substitution of a few variables
// (the actual substitution is implemented in GenerateAndroidBuildActions below)
@@ -63,7 +62,7 @@
// Available variables for substitution:
//
// $(location): the path to the first entry in tools or tool_files
- // $(location <label>): the path to the tool or tool_file with name <label>
+ // $(location <label>): the path to the tool, tool_file, input or output with name <label>
// $(in): one or more input files
// $(out): a single output file
// $(depfile): a file to which dependencies will be written, if the depfile property is set to true
@@ -141,10 +140,14 @@
android.ExtractSourcesDeps(ctx, g.properties.Srcs)
android.ExtractSourcesDeps(ctx, g.properties.Tool_files)
if g, ok := ctx.Module().(*Module); ok {
- if len(g.properties.Tools) > 0 {
+ for _, tool := range g.properties.Tools {
+ tag := hostToolDependencyTag{label: tool}
+ if m := android.SrcIsModule(tool); m != "" {
+ tool = m
+ }
ctx.AddFarVariationDependencies([]blueprint.Variation{
{Mutator: "arch", Variation: ctx.Config().BuildOsVariant},
- }, hostToolDepTag, g.properties.Tools...)
+ }, tag, tool)
}
}
}
@@ -159,12 +162,25 @@
g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForModuleGen(ctx, ""))
}
- tools := map[string]android.Path{}
+ locationLabels := map[string][]string{}
+ firstLabel := ""
+
+ addLocationLabel := func(label string, paths []string) {
+ if firstLabel == "" {
+ firstLabel = label
+ }
+ if _, exists := locationLabels[label]; !exists {
+ locationLabels[label] = paths
+ } else {
+ ctx.ModuleErrorf("multiple labels for %q, %q and %q",
+ label, strings.Join(locationLabels[label], " "), strings.Join(paths, " "))
+ }
+ }
if len(g.properties.Tools) > 0 {
ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
- switch ctx.OtherModuleDependencyTag(module) {
- case hostToolDepTag:
+ switch tag := ctx.OtherModuleDependencyTag(module).(type) {
+ case hostToolDependencyTag:
tool := ctx.OtherModuleName(module)
var path android.OptionalPath
@@ -192,11 +208,7 @@
if path.Valid() {
g.deps = append(g.deps, path.Path())
- if _, exists := tools[tool]; !exists {
- tools[tool] = path.Path()
- } else {
- ctx.ModuleErrorf("multiple tools for %q, %q and %q", tool, tools[tool], path.Path().String())
- }
+ addLocationLabel(tag.label, []string{path.Path().String()})
} else {
ctx.ModuleErrorf("host tool %q missing output file", tool)
}
@@ -208,21 +220,27 @@
return
}
- toolFiles := ctx.ExpandSources(g.properties.Tool_files, nil)
- for _, tool := range toolFiles {
- g.deps = append(g.deps, tool)
- if _, exists := tools[tool.Rel()]; !exists {
- tools[tool.Rel()] = tool
- } else {
- ctx.ModuleErrorf("multiple tools for %q, %q and %q", tool, tools[tool.Rel()], tool.Rel())
- }
+ for _, toolFile := range g.properties.Tool_files {
+ paths := ctx.ExpandSources([]string{toolFile}, nil)
+ g.deps = append(g.deps, paths...)
+ addLocationLabel(toolFile, paths.Strings())
+ }
+
+ var srcFiles android.Paths
+ for _, in := range g.properties.Srcs {
+ paths := ctx.ExpandSources([]string{in}, nil)
+ srcFiles = append(srcFiles, paths...)
+ addLocationLabel(in, paths.Strings())
+ }
+
+ task := g.taskGenerator(ctx, String(g.properties.Cmd), srcFiles)
+
+ for _, out := range task.out {
+ addLocationLabel(out.Rel(), []string{filepath.Join("__SBOX_OUT_DIR__", out.Rel())})
}
referencedDepfile := false
- srcFiles := ctx.ExpandSources(g.properties.Srcs, nil)
- task := g.taskGenerator(ctx, String(g.properties.Cmd), srcFiles)
-
rawCommand, err := android.Expand(task.cmd, func(name string) (string, error) {
// report the error directly without returning an error to android.Expand to catch multiple errors in a
// single run
@@ -233,13 +251,17 @@
switch name {
case "location":
- if len(g.properties.Tools) == 0 && len(toolFiles) == 0 {
+ if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 {
return reportError("at least one `tools` or `tool_files` is required if $(location) is used")
- } else if len(g.properties.Tools) > 0 {
- return tools[g.properties.Tools[0]].String(), nil
- } else {
- return tools[toolFiles[0].Rel()].String(), nil
}
+ paths := locationLabels[firstLabel]
+ if len(paths) == 0 {
+ return reportError("default label %q has no files", firstLabel)
+ } else if len(paths) > 1 {
+ return reportError("default label %q has multiple files, use $(locations %s) to reference it",
+ firstLabel, firstLabel)
+ }
+ return locationLabels[firstLabel][0], nil
case "in":
return "${in}", nil
case "out":
@@ -255,13 +277,30 @@
default:
if strings.HasPrefix(name, "location ") {
label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
- if tool, ok := tools[label]; ok {
- return tool.String(), nil
+ if paths, ok := locationLabels[label]; ok {
+ if len(paths) == 0 {
+ return reportError("label %q has no files", label)
+ } else if len(paths) > 1 {
+ return reportError("label %q has multiple files, use $(locations %s) to reference it",
+ label, label)
+ }
+ return paths[0], nil
} else {
return reportError("unknown location label %q", label)
}
+ } else if strings.HasPrefix(name, "locations ") {
+ label := strings.TrimSpace(strings.TrimPrefix(name, "locations "))
+ if paths, ok := locationLabels[label]; ok {
+ if len(paths) == 0 {
+ return reportError("label %q has no files", label)
+ }
+ return strings.Join(paths, " "), nil
+ } else {
+ return reportError("unknown locations label %q", label)
+ }
+ } else {
+ return reportError("unknown variable '$(%s)'", name)
}
- return reportError("unknown variable '$(%s)'", name)
}
})
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 0d690d4..7e16ce1 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -133,6 +133,15 @@
expect: "out/tool > __SBOX_OUT_FILES__",
},
{
+ name: "empty location tool2",
+ prop: `
+ tools: [":tool"],
+ out: ["out"],
+ cmd: "$(location) > $(out)",
+ `,
+ expect: "out/tool > __SBOX_OUT_FILES__",
+ },
+ {
name: "empty location tool file",
prop: `
tool_files: ["tool_file1"],
@@ -170,6 +179,15 @@
expect: "out/tool > __SBOX_OUT_FILES__",
},
{
+ name: "tool2",
+ prop: `
+ tools: [":tool"],
+ out: ["out"],
+ cmd: "$(location :tool) > $(out)",
+ `,
+ expect: "out/tool > __SBOX_OUT_FILES__",
+ },
+ {
name: "tool file",
prop: `
tool_files: ["tool_file1"],
@@ -183,7 +201,7 @@
prop: `
tool_files: [":1tool_file"],
out: ["out"],
- cmd: "$(location tool_file1) > $(out)",
+ cmd: "$(location :1tool_file) > $(out)",
`,
expect: "tool_file1 > __SBOX_OUT_FILES__",
},
@@ -192,7 +210,7 @@
prop: `
tool_files: [":tool_files"],
out: ["out"],
- cmd: "$(location tool_file1) $(location tool_file2) > $(out)",
+ cmd: "$(locations :tool_files) > $(out)",
`,
expect: "tool_file1 tool_file2 > __SBOX_OUT_FILES__",
},
@@ -233,6 +251,42 @@
expect: "cat ${in} > __SBOX_OUT_FILES__",
},
{
+ name: "location in1",
+ prop: `
+ srcs: ["in1"],
+ out: ["out"],
+ cmd: "cat $(location in1) > $(out)",
+ `,
+ expect: "cat in1 > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "location in1 fg",
+ prop: `
+ srcs: [":1in"],
+ out: ["out"],
+ cmd: "cat $(location :1in) > $(out)",
+ `,
+ expect: "cat in1 > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "location ins",
+ prop: `
+ srcs: ["in1", "in2"],
+ out: ["out"],
+ cmd: "cat $(location in1) > $(out)",
+ `,
+ expect: "cat in1 > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "location ins fg",
+ prop: `
+ srcs: [":ins"],
+ out: ["out"],
+ cmd: "cat $(locations :ins) > $(out)",
+ `,
+ expect: "cat in1 in2 > __SBOX_OUT_FILES__",
+ },
+ {
name: "outs",
prop: `
out: ["out", "out2"],
@@ -241,6 +295,14 @@
expect: "echo foo > __SBOX_OUT_FILES__",
},
{
+ name: "location out",
+ prop: `
+ out: ["out", "out2"],
+ cmd: "echo foo > $(location out2)",
+ `,
+ expect: "echo foo > __SBOX_OUT_DIR__/out2",
+ },
+ {
name: "depfile",
prop: `
out: ["out"],
@@ -267,6 +329,24 @@
err: "at least one `tools` or `tool_files` is required if $(location) is used",
},
{
+ name: "error empty location no files",
+ prop: `
+ tool_files: [":empty"],
+ out: ["out"],
+ cmd: "$(location) > $(out)",
+ `,
+ err: `default label ":empty" has no files`,
+ },
+ {
+ name: "error empty location multiple files",
+ prop: `
+ tool_files: [":tool_files"],
+ out: ["out"],
+ cmd: "$(location) > $(out)",
+ `,
+ err: `default label ":tool_files" has multiple files`,
+ },
+ {
name: "error location",
prop: `
out: ["out"],
@@ -275,6 +355,41 @@
err: `unknown location label "missing"`,
},
{
+ name: "error locations",
+ prop: `
+ out: ["out"],
+ cmd: "echo foo > $(locations missing)",
+ `,
+ err: `unknown locations label "missing"`,
+ },
+ {
+ name: "error location no files",
+ prop: `
+ out: ["out"],
+ srcs: [":empty"],
+ cmd: "echo $(location :empty) > $(out)",
+ `,
+ err: `label ":empty" has no files`,
+ },
+ {
+ name: "error locations no files",
+ prop: `
+ out: ["out"],
+ srcs: [":empty"],
+ cmd: "echo $(locations :empty) > $(out)",
+ `,
+ err: `label ":empty" has no files`,
+ },
+ {
+ name: "error location multiple files",
+ prop: `
+ out: ["out"],
+ srcs: [":ins"],
+ cmd: "echo $(location :ins) > $(out)",
+ `,
+ err: `label ":ins" has multiple files`,
+ },
+ {
name: "error variable",
prop: `
out: ["out"],
diff --git a/java/androidmk.go b/java/androidmk.go
index 313a144..359594c 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -243,6 +243,10 @@
if len(app.appProperties.Overrides) > 0 {
fmt.Fprintln(w, "LOCAL_OVERRIDES_PACKAGES := "+strings.Join(app.appProperties.Overrides, " "))
}
+
+ for _, jniLib := range app.installJniLibs {
+ fmt.Fprintln(w, "LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), "+=", jniLib.name)
+ }
},
},
}
@@ -267,6 +271,21 @@
return data
}
+func (a *AndroidTestHelperApp) AndroidMk() android.AndroidMkData {
+ data := a.AndroidApp.AndroidMk()
+ data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
+ fmt.Fprintln(w, "LOCAL_MODULE_TAGS := tests")
+ if len(a.appTestHelperAppProperties.Test_suites) > 0 {
+ fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
+ strings.Join(a.appTestHelperAppProperties.Test_suites, " "))
+ } else {
+ fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := null-suite")
+ }
+ })
+
+ return data
+}
+
func (a *AndroidLibrary) AndroidMk() android.AndroidMkData {
data := a.Library.AndroidMk()
diff --git a/java/app.go b/java/app.go
index dc5296d..d21b62a 100644
--- a/java/app.go
+++ b/java/app.go
@@ -19,26 +19,30 @@
import (
"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 {
- // path to a certificate, or the name of a certificate in the default
- // certificate directory, or blank to use the default product certificate
+ // 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
- // paths to extra certificates to sign the apk with
+ // 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
@@ -59,6 +63,11 @@
// 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 {
@@ -70,6 +79,8 @@
appProperties appProperties
extraLinkFlags []string
+
+ installJniLibs []jniLib
}
func (a *AndroidApp) ExportedProguardFlagFiles() android.Paths {
@@ -92,9 +103,36 @@
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) {
@@ -150,35 +188,45 @@
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)
+ 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...)
+
+ CreateAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, a.outputFile, certificates)
a.outputFile = packageFile
@@ -192,6 +240,42 @@
}
}
+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{}
@@ -212,7 +296,9 @@
return class == android.Device && ctx.Config().DevicePrefer32BitApps()
})
- InitJavaModule(module, android.DeviceSupported)
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+
return module
}
@@ -258,6 +344,7 @@
module.Module.properties.Instrument = true
module.Module.properties.Installable = proptools.BoolPtr(true)
+ module.appProperties.EmbedJNI = true
module.AddProperties(
&module.Module.properties,
@@ -268,6 +355,69 @@
&module.appTestProperties,
&module.testProperties)
- InitJavaModule(module, android.DeviceSupported)
+ 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.AddProperties(
+ &module.Module.properties,
+ &module.Module.deviceProperties,
+ &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"),
+ }
+}
diff --git a/java/app_builder.go b/java/app_builder.go
index e27b1b7..b9b5f43 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -19,9 +19,11 @@
// functions.
import (
+ "path/filepath"
"strings"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
"android/soong/android"
)
@@ -61,16 +63,18 @@
})
func CreateAppPackage(ctx android.ModuleContext, outputFile android.WritablePath,
- resJarFile, dexJarFile android.Path, certificates []certificate) {
-
- // TODO(ccross): JNI libs
+ resJarFile, jniJarFile, dexJarFile android.Path, certificates []certificate) {
unsignedApk := android.PathForModuleOut(ctx, "unsigned.apk")
- inputs := android.Paths{resJarFile}
+ var inputs android.Paths
if dexJarFile != nil {
inputs = append(inputs, dexJarFile)
}
+ inputs = append(inputs, resJarFile)
+ if jniJarFile != nil {
+ inputs = append(inputs, jniJarFile)
+ }
ctx.Build(pctx, android.BuildParams{
Rule: combineApk,
@@ -132,3 +136,37 @@
},
})
}
+
+func TransformJniLibsToJar(ctx android.ModuleContext, outputFile android.WritablePath,
+ jniLibs []jniLib) {
+
+ var deps android.Paths
+ jarArgs := []string{
+ "-j", // junk paths, they will be added back with -P arguments
+ }
+
+ if !ctx.Config().UnbundledBuild() {
+ jarArgs = append(jarArgs, "-L 0")
+ }
+
+ for _, j := range jniLibs {
+ deps = append(deps, j.path)
+ jarArgs = append(jarArgs,
+ "-P "+targetToJniDir(j.target),
+ "-f "+j.path.String())
+ }
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: zip,
+ Description: "zip jni libs",
+ Output: outputFile,
+ Implicits: deps,
+ Args: map[string]string{
+ "jarArgs": strings.Join(proptools.NinjaAndShellEscape(jarArgs), " "),
+ },
+ })
+}
+
+func targetToJniDir(target android.Target) string {
+ return filepath.Join("lib", target.Arch.Abi[0])
+}
diff --git a/java/app_test.go b/java/app_test.go
index c7c94ec..f6476dc 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -17,6 +17,7 @@
import (
"android/soong/android"
"fmt"
+ "path/filepath"
"reflect"
"sort"
"strings"
@@ -338,3 +339,118 @@
}
}
}
+
+func TestJNI(t *testing.T) {
+ ctx := testJava(t, `
+ toolchain_library {
+ name: "libcompiler_rt-extras",
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libatomic",
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libgcc",
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libclang_rt.builtins-aarch64-android",
+ src: "",
+ }
+
+ toolchain_library {
+ name: "libclang_rt.builtins-arm-android",
+ src: "",
+ }
+
+ cc_object {
+ name: "crtbegin_so",
+ stl: "none",
+ }
+
+ cc_object {
+ name: "crtend_so",
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libjni",
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ android_test {
+ name: "test",
+ no_framework_libs: true,
+ jni_libs: ["libjni"],
+ }
+
+ android_test {
+ name: "test_first",
+ no_framework_libs: true,
+ compile_multilib: "first",
+ jni_libs: ["libjni"],
+ }
+
+ android_test {
+ name: "test_both",
+ no_framework_libs: true,
+ compile_multilib: "both",
+ jni_libs: ["libjni"],
+ }
+
+ android_test {
+ name: "test_32",
+ no_framework_libs: true,
+ compile_multilib: "32",
+ jni_libs: ["libjni"],
+ }
+
+ android_test {
+ name: "test_64",
+ no_framework_libs: true,
+ compile_multilib: "64",
+ jni_libs: ["libjni"],
+ }
+ `)
+
+ // check the existence of the internal modules
+ ctx.ModuleForTests("test", "android_common")
+ ctx.ModuleForTests("test_first", "android_common")
+ ctx.ModuleForTests("test_both", "android_common")
+ ctx.ModuleForTests("test_32", "android_common")
+ ctx.ModuleForTests("test_64", "android_common")
+
+ testCases := []struct {
+ name string
+ abis []string
+ }{
+ {"test", []string{"arm64-v8a"}},
+ {"test_first", []string{"arm64-v8a"}},
+ {"test_both", []string{"arm64-v8a", "armeabi-v7a"}},
+ {"test_32", []string{"armeabi-v7a"}},
+ {"test_64", []string{"arm64-v8a"}},
+ }
+
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ app := ctx.ModuleForTests(test.name, "android_common")
+ jniLibZip := app.Output("jnilibs.zip")
+ var abis []string
+ args := strings.Fields(jniLibZip.Args["jarArgs"])
+ for i := 0; i < len(args); i++ {
+ if args[i] == "-P" {
+ abis = append(abis, filepath.Base(args[i+1]))
+ i++
+ }
+ }
+ if !reflect.DeepEqual(abis, test.abis) {
+ t.Errorf("want abis %v, got %v", test.abis, abis)
+ }
+ })
+ }
+}
diff --git a/java/builder.go b/java/builder.go
index 07af8eb..f55a7c7 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -109,6 +109,15 @@
},
"jarArgs")
+ zip = pctx.AndroidStaticRule("zip",
+ blueprint.RuleParams{
+ Command: `${config.SoongZipCmd} -o $out @$out.rsp`,
+ CommandDeps: []string{"${config.SoongZipCmd}"},
+ Rspfile: "$out.rsp",
+ RspfileContent: "$jarArgs",
+ },
+ "jarArgs")
+
combineJar = pctx.AndroidStaticRule("combineJar",
blueprint.RuleParams{
Command: `${config.MergeZipsCmd} --ignore-duplicates -j $jarArgs $out $in`,
diff --git a/java/java.go b/java/java.go
index b4b8feb..c5414f4 100644
--- a/java/java.go
+++ b/java/java.go
@@ -95,9 +95,6 @@
// list of java libraries that will be compiled into the resulting jar
Static_libs []string `android:"arch_variant"`
- // list of native libraries that will be provided in or alongside the resulting jar
- Jni_libs []string `android:"arch_variant"`
-
// manifest file to be included in resulting jar
Manifest *string
@@ -365,6 +362,11 @@
name string
}
+type jniDependencyTag struct {
+ blueprint.BaseDependencyTag
+ target android.Target
+}
+
var (
staticLibTag = dependencyTag{name: "staticlib"}
libTag = dependencyTag{name: "javalib"}
@@ -375,6 +377,7 @@
frameworkApkTag = dependencyTag{name: "framework-apk"}
kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib"}
proguardRaiseTag = dependencyTag{name: "proguard-raise"}
+ certificateTag = dependencyTag{name: "certificate"}
)
type sdkDep struct {
@@ -389,6 +392,12 @@
aidl android.Path
}
+type jniLib struct {
+ name string
+ path android.Path
+ target android.Target
+}
+
func (j *Module) shouldInstrument(ctx android.BaseContext) bool {
return j.properties.Instrument && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT")
}
@@ -597,6 +606,7 @@
ctx.AddFarVariationDependencies([]blueprint.Variation{
{Mutator: "arch", Variation: ctx.Config().BuildOsCommonVariant},
}, annoTag, j.properties.Annotation_processors...)
+
android.ExtractSourcesDeps(ctx, j.properties.Srcs)
android.ExtractSourcesDeps(ctx, j.properties.Exclude_srcs)
android.ExtractSourcesDeps(ctx, j.properties.Java_resources)
@@ -787,6 +797,15 @@
otherName := ctx.OtherModuleName(module)
tag := ctx.OtherModuleDependencyTag(module)
+ if _, ok := tag.(*jniDependencyTag); ok {
+ // Handled by AndroidApp.collectAppDeps
+ return
+ }
+ if tag == certificateTag {
+ // Handled by AndroidApp.collectAppDeps
+ return
+ }
+
if to, ok := module.(*Library); ok {
switch tag {
case bootClasspathTag, libTag, staticLibTag:
diff --git a/java/java_test.go b/java/java_test.go
index 82accd5..1bfd24b 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -15,8 +15,6 @@
package java
import (
- "android/soong/android"
- "android/soong/genrule"
"fmt"
"io/ioutil"
"os"
@@ -27,6 +25,10 @@
"testing"
"github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+ "android/soong/cc"
+ "android/soong/genrule"
)
var buildDir string
@@ -73,6 +75,7 @@
ctx := android.NewTestArchContext()
ctx.RegisterModuleType("android_app", android.ModuleFactoryAdaptor(AndroidAppFactory))
ctx.RegisterModuleType("android_library", android.ModuleFactoryAdaptor(AndroidLibraryFactory))
+ ctx.RegisterModuleType("android_test", android.ModuleFactoryAdaptor(AndroidTestFactory))
ctx.RegisterModuleType("java_binary_host", android.ModuleFactoryAdaptor(BinaryHostFactory))
ctx.RegisterModuleType("java_library", android.ModuleFactoryAdaptor(LibraryFactory))
ctx.RegisterModuleType("java_library_host", android.ModuleFactoryAdaptor(LibraryHostFactory))
@@ -95,6 +98,16 @@
ctx.TopDown("java_sdk_library", sdkLibraryMutator).Parallel()
})
ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(OverlaySingletonFactory))
+
+ // Register module types and mutators from cc needed for JNI testing
+ ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory))
+ ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc.ObjectFactory))
+ ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(cc.ToolchainLibraryFactory))
+ ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("link", cc.LinkageMutator).Parallel()
+ ctx.BottomUp("begin", cc.BeginMutator).Parallel()
+ })
+
ctx.Register()
extraModules := []string{