Merge "Add zip-apex"
diff --git a/apex/apex.go b/apex/apex.go
index 52fa561..0445c20 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -55,6 +55,7 @@
`${apexer} --force --manifest ${manifest} ` +
`--file_contexts ${file_contexts} ` +
`--canned_fs_config ${canned_fs_config} ` +
+ `--payload_type image ` +
`--key ${key} ${image_dir} ${out} `,
CommandDeps: []string{"${apexer}", "${avbtool}", "${e2fsdroid}", "${merge_zips}",
"${mke2fs}", "${resize2fs}", "${sefcontext_compile}",
@@ -62,6 +63,17 @@
Description: "APEX ${image_dir} => ${out}",
}, "tool_path", "image_dir", "copy_commands", "manifest", "file_contexts", "canned_fs_config", "key")
+ zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{
+ Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
+ `(${copy_commands}) && ` +
+ `APEXER_TOOL_PATH=${tool_path} ` +
+ `${apexer} --force --manifest ${manifest} ` +
+ `--payload_type zip ` +
+ `${image_dir} ${out} `,
+ CommandDeps: []string{"${apexer}", "${merge_zips}", "${soong_zip}", "${zipalign}", "${aapt2}"},
+ Description: "ZipAPEX ${image_dir} => ${out}",
+ }, "tool_path", "image_dir", "copy_commands", "manifest")
+
apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule",
blueprint.RuleParams{
Command: `${aapt2} convert --output-format proto $in -o $out`,
@@ -78,7 +90,11 @@
}, "abi")
)
-var apexSuffix = ".apex"
+var imageApexSuffix = ".apex"
+var zipApexSuffix = ".zipapex"
+
+var imageApexType = "image"
+var zipApexType = "zip"
type dependencyTag struct {
blueprint.BaseDependencyTag
@@ -199,6 +215,10 @@
// Name of the apex_key module that provides the private key to sign APEX
Key *string
+ // The type of APEX to build. Controls what the APEX payload is. Either
+ // 'image', 'zip' or 'both'. Default: 'image'.
+ Payload_type *string
+
// 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
@@ -246,6 +266,56 @@
javaSharedLib
)
+type apexPackaging int
+
+const (
+ imageApex apexPackaging = iota
+ zipApex
+ both
+)
+
+func (a apexPackaging) image() bool {
+ switch a {
+ case imageApex, both:
+ return true
+ }
+ return false
+}
+
+func (a apexPackaging) zip() bool {
+ switch a {
+ case zipApex, both:
+ return true
+ }
+ return false
+}
+
+func (a apexPackaging) suffix() string {
+ switch a {
+ case imageApex:
+ return imageApexSuffix
+ case zipApex:
+ return zipApexSuffix
+ case both:
+ panic(fmt.Errorf("must be either zip or image"))
+ default:
+ panic(fmt.Errorf("unkonwn APEX type %d", a))
+ }
+}
+
+func (a apexPackaging) name() string {
+ switch a {
+ case imageApex:
+ return imageApexType
+ case zipApex:
+ return zipApexType
+ case both:
+ panic(fmt.Errorf("must be either zip or image"))
+ default:
+ panic(fmt.Errorf("unkonwn APEX type %d", a))
+ }
+}
+
func (class apexFileClass) NameInMake() string {
switch class {
case etc:
@@ -275,8 +345,10 @@
properties apexBundleProperties
+ apexTypes apexPackaging
+
bundleModuleFile android.WritablePath
- outputFile android.WritablePath
+ outputFiles map[apexPackaging]android.WritablePath
installDir android.OutputPath
// list of files to be included in this apex
@@ -425,6 +497,17 @@
var keyFile android.Path
var certificate java.Certificate
+ if a.properties.Payload_type == nil || *a.properties.Payload_type == "image" {
+ a.apexTypes = imageApex
+ } else if *a.properties.Payload_type == "zip" {
+ a.apexTypes = zipApex
+ } else if *a.properties.Payload_type == "both" {
+ a.apexTypes = both
+ } else {
+ ctx.PropertyErrorf("type", "%q is not one of \"image\", \"zip\", or \"both\".", *a.properties.Payload_type)
+ return
+ }
+
ctx.WalkDeps(func(child, parent android.Module) bool {
if _, ok := parent.(*apexBundle); ok {
// direct dependencies
@@ -532,14 +615,20 @@
a.flattened = ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuild()
a.installDir = android.PathForModuleInstall(ctx, "apex")
a.filesInfo = filesInfo
- if ctx.Config().FlattenApex() {
- a.buildFlattenedApex(ctx)
- } else {
- a.buildUnflattenedApex(ctx, keyFile, certificate)
+
+ if a.apexTypes.zip() {
+ a.buildUnflattenedApex(ctx, keyFile, certificate, zipApex)
+ }
+ if a.apexTypes.image() {
+ if ctx.Config().FlattenApex() {
+ a.buildFlattenedApex(ctx)
+ } else {
+ a.buildUnflattenedApex(ctx, keyFile, certificate, imageApex)
+ }
}
}
-func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, keyFile android.Path, certificate java.Certificate) {
+func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, keyFile android.Path, certificate java.Certificate, apexType apexPackaging) {
cert := String(a.properties.Certificate)
if cert != "" && android.SrcIsModule(cert) == "" {
defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
@@ -552,45 +641,19 @@
certificate = java.Certificate{pem, key}
}
- // files and dirs that will be created in apex
- var readOnlyPaths []string
- var executablePaths []string // this also includes dirs
- for _, f := range a.filesInfo {
- pathInApex := filepath.Join(f.installDir, f.builtFile.Base())
- if f.installDir == "bin" {
- executablePaths = append(executablePaths, pathInApex)
- } else {
- readOnlyPaths = append(readOnlyPaths, pathInApex)
- }
- if !android.InList(f.installDir, executablePaths) {
- executablePaths = append(executablePaths, f.installDir)
- }
- }
- sort.Strings(readOnlyPaths)
- sort.Strings(executablePaths)
- cannedFsConfig := android.PathForModuleOut(ctx, "canned_fs_config")
- ctx.Build(pctx, android.BuildParams{
- Rule: generateFsConfig,
- Output: cannedFsConfig,
- Description: "generate fs config",
- Args: map[string]string{
- "ro_paths": strings.Join(readOnlyPaths, " "),
- "exec_paths": strings.Join(executablePaths, " "),
- },
- })
-
manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
- fcName := proptools.StringDefault(a.properties.File_contexts, ctx.ModuleName())
- fileContextsPath := "system/sepolicy/apex/" + fcName + "-file_contexts"
- fileContextsOptionalPath := android.ExistentPathForSource(ctx, fileContextsPath)
- if !fileContextsOptionalPath.Valid() {
- ctx.ModuleErrorf("Cannot find file_contexts file: %q", fileContextsPath)
- return
+ var abis []string
+ for _, target := range ctx.MultiTargets() {
+ if len(target.Arch.Abi) > 0 {
+ abis = append(abis, target.Arch.Abi[0])
+ }
}
- fileContexts := fileContextsOptionalPath.Path()
- unsignedOutputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+apexSuffix+".unsigned")
+ abis = android.FirstUniqueStrings(abis)
+
+ suffix := apexType.suffix()
+ unsignedOutputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+suffix+".unsigned")
filesToCopy := []android.Path{}
for _, f := range a.filesInfo {
@@ -600,68 +663,121 @@
copyCommands := []string{}
for i, src := range filesToCopy {
dest := filepath.Join(a.filesInfo[i].installDir, src.Base())
- dest_path := filepath.Join(android.PathForModuleOut(ctx, "image").String(), dest)
+ dest_path := filepath.Join(android.PathForModuleOut(ctx, "image"+suffix).String(), dest)
copyCommands = append(copyCommands, "mkdir -p "+filepath.Dir(dest_path))
copyCommands = append(copyCommands, "cp "+src.String()+" "+dest_path)
}
implicitInputs := append(android.Paths(nil), filesToCopy...)
- implicitInputs = append(implicitInputs, cannedFsConfig, manifest, fileContexts, keyFile)
+ implicitInputs = append(implicitInputs, manifest)
+
outHostBinDir := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "bin").String()
prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
- ctx.Build(pctx, android.BuildParams{
- Rule: apexRule,
- Implicits: implicitInputs,
- Output: unsignedOutputFile,
- Description: "apex",
- Args: map[string]string{
- "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir,
- "image_dir": android.PathForModuleOut(ctx, "image").String(),
- "copy_commands": strings.Join(copyCommands, " && "),
- "manifest": manifest.String(),
- "file_contexts": fileContexts.String(),
- "canned_fs_config": cannedFsConfig.String(),
- "key": keyFile.String(),
- },
- })
- var abis []string
- for _, target := range ctx.MultiTargets() {
- abis = append(abis, target.Arch.Abi[0])
+ if apexType.image() {
+ // files and dirs that will be created in APEX
+ var readOnlyPaths []string
+ var executablePaths []string // this also includes dirs
+ for _, f := range a.filesInfo {
+ pathInApex := filepath.Join(f.installDir, f.builtFile.Base())
+ if f.installDir == "bin" {
+ executablePaths = append(executablePaths, pathInApex)
+ } else {
+ readOnlyPaths = append(readOnlyPaths, pathInApex)
+ }
+ if !android.InList(f.installDir, executablePaths) {
+ executablePaths = append(executablePaths, f.installDir)
+ }
+ }
+ sort.Strings(readOnlyPaths)
+ sort.Strings(executablePaths)
+ cannedFsConfig := android.PathForModuleOut(ctx, "canned_fs_config")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: generateFsConfig,
+ Output: cannedFsConfig,
+ Description: "generate fs config",
+ Args: map[string]string{
+ "ro_paths": strings.Join(readOnlyPaths, " "),
+ "exec_paths": strings.Join(executablePaths, " "),
+ },
+ })
+
+ fcName := proptools.StringDefault(a.properties.File_contexts, ctx.ModuleName())
+ fileContextsPath := "system/sepolicy/apex/" + fcName + "-file_contexts"
+ fileContextsOptionalPath := android.ExistentPathForSource(ctx, fileContextsPath)
+ if !fileContextsOptionalPath.Valid() {
+ ctx.ModuleErrorf("Cannot find file_contexts file: %q", fileContextsPath)
+ return
+ }
+ fileContexts := fileContextsOptionalPath.Path()
+
+ // Additional implicit inputs.
+ implicitInputs = append(implicitInputs, cannedFsConfig, fileContexts, keyFile)
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: apexRule,
+ Implicits: implicitInputs,
+ Output: unsignedOutputFile,
+ Description: "apex (" + apexType.name() + ")",
+ Args: map[string]string{
+ "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+ "image_dir": android.PathForModuleOut(ctx, "image"+suffix).String(),
+ "copy_commands": strings.Join(copyCommands, " && "),
+ "manifest": manifest.String(),
+ "file_contexts": fileContexts.String(),
+ "canned_fs_config": cannedFsConfig.String(),
+ "key": keyFile.String(),
+ },
+ })
+
+ apexProtoFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".pb"+suffix)
+ bundleModuleFile := android.PathForModuleOut(ctx, ctx.ModuleName()+suffix+"-base.zip")
+ a.bundleModuleFile = bundleModuleFile
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: apexProtoConvertRule,
+ Input: unsignedOutputFile,
+ Output: apexProtoFile,
+ Description: "apex proto convert",
+ })
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: apexBundleRule,
+ Input: apexProtoFile,
+ Output: a.bundleModuleFile,
+ Description: "apex bundle module",
+ Args: map[string]string{
+ "abi": strings.Join(abis, "."),
+ },
+ })
+ } else {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: zipApexRule,
+ Implicits: implicitInputs,
+ Output: unsignedOutputFile,
+ Description: "apex (" + apexType.name() + ")",
+ Args: map[string]string{
+ "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+ "image_dir": android.PathForModuleOut(ctx, "image"+suffix).String(),
+ "copy_commands": strings.Join(copyCommands, " && "),
+ "manifest": manifest.String(),
+ },
+ })
}
- abis = android.FirstUniqueStrings(abis)
- apexProtoFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".pb"+apexSuffix)
- bundleModuleFile := android.PathForModuleOut(ctx, ctx.ModuleName()+"-base.zip")
- a.bundleModuleFile = bundleModuleFile
-
- ctx.Build(pctx, android.BuildParams{
- Rule: apexProtoConvertRule,
- Input: unsignedOutputFile,
- Output: apexProtoFile,
- Description: "apex proto convert",
- })
-
- ctx.Build(pctx, android.BuildParams{
- Rule: apexBundleRule,
- Input: apexProtoFile,
- Output: bundleModuleFile,
- Description: "apex bundle module",
- Args: map[string]string{
- "abi": strings.Join(abis, "."),
- },
- })
-
- a.outputFile = android.PathForModuleOut(ctx, ctx.ModuleName()+apexSuffix)
+ a.outputFiles[apexType] = android.PathForModuleOut(ctx, ctx.ModuleName()+suffix)
ctx.Build(pctx, android.BuildParams{
Rule: java.Signapk,
Description: "signapk",
- Output: a.outputFile,
+ Output: a.outputFiles[apexType],
Input: unsignedOutputFile,
Args: map[string]string{
"certificates": strings.Join([]string{certificate.Pem.String(), certificate.Key.String()}, " "),
"flags": "-a 4096", //alignment
},
})
+
+ // Install to $OUT/soong/{target,host}/.../apex
+ ctx.InstallFile(android.PathForModuleInstall(ctx, "apex"), ctx.ModuleName()+suffix, a.outputFiles[apexType])
}
func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) {
@@ -677,7 +793,24 @@
}
func (a *apexBundle) AndroidMk() android.AndroidMkData {
- if a.flattened {
+ writers := []android.AndroidMkData{}
+ if a.apexTypes.image() {
+ writers = append(writers, a.androidMkForType(imageApex))
+ }
+ if a.apexTypes.zip() {
+ writers = append(writers, a.androidMkForType(zipApex))
+ }
+ return android.AndroidMkData{
+ Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+ for _, data := range writers {
+ data.Custom(w, name, prefix, moduleDir, data)
+ }
+ }}
+}
+
+func (a *apexBundle) androidMkForType(apexType apexPackaging) android.AndroidMkData {
+ // Only image APEXes can be flattened.
+ if a.flattened && apexType.image() {
return android.AndroidMkData{
Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
moduleNames := []string{}
@@ -716,29 +849,37 @@
} else {
return android.AndroidMkData{
Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+ // zip-apex is the less common type so have the name refer to the image-apex
+ // only and use {name}.zip if you want the zip-apex
+ if apexType == zipApex && a.apexTypes == both {
+ name = name + ".zip"
+ }
fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w, "LOCAL_MODULE :=", name)
fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") // do we need a new class?
- fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", a.outputFile.String())
+ fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", a.outputFiles[apexType].String())
fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", a.installDir.RelPathString()))
- fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", name+apexSuffix)
+ fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", name+apexType.suffix())
fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", String(a.properties.Key))
fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
- fmt.Fprintln(w, "ALL_MODULES.$(LOCAL_MODULE).BUNDLE :=", a.bundleModuleFile.String())
+ if apexType == imageApex {
+ fmt.Fprintln(w, "ALL_MODULES.$(LOCAL_MODULE).BUNDLE :=", a.bundleModuleFile.String())
+ }
}}
}
}
func apexBundleFactory() android.Module {
- module := &apexBundle{}
+ module := &apexBundle{
+ outputFiles: map[apexPackaging]android.WritablePath{},
+ }
module.AddProperties(&module.properties)
- module.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase,
- class android.OsClass) bool {
+ module.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase, class android.OsClass) bool {
return class == android.Device && ctx.Config().DevicePrefer32BitExecutables()
})
- android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitAndroidMultiTargetsArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
android.InitDefaultableModule(module)
return module
}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 41d8455..d1794ee 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -188,8 +188,56 @@
ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_myapex")
// Ensure that both direct and indirect deps are copied into apex
- ensureContains(t, copyCmds, "image/lib64/mylib.so")
- ensureContains(t, copyCmds, "image/lib64/mylib2.so")
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib2.so")
+}
+
+func TestBasicZipApex(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ payload_type: "zip",
+ native_shared_libs: ["mylib"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ shared_libs: ["mylib2"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "mylib2",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+ `)
+
+ zipApexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("zipApexRule")
+ copyCmds := zipApexRule.Args["copy_commands"]
+
+ // Ensure that main rule creates an output
+ ensureContains(t, zipApexRule.Output.String(), "myapex.zipapex.unsigned")
+
+ // Ensure that APEX variant is created for the direct dep
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_myapex")
+
+ // Ensure that APEX variant is created for the indirect dep
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_myapex")
+
+ // Ensure that both direct and indirect deps are copied into apex
+ ensureContains(t, copyCmds, "image.zipapex/lib64/mylib.so")
+ ensureContains(t, copyCmds, "image.zipapex/lib64/mylib2.so")
}
func TestApexWithStubs(t *testing.T) {
@@ -239,13 +287,13 @@
copyCmds := apexRule.Args["copy_commands"]
// Ensure that direct non-stubs dep is always included
- ensureContains(t, copyCmds, "image/lib64/mylib.so")
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
// Ensure that indirect stubs dep is not included
- ensureNotContains(t, copyCmds, "image/lib64/mylib2.so")
+ ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so")
// Ensure that direct stubs dep is included
- ensureContains(t, copyCmds, "image/lib64/mylib3.so")
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib3.so")
mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_myapex").Rule("ld").Args["libFlags"]
@@ -326,12 +374,12 @@
copyCmds := apexRule.Args["copy_commands"]
// Ensure that mylib, libm, libdl are included.
- ensureContains(t, copyCmds, "image/lib64/mylib.so")
- ensureContains(t, copyCmds, "image/lib64/libm.so")
- ensureContains(t, copyCmds, "image/lib64/libdl.so")
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
+ ensureContains(t, copyCmds, "image.apex/lib64/libm.so")
+ ensureContains(t, copyCmds, "image.apex/lib64/libdl.so")
// Ensure that libc is not included (since it has stubs and not listed in native_shared_libs)
- ensureNotContains(t, copyCmds, "image/lib64/libc.so")
+ ensureNotContains(t, copyCmds, "image.apex/lib64/libc.so")
mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_myapex").Rule("ld").Args["libFlags"]
mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"]