Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 40ef4b92 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "use symlink for bundled APEX"

parents 4d9c97bd 58ab941c
Loading
Loading
Loading
Loading
+32 −5
Original line number Diff line number Diff line
@@ -52,13 +52,40 @@ func (a *apexBundle) androidMkForFiles(w io.Writer, apexName, moduleDir string)
		return moduleNames
	}

	var postInstallCommands []string
	for _, fi := range a.filesInfo {
		if a.linkToSystemLib && fi.transitiveDep && fi.AvailableToPlatform() {
			// TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
			linkTarget := filepath.Join("/system", fi.Path())
			linkPath := filepath.Join(a.installDir.ToMakePath().String(), apexName, fi.Path())
			mkdirCmd := "mkdir -p " + filepath.Dir(linkPath)
			linkCmd := "ln -s " + linkTarget + " " + linkPath
			postInstallCommands = append(postInstallCommands, mkdirCmd, linkCmd)
		}
	}
	postInstallCommands = append(postInstallCommands, a.compatSymlinks...)

	for _, fi := range a.filesInfo {
		if cc, ok := fi.module.(*cc.Module); ok && cc.Properties.HideFromMake {
			continue
		}

		if !android.InList(fi.moduleName, moduleNames) {
			moduleNames = append(moduleNames, fi.moduleName)
		linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.AvailableToPlatform()

		var moduleName string
		if linkToSystemLib {
			moduleName = fi.moduleName
		} else {
			moduleName = fi.moduleName + "." + apexName + a.suffix
		}

		if !android.InList(moduleName, moduleNames) {
			moduleNames = append(moduleNames, moduleName)
		}

		if linkToSystemLib {
			// No need to copy the file since it's linked to the system file
			continue
		}

		fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
@@ -67,7 +94,7 @@ func (a *apexBundle) androidMkForFiles(w io.Writer, apexName, moduleDir string)
		} else {
			fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
		}
		fmt.Fprintln(w, "LOCAL_MODULE :=", fi.moduleName)
		fmt.Fprintln(w, "LOCAL_MODULE :=", moduleName)
		// /apex/<apex_name>/{lib|framework|...}
		pathWhenActivated := filepath.Join("$(PRODUCT_OUT)", "apex", apexName, fi.installDir)
		if apexType == flattenedApex {
@@ -152,8 +179,8 @@ func (a *apexBundle) androidMkForFiles(w io.Writer, apexName, moduleDir string)
		} else {
			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.builtFile.Base())
			// For flattened apexes, compat symlinks are attached to apex_manifest.json which is guaranteed for every apex
			if a.primaryApexType && fi.builtFile == a.manifestPbOut && len(a.compatSymlinks) > 0 {
				fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(a.compatSymlinks, " && "))
			if a.primaryApexType && apexType == flattenedApex && fi.builtFile == a.manifestPbOut && len(postInstallCommands) > 0 {
				fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(postInstallCommands, " && "))
			}
			fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
		}
+50 −12
Original line number Diff line number Diff line
@@ -490,6 +490,30 @@ func (af *apexFile) Ok() bool {
	return af.builtFile != nil && af.builtFile.String() != ""
}

// Path() returns path of this apex file relative to the APEX root
func (af *apexFile) Path() string {
	return filepath.Join(af.installDir, af.builtFile.Base())
}

// SymlinkPaths() returns paths of the symlinks (if any) relative to the APEX root
func (af *apexFile) SymlinkPaths() []string {
	var ret []string
	for _, symlink := range af.symlinks {
		ret = append(ret, filepath.Join(af.installDir, symlink))
	}
	return ret
}

func (af *apexFile) AvailableToPlatform() bool {
	if af.module == nil {
		return false
	}
	if am, ok := af.module.(android.ApexModule); ok {
		return am.AvailableFor(android.AvailableToPlatform)
	}
	return false
}

