From 8fd6192480f244c8188450b99ab310cc77caab38 Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Thu, 8 Nov 2018 02:50:25 +0900 Subject: [PATCH] APEX can be flattened When TARGET_FLATTEN_APEX is set to true, APEXes are flattened, which means files in an APEX is not packaged into the mini file system image, but instead directly copied to the system partition. This option is for devices where kernel does not support loopback devices or the maximum number of loopback devices is too small (though the threshold is TBD as of now). This CL also fixes a bug that jars having bytecode are installed instead of those having dex. Bug: 118485880 Test: TARGET_FLATTEN_APEX=true m apex.test; tree out/target/product/.../system/apex/apex.test shows list of files in it. Test; m apex.test, then a file out/target/product/.../system/apex/apex .test.apex exists. Change-Id: I5a3d62d392d05f2779c4925388afe4f6e460059b --- android/config.go | 4 + android/variable.go | 2 + apex/apex.go | 188 ++++++++++++++++++++++++++++++++++++-------- java/java.go | 4 + 4 files changed, 165 insertions(+), 33 deletions(-) diff --git a/android/config.go b/android/config.go index 695a2980f..367b42c0c 100644 --- a/android/config.go +++ b/android/config.go @@ -881,6 +881,10 @@ func (c *config) NdkAbis() bool { return Bool(c.productVariables.Ndk_abis) } +func (c *config) FlattenApex() bool { + return Bool(c.productVariables.FlattenApex) +} + func stringSlice(s *[]string) []string { if s != nil { return *s diff --git a/android/variable.go b/android/variable.go index 2686049db..2763bf292 100644 --- a/android/variable.go +++ b/android/variable.go @@ -250,6 +250,8 @@ type productVariables struct { VendorVars map[string]map[string]string `json:",omitempty"` Ndk_abis *bool `json:",omitempty"` + + FlattenApex *bool `json:",omitempty"` } func boolPtr(v bool) *bool { diff --git a/apex/apex.go b/apex/apex.go index 7232c1b6d..177856e61 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -230,6 +230,38 @@ type apexBundleProperties struct { } } +type apexFileClass int + +const ( + etc apexFileClass = iota + nativeSharedLib + nativeExecutable + javaSharedLib +) + +func (class apexFileClass) NameInMake() string { + switch class { + case etc: + return "ETC" + case nativeSharedLib: + return "SHARED_LIBRARIES" + case nativeExecutable: + return "EXECUTABLES" + case javaSharedLib: + return "JAVA_LIBRARIES" + default: + panic(fmt.Errorf("unkonwn class %d", class)) + } +} + +type apexFile struct { + builtFile android.Path + moduleName string + archType android.ArchType + installDir string + class apexFileClass +} + type apexBundle struct { android.ModuleBase android.DefaultableModuleBase @@ -238,6 +270,11 @@ type apexBundle struct { outputFile android.WritablePath installDir android.OutputPath + + // list of files to be included in this apex + filesInfo []apexFile + + flattened bool } func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, @@ -364,7 +401,7 @@ func getCopyManifestForExecutable(cc *cc.Module) (fileToCopy android.Path, dirIn func getCopyManifestForJavaLibrary(java *java.Library) (fileToCopy android.Path, dirInApex string) { dirInApex = "javalib" - fileToCopy = java.Srcs()[0] + fileToCopy = java.DexJarFile() return } @@ -375,8 +412,7 @@ func getCopyManifestForPrebuiltEtc(prebuilt *android.PrebuiltEtc) (fileToCopy an } func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { - // files to copy -> dir in apex - copyManifest := make(map[android.Path]string) + filesInfo := []apexFile{} var keyFile android.Path var certificate java.Certificate @@ -390,7 +426,7 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { case sharedLibTag: if cc, ok := child.(*cc.Module); ok { fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc) - copyManifest[fileToCopy] = dirInApex + filesInfo = append(filesInfo, apexFile{fileToCopy, depName, cc.Arch().ArchType, dirInApex, nativeSharedLib}) return true } else { ctx.PropertyErrorf("native_shared_libs", "%q is not a cc_library or cc_library_shared module", depName) @@ -398,7 +434,7 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { case executableTag: if cc, ok := child.(*cc.Module); ok { fileToCopy, dirInApex := getCopyManifestForExecutable(cc) - copyManifest[fileToCopy] = dirInApex + filesInfo = append(filesInfo, apexFile{fileToCopy, depName, cc.Arch().ArchType, dirInApex, nativeExecutable}) return true } else { ctx.PropertyErrorf("binaries", "%q is not a cc_binary module", depName) @@ -406,7 +442,11 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { case javaLibTag: if java, ok := child.(*java.Library); ok { fileToCopy, dirInApex := getCopyManifestForJavaLibrary(java) - copyManifest[fileToCopy] = dirInApex + if fileToCopy == nil { + ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName) + } else { + filesInfo = append(filesInfo, apexFile{fileToCopy, depName, java.Arch().ArchType, dirInApex, javaSharedLib}) + } return true } else { ctx.PropertyErrorf("java_libs", "%q is not a java_library module", depName) @@ -414,7 +454,7 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { case prebuiltTag: if prebuilt, ok := child.(*android.PrebuiltEtc); ok { fileToCopy, dirInApex := getCopyManifestForPrebuiltEtc(prebuilt) - copyManifest[fileToCopy] = dirInApex + filesInfo = append(filesInfo, apexFile{fileToCopy, depName, prebuilt.Arch().ArchType, dirInApex, etc}) return true } else { ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName) @@ -438,8 +478,9 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { // indirect dependencies if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() && am.IsInstallableToApex() { if cc, ok := child.(*cc.Module); ok { + depName := ctx.OtherModuleName(child) fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc) - copyManifest[fileToCopy] = dirInApex + filesInfo = append(filesInfo, apexFile{fileToCopy, depName, cc.Arch().ArchType, dirInApex, nativeSharedLib}) return true } } @@ -452,6 +493,42 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { return } + // remove duplicates in filesInfo + removeDup := func(filesInfo []apexFile) []apexFile { + encountered := make(map[android.Path]bool) + result := []apexFile{} + for _, f := range filesInfo { + if !encountered[f.builtFile] { + encountered[f.builtFile] = true + result = append(result, f) + } + } + return result + } + filesInfo = removeDup(filesInfo) + + // to have consistent build rules + sort.Slice(filesInfo, func(i, j int) bool { + return filesInfo[i].builtFile.String() < filesInfo[j].builtFile.String() + }) + + // prepend the name of this APEX to the module names. These names will be the names of + // modules that will be defined if the APEX is flattened. + for i := range filesInfo { + filesInfo[i].moduleName = ctx.ModuleName() + "." + filesInfo[i].moduleName + } + + a.flattened = ctx.Config().FlattenApex() + a.installDir = android.PathForModuleInstall(ctx, "apex") + a.filesInfo = filesInfo + if ctx.Config().FlattenApex() { + a.buildFlattenedApex(ctx) + } else { + a.buildUnflattenedApex(ctx, keyFile, certificate) + } +} + +func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, keyFile android.Path, certificate java.Certificate) { cert := String(a.properties.Certificate) if cert != "" && android.SrcIsModule(cert) == "" { defaultDir := ctx.Config().DefaultAppCertificateDir(ctx) @@ -467,15 +544,15 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { // files and dirs that will be created in apex var readOnlyPaths []string var executablePaths []string // this also includes dirs - for fileToCopy, dirInApex := range copyManifest { - pathInApex := filepath.Join(dirInApex, fileToCopy.Base()) - if dirInApex == "bin" { + 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(dirInApex, executablePaths) { - executablePaths = append(executablePaths, dirInApex) + if !android.InList(f.installDir, executablePaths) { + executablePaths = append(executablePaths, f.installDir) } } sort.Strings(readOnlyPaths) @@ -504,16 +581,13 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { unsignedOutputFile := android.PathForModuleOut(ctx, a.ModuleBase.Name()+apexSuffix+".unsigned") filesToCopy := []android.Path{} - for file := range copyManifest { - filesToCopy = append(filesToCopy, file) + for _, f := range a.filesInfo { + filesToCopy = append(filesToCopy, f.builtFile) } - sort.Slice(filesToCopy, func(i, j int) bool { - return filesToCopy[i].String() < filesToCopy[j].String() - }) copyCommands := []string{} - for _, src := range filesToCopy { - dest := filepath.Join(copyManifest[src], src.Base()) + for i, src := range filesToCopy { + dest := filepath.Join(a.filesInfo[i].installDir, src.Base()) dest_path := filepath.Join(android.PathForModuleOut(ctx, "image").String(), dest) copyCommands = append(copyCommands, "mkdir -p "+filepath.Dir(dest_path)) copyCommands = append(copyCommands, "cp "+src.String()+" "+dest_path) @@ -547,23 +621,71 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { "certificates": strings.Join([]string{certificate.Pem.String(), certificate.Key.String()}, " "), }, }) +} - a.installDir = android.PathForModuleInstall(ctx, "apex") +func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) { + // For flattened APEX, do nothing but make sure that manifest.json file is also copied along + // with other ordinary files. + manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "manifest.json")) + a.filesInfo = append(a.filesInfo, apexFile{manifest, a.Name() + ".manifest.json", android.Common, ".", etc}) + + for _, fi := range a.filesInfo { + dir := filepath.Join("apex", a.Name(), fi.installDir) + ctx.InstallFile(android.PathForModuleInstall(ctx, dir), fi.builtFile.Base(), fi.builtFile) + } } func (a *apexBundle) AndroidMk() android.AndroidMkData { - return android.AndroidMkData{ - Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { - 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_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", a.installDir.RelPathString())) - fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", name+apexSuffix) - fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", String(a.properties.Key)) - fmt.Fprintln(w, "include $(BUILD_PREBUILT)") - }} + if a.flattened { + return android.AndroidMkData{ + Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { + moduleNames := []string{} + for _, fi := range a.filesInfo { + if !android.InList(fi.moduleName, moduleNames) { + moduleNames = append(moduleNames, fi.moduleName) + } + } + fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)") + fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir) + fmt.Fprintln(w, "LOCAL_MODULE :=", name) + fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(moduleNames, " ")) + fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)") + + for _, fi := range a.filesInfo { + fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)") + fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir) + fmt.Fprintln(w, "LOCAL_MODULE :=", fi.moduleName) + fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", a.installDir.RelPathString(), name, fi.installDir)) + fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", fi.builtFile.Base()) + fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String()) + fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.NameInMake()) + archStr := fi.archType.String() + if archStr != "common" { + fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", archStr) + } + if fi.class == javaSharedLib { + fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", fi.builtFile.String()) + fmt.Fprintln(w, "LOCAL_DEX_PREOPT := false") + fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_java_prebuilt.mk") + } else { + fmt.Fprintln(w, "include $(BUILD_PREBUILT)") + } + } + }} + } else { + return android.AndroidMkData{ + Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { + 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_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", a.installDir.RelPathString())) + fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", name+apexSuffix) + fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", String(a.properties.Key)) + fmt.Fprintln(w, "include $(BUILD_PREBUILT)") + }} + } } func apexBundleFactory() android.Module { diff --git a/java/java.go b/java/java.go index 50c284a94..fef9a215d 100644 --- a/java/java.go +++ b/java/java.go @@ -330,6 +330,10 @@ func (j *Module) Srcs() android.Paths { return android.Paths{j.outputFile} } +func (j *Module) DexJarFile() android.Path { + return j.dexJarFile +} + var _ android.SourceFileProducer = (*Module)(nil) type Dependency interface {