Add support for JNI libraries to android_app modules
Make android_app modules a MultiTargets module, which means the common variant will have a list of Targets that it needs to handle. Collect JNI libraries for each Target, and package them into or alongside the APK. Bug: 80095087 Test: app_test.go Change-Id: Iabd3921e1d4c4b4cfcc7e131a0b0d9ab83b0ebbb
This commit is contained in:
parent
b1a5e9cadf
commit
a4f08813a3
|
@ -219,6 +219,7 @@ bootstrap_go_package {
|
|||
"blueprint-pathtools",
|
||||
"soong",
|
||||
"soong-android",
|
||||
"soong-cc",
|
||||
"soong-genrule",
|
||||
"soong-java-config",
|
||||
"soong-tradefed",
|
||||
|
|
|
@ -232,8 +232,8 @@ func TestArchConfig(buildDir string, env map[string]string) Config {
|
|||
|
||||
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}},
|
||||
|
|
|
@ -140,6 +140,7 @@ func init() {
|
|||
"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",
|
||||
|
|
|
@ -243,6 +243,10 @@ func (app *AndroidApp) AndroidMk() android.AndroidMkData {
|
|||
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)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
72
java/app.go
72
java/app.go
|
@ -19,9 +19,11 @@ package java
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/google/blueprint"
|
||||
"github.com/google/blueprint/proptools"
|
||||
|
||||
"android/soong/android"
|
||||
"android/soong/cc"
|
||||
"android/soong/tradefed"
|
||||
)
|
||||
|
||||
|
@ -59,6 +61,11 @@ type appProperties struct {
|
|||
// 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 +77,8 @@ type AndroidApp struct {
|
|||
appProperties appProperties
|
||||
|
||||
extraLinkFlags []string
|
||||
|
||||
installJniLibs []jniLib
|
||||
}
|
||||
|
||||
func (a *AndroidApp) ExportedProguardFlagFiles() android.Paths {
|
||||
|
@ -92,9 +101,21 @@ type certificate struct {
|
|||
|
||||
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...)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
||||
|
@ -178,7 +199,19 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
|
|||
|
||||
packageFile := android.PathForModuleOut(ctx, "package.apk")
|
||||
|
||||
CreateAppPackage(ctx, packageFile, a.exportPackage, a.outputFile, certificates)
|
||||
var jniJarFile android.WritablePath
|
||||
jniLibs := a.collectJniDeps(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
|
||||
}
|
||||
}
|
||||
|
||||
CreateAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, a.outputFile, certificates)
|
||||
|
||||
a.outputFile = packageFile
|
||||
|
||||
|
@ -192,6 +225,35 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
|
|||
}
|
||||
}
|
||||
|
||||
func (a *AndroidApp) collectJniDeps(ctx android.ModuleContext) []jniLib {
|
||||
var jniLibs []jniLib
|
||||
|
||||
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)
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return jniLibs
|
||||
}
|
||||
|
||||
func AndroidAppFactory() android.Module {
|
||||
module := &AndroidApp{}
|
||||
|
||||
|
@ -212,7 +274,9 @@ func AndroidAppFactory() android.Module {
|
|||
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 +322,7 @@ func AndroidTestFactory() android.Module {
|
|||
|
||||
module.Module.properties.Instrument = true
|
||||
module.Module.properties.Installable = proptools.BoolPtr(true)
|
||||
module.appProperties.EmbedJNI = true
|
||||
|
||||
module.AddProperties(
|
||||
&module.Module.properties,
|
||||
|
@ -268,6 +333,7 @@ func AndroidTestFactory() android.Module {
|
|||
&module.appTestProperties,
|
||||
&module.testProperties)
|
||||
|
||||
InitJavaModule(module, android.DeviceSupported)
|
||||
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
|
||||
android.InitDefaultableModule(module)
|
||||
return module
|
||||
}
|
||||
|
|
|
@ -19,9 +19,11 @@ package java
|
|||
// functions.
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/google/blueprint"
|
||||
"github.com/google/blueprint/proptools"
|
||||
|
||||
"android/soong/android"
|
||||
)
|
||||
|
@ -61,16 +63,18 @@ var combineApk = pctx.AndroidStaticRule("combineApk",
|
|||
})
|
||||
|
||||
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 BuildAAR(ctx android.ModuleContext, outputFile android.WritablePath,
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
|
|
116
java/app_test.go
116
java/app_test.go
|
@ -17,6 +17,7 @@ package java
|
|||
import (
|
||||
"android/soong/android"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -338,3 +339,118 @@ func TestAppSdkVersion(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,6 +109,15 @@ var (
|
|||
},
|
||||
"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`,
|
||||
|
|
20
java/java.go
20
java/java.go
|
@ -95,9 +95,6 @@ type CompilerProperties struct {
|
|||
// 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 @@ type dependencyTag struct {
|
|||
name string
|
||||
}
|
||||
|
||||
type jniDependencyTag struct {
|
||||
blueprint.BaseDependencyTag
|
||||
target android.Target
|
||||
}
|
||||
|
||||
var (
|
||||
staticLibTag = dependencyTag{name: "staticlib"}
|
||||
libTag = dependencyTag{name: "javalib"}
|
||||
|
@ -389,6 +391,12 @@ type sdkDep struct {
|
|||
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 +605,7 @@ func (j *Module) deps(ctx android.BottomUpMutatorContext) {
|
|||
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 +796,11 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps {
|
|||
otherName := ctx.OtherModuleName(module)
|
||||
tag := ctx.OtherModuleDependencyTag(module)
|
||||
|
||||
if _, ok := tag.(*jniDependencyTag); ok {
|
||||
// Handled by AndroidApp.collectJniDeps
|
||||
return
|
||||
}
|
||||
|
||||
if to, ok := module.(*Library); ok {
|
||||
switch tag {
|
||||
case bootClasspathTag, libTag, staticLibTag:
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
package java
|
||||
|
||||
import (
|
||||
"android/soong/android"
|
||||
"android/soong/genrule"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
@ -27,6 +25,10 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/google/blueprint/proptools"
|
||||
|
||||
"android/soong/android"
|
||||
"android/soong/cc"
|
||||
"android/soong/genrule"
|
||||
)
|
||||
|
||||
var buildDir string
|
||||
|
@ -73,6 +75,7 @@ func testContext(config android.Config, bp string,
|
|||
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 @@ func testContext(config android.Config, bp string,
|
|||
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{
|
||||
|
|
Loading…
Reference in New Issue