type apexBundle struct {
	android.ModuleBase
	android.DefaultableModuleBase
@@ -540,6 +564,10 @@ type apexBundle struct {
	// Suffix of module name in Android.mk
	// ".flattened", ".apex", ".zipapex", or ""
	suffix string

	// Whether to create symlink to the system file instead of having a file
	// inside the apex or not
	linkToSystemLib bool
}

func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
@@ -1154,7 +1182,8 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
						// of the original test module (`depName`, shared by all `test_per_src`
						// variations of that module).
						af.moduleName = filepath.Base(af.builtFile.String())
						af.transitiveDep = true
						// these are not considered transitive dep
						af.transitiveDep = false
						filesInfo = append(filesInfo, af)
						return true // track transitive dependencies
					}
@@ -1190,14 +1219,21 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {

	// remove duplicates in filesInfo
	removeDup := func(filesInfo []apexFile) []apexFile {
		encountered := make(map[string]bool)
		result := []apexFile{}
		encountered := make(map[string]apexFile)
		for _, f := range filesInfo {
			dest := filepath.Join(f.installDir, f.builtFile.Base())
			if !encountered[dest] {
				encountered[dest] = true
				result = append(result, f)
			if e, ok := encountered[dest]; !ok {
				encountered[dest] = f
			} else {
				// If a module is directly included and also transitively depended on
				// consider it as directly included.
				e.transitiveDep = e.transitiveDep && f.transitiveDep
				encountered[dest] = e
			}
		}
		var result []apexFile
		for _, v := range encountered {
			result = append(result, v)
		}
		return result
	}
@@ -1220,12 +1256,6 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
		}
	}

	// 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 = filesInfo[i].moduleName + "." + a.Name() + a.suffix
	}

	a.installDir = android.PathForModuleInstall(ctx, "apex")
	a.filesInfo = filesInfo

@@ -1245,6 +1275,14 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
			return
		}
	}
	// Optimization. If we are building bundled APEX, for the files that are gathered due to the
	// transitive dependencies, don't place them inside the APEX, but place a symlink pointing
	// the same library in the system partition, thus effectively sharing the same libraries
	// across the APEX boundary. For unbundled APEX, all the gathered files are actually placed
	// in the APEX.
	a.linkToSystemLib = !ctx.Config().UnbundledBuild() &&
		a.installable() &&
		!proptools.Bool(a.properties.Use_vendor)

	// prepare apex_manifest.json
	a.buildManifest(ctx, provideNativeLibs, requireNativeLibs)
+127 −17
Original line number Diff line number Diff line
@@ -91,6 +91,10 @@ func withBinder32bit(fs map[string][]byte, config android.Config) {
	config.TestProductVariables.Binder32bit = proptools.BoolPtr(true)
}

func withUnbundledBuild(fs map[string][]byte, config android.Config) {
	config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
}

func testApexContext(t *testing.T, bp string, handlers ...testCustomizer) (*android.TestContext, android.Config) {
	android.ClearApexDependency()

@@ -1531,45 +1535,67 @@ func TestHeaderLibsDependency(t *testing.T) {
	ensureContains(t, cFlags, "-Imy_include")
}

func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName string, files []string) {
type fileInApex struct {
	path   string // path in apex
	isLink bool
}

func getFiles(t *testing.T, ctx *android.TestContext, moduleName string) []fileInApex {
	t.Helper()
	apexRule := ctx.ModuleForTests(moduleName, "android_common_"+moduleName+"_image").Rule("apexRule")
	copyCmds := apexRule.Args["copy_commands"]
	imageApexDir := "/image.apex/"
	var failed bool
	var surplus []string
	filesMatched := make(map[string]bool)
	addContent := func(content string) {
		for _, expected := range files {
			if matched, _ := path.Match(expected, content); matched {
				filesMatched[expected] = true
				return
			}
		}
		surplus = append(surplus, content)
	}
	var ret []fileInApex
	for _, cmd := range strings.Split(copyCmds, "&&") {
		cmd = strings.TrimSpace(cmd)
		if cmd == "" {
			continue
		}
		terms := strings.Split(cmd, " ")
		var dst string
		var isLink bool
		switch terms[0] {
		case "mkdir":
		case "cp":
			if len(terms) != 3 {
				t.Fatal("copyCmds contains invalid cp command", cmd)
			}
			dst := terms[2]
			dst = terms[2]
			isLink = false
		case "ln":
			if len(terms) != 3 && len(terms) != 4 {
				// ln LINK TARGET or ln -s LINK TARGET
				t.Fatal("copyCmds contains invalid ln command", cmd)
			}
			dst = terms[len(terms)-1]
			isLink = true
		default:
			t.Fatalf("copyCmds should contain mkdir/cp commands only: %q", cmd)
		}
		if dst != "" {
			index := strings.Index(dst, imageApexDir)
			if index == -1 {
				t.Fatal("copyCmds should copy a file to image.apex/", cmd)
			}
			dstFile := dst[index+len(imageApexDir):]
			addContent(dstFile)
		default:
			t.Fatalf("copyCmds should contain mkdir/cp commands only: %q", cmd)
			ret = append(ret, fileInApex{path: dstFile, isLink: isLink})
		}
	}
	return ret
}

func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName string, files []string) {
	var failed bool
	var surplus []string
	filesMatched := make(map[string]bool)
	for _, file := range getFiles(t, ctx, moduleName) {
		for _, expected := range files {
			if matched, _ := path.Match(expected, file.path); matched {
				filesMatched[expected] = true
				return
			}
		}
		surplus = append(surplus, file.path)
	}

	if len(surplus) > 0 {
@@ -3365,6 +3391,90 @@ func TestCarryRequiredModuleNames(t *testing.T) {
	ensureContains(t, androidMk, "LOCAL_TARGET_REQUIRED_MODULES += e f\n")
}

func TestSymlinksFromApexToSystem(t *testing.T) {
	bp := `
		apex {
			name: "myapex",
			key: "myapex.key",
			native_shared_libs: ["mylib"],
			java_libs: ["myjar"],
		}

		apex_key {
			name: "myapex.key",
			public_key: "testkey.avbpubkey",
			private_key: "testkey.pem",
		}

		cc_library {
			name: "mylib",
			srcs: ["mylib.cpp"],
			shared_libs: ["myotherlib"],
			system_shared_libs: [],
			stl: "none",
		}

		cc_library {
			name: "myotherlib",
			srcs: ["mylib.cpp"],
			system_shared_libs: [],
			stl: "none",
		}

		java_library {
			name: "myjar",
			srcs: ["foo/bar/MyClass.java"],
			sdk_version: "none",
			system_modules: "none",
			libs: ["myotherjar"],
			compile_dex: true,
		}

		java_library {
			name: "myotherjar",
			srcs: ["foo/bar/MyClass.java"],
			sdk_version: "none",
			system_modules: "none",
		}
	`

	ensureRealfileExists := func(t *testing.T, files []fileInApex, file string) {
		for _, f := range files {
			if f.path == file {
				if f.isLink {
					t.Errorf("%q is not a real file", file)
				}
				return
			}
		}
		t.Errorf("%q is not found", file)
	}

	ensureSymlinkExists := func(t *testing.T, files []fileInApex, file string) {
		for _, f := range files {
			if f.path == file {
				if !f.isLink {
					t.Errorf("%q is not a symlink", file)
				}
				return
			}
		}
		t.Errorf("%q is not found", file)
	}

	ctx, _ := testApex(t, bp, withUnbundledBuild)
	files := getFiles(t, ctx, "myapex")
	ensureRealfileExists(t, files, "javalib/myjar.jar")
	ensureRealfileExists(t, files, "lib64/mylib.so")
	ensureRealfileExists(t, files, "lib64/myotherlib.so")

	ctx, _ = testApex(t, bp)
	files = getFiles(t, ctx, "myapex")
	ensureRealfileExists(t, files, "javalib/myjar.jar")
	ensureRealfileExists(t, files, "lib64/mylib.so")
	ensureSymlinkExists(t, files, "lib64/myotherlib.so") // this is symlink
}

func TestMain(m *testing.M) {
	run := func() int {
		setUp()
+27 −21
Original line number Diff line number Diff line
@@ -245,36 +245,41 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) {

	apexType := a.properties.ApexType
	suffix := apexType.suffix()
	var implicitInputs []android.Path
	unsignedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix+".unsigned")

	filesToCopy := []android.Path{}
	for _, f := range a.filesInfo {
		filesToCopy = append(filesToCopy, f.builtFile)
	// TODO(jiyong): construct the copy rules using RuleBuilder
	var copyCommands []string
	for _, fi := range a.filesInfo {
		destPath := android.PathForModuleOut(ctx, "image"+suffix, fi.Path()).String()
		copyCommands = append(copyCommands, "mkdir -p "+filepath.Dir(destPath))
		if a.linkToSystemLib && fi.transitiveDep && fi.AvailableToPlatform() {
			// TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
			pathOnDevice := filepath.Join("/system", fi.Path())
			copyCommands = append(copyCommands, "ln -s "+pathOnDevice+" "+destPath)
		} else {
			copyCommands = append(copyCommands, "cp "+fi.builtFile.String()+" "+destPath)
			implicitInputs = append(implicitInputs, fi.builtFile)
		}
		// create additional symlinks pointing the file inside the APEX
		for _, symlinkPath := range fi.SymlinkPaths() {
			symlinkDest := android.PathForModuleOut(ctx, "image"+suffix, symlinkPath).String()
			copyCommands = append(copyCommands, "ln -s "+filepath.Base(destPath)+" "+symlinkDest)
		}
	}

	copyCommands := []string{}
	emitCommands := []string{}
	imageContentFile := android.PathForModuleOut(ctx, a.Name()+"-content.txt")
	// TODO(jiyong): use RuleBuilder
	var emitCommands []string
	imageContentFile := android.PathForModuleOut(ctx, "content.txt")
	emitCommands = append(emitCommands, "echo ./apex_manifest.pb >> "+imageContentFile.String())
	if proptools.Bool(a.properties.Legacy_android10_support) {
		emitCommands = append(emitCommands, "echo ./apex_manifest.json >> "+imageContentFile.String())
	}
	for i, src := range filesToCopy {
		dest := filepath.Join(a.filesInfo[i].installDir, src.Base())
		emitCommands = append(emitCommands, "echo './"+dest+"' >> "+imageContentFile.String())
		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)
		for _, sym := range a.filesInfo[i].symlinks {
			symlinkDest := filepath.Join(filepath.Dir(dest_path), sym)
			copyCommands = append(copyCommands, "ln -s "+filepath.Base(dest)+" "+symlinkDest)
		}
	for _, fi := range a.filesInfo {
		emitCommands = append(emitCommands, "echo './"+fi.Path()+"' >> "+imageContentFile.String())
	}
	emitCommands = append(emitCommands, "sort -o "+imageContentFile.String()+" "+imageContentFile.String())

	implicitInputs := append(android.Paths(nil), filesToCopy...)
	implicitInputs = append(implicitInputs, a.manifestPbOut)

	if a.properties.Whitelisted_files != nil {
		ctx.Build(pctx, android.BuildParams{
			Rule:        emitApexContentRule,
@@ -396,6 +401,7 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) {
			optFlags = append(optFlags, "--do_not_check_keyname")
		}

		implicitInputs = append(implicitInputs, a.manifestPbOut)
		if proptools.Bool(a.properties.Legacy_android10_support) {
			implicitInputs = append(implicitInputs, a.manifestJsonOut)
			optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String())
@@ -513,7 +519,7 @@ func (a *apexBundle) buildFilesInfo(ctx android.ModuleContext) {
	if a.installable() {
		// For flattened APEX, do nothing but make sure that APEX manifest and apex_pubkey are also copied along
		// with other ordinary files.
		a.filesInfo = append(a.filesInfo, newApexFile(ctx, a.manifestPbOut, "apex_manifest.pb."+a.Name()+a.suffix, ".", etc, nil))
		a.filesInfo = append(a.filesInfo, newApexFile(ctx, a.manifestPbOut, "apex_manifest.pb", ".", etc, nil))

		// rename to apex_pubkey
		copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey")
@@ -522,7 +528,7 @@ func (a *apexBundle) buildFilesInfo(ctx android.ModuleContext) {
			Input:  a.public_key_file,
			Output: copiedPubkey,
		})
		a.filesInfo = append(a.filesInfo, newApexFile(ctx, copiedPubkey, "apex_pubkey."+a.Name()+a.suffix, ".", etc, nil))
		a.filesInfo = append(a.filesInfo, newApexFile(ctx, copiedPubkey, "apex_pubkey", ".", etc, nil))

		if a.properties.ApexType == flattenedApex {
			apexName := proptools.StringDefault(a.properties.Apex_name, a.Name())