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

Commit f653b05d authored by Jiyong Park's avatar Jiyong Park
Browse files

Refactor the routine for creating apexFile

This change is to make it easier to add new fields to the struct.
transitiveDep field is added to distinguish apexFiles coming from
transitive dependencies of the APEX. We will later use the info to
reduce the size of bundled APEXes by replacing the transitive deps with
symlinks to the corresponding files in the system partition outside of
the APEX.

Bug: 144533348
Test: m

Change-Id: I283859f2f2f1b5cfb3025569f168ba8569b22bb9
parent 66905ed6
Loading
Loading
Loading
Loading
+125 −116
Original line number Diff line number Diff line
@@ -370,20 +370,6 @@ type overridableProperties struct {
	Apps []string
}

type apexFileClass int

const (
	etc apexFileClass = iota
	nativeSharedLib
	nativeExecutable
	shBinary
	pyBinary
	goBinary
	javaSharedLib
	nativeTest
	app
)

type apexPackaging int

const (
@@ -415,6 +401,20 @@ func (a apexPackaging) name() string {
	}
}

type apexFileClass int

const (
	etc apexFileClass = iota
	nativeSharedLib
	nativeExecutable
	shBinary
	pyBinary
	goBinary
	javaSharedLib
	nativeTest
	app
)

func (class apexFileClass) NameInMake() string {
	switch class {
	case etc:
@@ -441,13 +441,30 @@ func (class apexFileClass) NameInMake() string {
	}
}

// apexFile represents a file in an APEX bundle
type apexFile struct {
	builtFile  android.Path
	moduleName string
	installDir string
	class      apexFileClass
	module     android.Module
	// list of symlinks that will be created in installDir that point to this apexFile
	symlinks      []string
	transitiveDep bool
}

func newApexFile(builtFile android.Path, moduleName string, installDir string, class apexFileClass, module android.Module) apexFile {
	return apexFile{
		builtFile:  builtFile,
		moduleName: moduleName,
		installDir: installDir,
		class:      class,
		module:     module,
	}
}

func (af *apexFile) Ok() bool {
	return af.builtFile != nil || af.builtFile.String() == ""
}

type apexBundle struct {
@@ -761,9 +778,11 @@ func (a *apexBundle) HideFromMake() {
	a.properties.HideFromMake = true
}

func getCopyManifestForNativeLibrary(ccMod *cc.Module, config android.Config, handleSpecialLibs bool) (fileToCopy android.Path, dirInApex string) {
// TODO(jiyong) move apexFileFor* close to the apexFile type definition
func apexFileForNativeLibrary(ccMod *cc.Module, config android.Config, handleSpecialLibs bool) apexFile {
	// Decide the APEX-local directory by the multilib of the library
	// In the future, we may query this to the module.
	var dirInApex string
	switch ccMod.Arch().ArchType.Multilib {
	case "lib32":
		dirInApex = "lib"
@@ -788,83 +807,84 @@ func getCopyManifestForNativeLibrary(ccMod *cc.Module, config android.Config, ha
		dirInApex = filepath.Join(dirInApex, "bionic")
	}

	fileToCopy = ccMod.OutputFile().Path()
	return
	fileToCopy := ccMod.OutputFile().Path()
	return newApexFile(fileToCopy, ccMod.Name(), dirInApex, nativeSharedLib, ccMod)
}

func getCopyManifestForExecutable(cc *cc.Module) (fileToCopy android.Path, dirInApex string) {
	dirInApex = filepath.Join("bin", cc.RelativeInstallPath())
func apexFileForExecutable(cc *cc.Module) apexFile {
	dirInApex := filepath.Join("bin", cc.RelativeInstallPath())
	if cc.Target().NativeBridge == android.NativeBridgeEnabled {
		dirInApex = filepath.Join(dirInApex, cc.Target().NativeBridgeRelativePath)
	}
	fileToCopy = cc.OutputFile().Path()
	return
	fileToCopy := cc.OutputFile().Path()
	af := newApexFile(fileToCopy, cc.Name(), dirInApex, nativeExecutable, cc)
	af.symlinks = cc.Symlinks()
	return af
}

func getCopyManifestForPyBinary(py *python.Module) (fileToCopy android.Path, dirInApex string) {
	dirInApex = "bin"
	fileToCopy = py.HostToolPath().Path()
	return
func apexFileForPyBinary(py *python.Module) apexFile {
	dirInApex := "bin"
	fileToCopy := py.HostToolPath().Path()
	return newApexFile(fileToCopy, py.Name(), dirInApex, pyBinary, py)
}
func getCopyManifestForGoBinary(ctx android.ModuleContext, gb bootstrap.GoBinaryTool) (fileToCopy android.Path, dirInApex string) {
	dirInApex = "bin"
func apexFileForGoBinary(ctx android.ModuleContext, depName string, gb bootstrap.GoBinaryTool) apexFile {
	dirInApex := "bin"
	s, err := filepath.Rel(android.PathForOutput(ctx).String(), gb.InstallPath())
	if err != nil {
		ctx.ModuleErrorf("Unable to use compiled binary at %s", gb.InstallPath())
		return
		return apexFile{}
	}
	fileToCopy = android.PathForOutput(ctx, s)
	return
	fileToCopy := android.PathForOutput(ctx, s)
	// NB: Since go binaries are static we don't need the module for anything here, which is
	// good since the go tool is a blueprint.Module not an android.Module like we would
	// normally use.
	return newApexFile(fileToCopy, depName, dirInApex, goBinary, nil)
}

func getCopyManifestForShBinary(sh *android.ShBinary) (fileToCopy android.Path, dirInApex string) {
	dirInApex = filepath.Join("bin", sh.SubDir())
	fileToCopy = sh.OutputFile()
	return
func apexFileForShBinary(sh *android.ShBinary) apexFile {
	dirInApex := filepath.Join("bin", sh.SubDir())
	fileToCopy := sh.OutputFile()
	af := newApexFile(fileToCopy, sh.Name(), dirInApex, shBinary, sh)
	af.symlinks = sh.Symlinks()
	return af
}

func getCopyManifestForJavaLibrary(java *java.Library) (fileToCopy android.Path, dirInApex string) {
	dirInApex = "javalib"
	fileToCopy = java.DexJarFile()
	return
func apexFileForJavaLibrary(java *java.Library) apexFile {
	dirInApex := "javalib"
	fileToCopy := java.DexJarFile()
	return newApexFile(fileToCopy, java.Name(), dirInApex, javaSharedLib, java)
}

func getCopyManifestForPrebuiltJavaLibrary(java *java.Import) (fileToCopy android.Path, dirInApex string) {
	dirInApex = "javalib"
func apexFileForPrebuiltJavaLibrary(java *java.Import) apexFile {
	dirInApex := "javalib"
	// The output is only one, but for some reason, ImplementationJars returns Paths, not Path
	implJars := java.ImplementationJars()
	if len(implJars) != 1 {
		panic(fmt.Errorf("java.ImplementationJars() must return single Path, but got: %s",
			strings.Join(implJars.Strings(), ", ")))
	}
	fileToCopy = implJars[0]
	return
}

func getCopyManifestForPrebuiltEtc(prebuilt android.PrebuiltEtcModule) (fileToCopy android.Path, dirInApex string) {
	dirInApex = filepath.Join("etc", prebuilt.SubDir())
	fileToCopy = prebuilt.OutputFile()
	return
	fileToCopy := implJars[0]
	return newApexFile(fileToCopy, java.Name(), dirInApex, javaSharedLib, java)
}

func getCopyManifestForAndroidApp(app *java.AndroidApp, pkgName string) (fileToCopy android.Path, dirInApex string) {
	appDir := "app"
	if app.Privileged() {
		appDir = "priv-app"
	}
	dirInApex = filepath.Join(appDir, pkgName)
	fileToCopy = app.OutputFile()
	return
func apexFileForPrebuiltEtc(prebuilt android.PrebuiltEtcModule, depName string) apexFile {
	dirInApex := filepath.Join("etc", prebuilt.SubDir())
	fileToCopy := prebuilt.OutputFile()
	return newApexFile(fileToCopy, depName, dirInApex, etc, prebuilt)
}

func getCopyManifestForAndroidAppImport(app *java.AndroidAppImport, pkgName string) (fileToCopy android.Path, dirInApex string) {
func apexFileForAndroidApp(aapp interface {
	android.Module
	Privileged() bool
	OutputFile() android.Path
}, pkgName string) apexFile {
	appDir := "app"
	if app.Privileged() {
	if aapp.Privileged() {
		appDir = "priv-app"
	}
	dirInApex = filepath.Join(appDir, pkgName)
	fileToCopy = app.OutputFile()
	return
	dirInApex := filepath.Join(appDir, pkgName)
	fileToCopy := aapp.OutputFile()
	return newApexFile(fileToCopy, aapp.Name(), dirInApex, app, aapp)
}

// Context "decorator", overriding the InstallBypassMake method to always reply `true`.
@@ -877,8 +897,6 @@ func (c *flattenedApexContext) InstallBypassMake() bool {
}

func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
	filesInfo := []apexFile{}

	buildFlattenedAsDefault := ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuild()
	switch a.properties.ApexType {
	case imageApex:
@@ -939,68 +957,67 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
		providedNativeSharedLibs = append(providedNativeSharedLibs, other.properties.Native_shared_libs...)
	})

	var filesInfo []apexFile
	ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
		depTag := ctx.OtherModuleDependencyTag(child)
		depName := ctx.OtherModuleName(child)
		if _, ok := parent.(*apexBundle); ok {
			// direct dependencies
		if _, isDirectDep := parent.(*apexBundle); isDirectDep {
			switch depTag {
			case sharedLibTag:
				if cc, ok := child.(*cc.Module); ok {
					if cc.HasStubsVariants() {
						provideNativeLibs = append(provideNativeLibs, cc.OutputFile().Path().Base())
					}
					fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc, ctx.Config(), handleSpecialLibs)
					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeSharedLib, cc, nil})
					return true
					filesInfo = append(filesInfo, apexFileForNativeLibrary(cc, ctx.Config(), handleSpecialLibs))
					return true // track transitive dependencies
				} else {
					ctx.PropertyErrorf("native_shared_libs", "%q is not a cc_library or cc_library_shared module", depName)
				}
			case executableTag:
				if cc, ok := child.(*cc.Module); ok {
					fileToCopy, dirInApex := getCopyManifestForExecutable(cc)
					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeExecutable, cc, cc.Symlinks()})
					return true
					filesInfo = append(filesInfo, apexFileForExecutable(cc))
					return true // track transitive dependencies
				} else if sh, ok := child.(*android.ShBinary); ok {
					fileToCopy, dirInApex := getCopyManifestForShBinary(sh)
					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, shBinary, sh, sh.Symlinks()})
					filesInfo = append(filesInfo, apexFileForShBinary(sh))
				} else if py, ok := child.(*python.Module); ok && py.HostToolPath().Valid() {
					fileToCopy, dirInApex := getCopyManifestForPyBinary(py)
					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, pyBinary, py, nil})
					filesInfo = append(filesInfo, apexFileForPyBinary(py))
				} else if gb, ok := child.(bootstrap.GoBinaryTool); ok && a.Host() {
					fileToCopy, dirInApex := getCopyManifestForGoBinary(ctx, gb)
					// NB: Since go binaries are static we don't need the module for anything here, which is
					// good since the go tool is a blueprint.Module not an android.Module like we would
					// normally use.
					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, goBinary, nil, nil})
					filesInfo = append(filesInfo, apexFileForGoBinary(ctx, depName, gb))
				} else {
					ctx.PropertyErrorf("binaries", "%q is neither cc_binary, (embedded) py_binary, (host) blueprint_go_binary, (host) bootstrap_go_binary, nor sh_binary", depName)
				}
			case javaLibTag:
				if javaLib, ok := child.(*java.Library); ok {
					fileToCopy, dirInApex := getCopyManifestForJavaLibrary(javaLib)
					if fileToCopy == nil {
					af := apexFileForJavaLibrary(javaLib)
					if !af.Ok() {
						ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
					} else {
						filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, javaSharedLib, javaLib, nil})
						filesInfo = append(filesInfo, af)
						return true // track transitive dependencies
					}
					return true
				} else if javaLib, ok := child.(*java.Import); ok {
					fileToCopy, dirInApex := getCopyManifestForPrebuiltJavaLibrary(javaLib)
					if fileToCopy == nil {
					af := apexFileForPrebuiltJavaLibrary(javaLib)
					if !af.Ok() {
						ctx.PropertyErrorf("java_libs", "%q does not have a jar output", depName)
					} else {
						filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, javaSharedLib, javaLib, nil})
						filesInfo = append(filesInfo, af)
					}
					return true
				} else {
					ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
				}
			case androidAppTag:
				pkgName := ctx.DeviceConfig().OverridePackageNameFor(depName)
				if ap, ok := child.(*java.AndroidApp); ok {
					filesInfo = append(filesInfo, apexFileForAndroidApp(ap, pkgName))
					return true // track transitive dependencies
				} else if ap, ok := child.(*java.AndroidAppImport); ok {
					filesInfo = append(filesInfo, apexFileForAndroidApp(ap, pkgName))
				} else {
					ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
				}
			case prebuiltTag:
				if prebuilt, ok := child.(android.PrebuiltEtcModule); ok {
					fileToCopy, dirInApex := getCopyManifestForPrebuiltEtc(prebuilt)
					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, etc, prebuilt, nil})
					return true
					filesInfo = append(filesInfo, apexFileForPrebuiltEtc(prebuilt, depName))
				} else {
					ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
				}
@@ -1016,10 +1033,10 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
						return true
					} else {
						// Single-output test module (where `test_per_src: false`).
						fileToCopy, dirInApex := getCopyManifestForExecutable(ccTest)
						filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeTest, ccTest, nil})
						af := apexFileForExecutable(ccTest)
						af.class = nativeTest
						filesInfo = append(filesInfo, af)
					}
					return true
				} else {
					ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
				}
@@ -1027,15 +1044,14 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
				if key, ok := child.(*apexKey); ok {
					a.private_key_file = key.private_key_file
					a.public_key_file = key.public_key_file
					return false
				} else {
					ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
				}
				return false
			case certificateTag:
				if dep, ok := child.(*java.AndroidAppCertificate); ok {
					a.container_certificate_file = dep.Certificate.Pem
					a.container_private_key_file = dep.Certificate.Key
					return false
				} else {
					ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
				}
@@ -1045,17 +1061,6 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
				if prebuilt, ok := child.(*Prebuilt); ok && prebuilt.isForceDisabled() {
					a.prebuiltFileToDelete = prebuilt.InstallFilename()
				}
			case androidAppTag:
				if ap, ok := child.(*java.AndroidApp); ok {
					fileToCopy, dirInApex := getCopyManifestForAndroidApp(ap, ctx.DeviceConfig().OverridePackageNameFor(depName))
					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, app, ap, nil})
					return true
				} else if ap, ok := child.(*java.AndroidAppImport); ok {
					fileToCopy, dirInApex := getCopyManifestForAndroidAppImport(ap, ctx.DeviceConfig().OverridePackageNameFor(depName))
					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, app, ap, nil})
				} else {
					ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
				}
			}
		} else if !a.vndkApex {
			// indirect dependencies
@@ -1084,23 +1089,26 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
							// Don't track further
							return false
						}
						fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc, ctx.Config(), handleSpecialLibs)
						filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeSharedLib, cc, nil})
						return true
						af := apexFileForNativeLibrary(cc, ctx.Config(), handleSpecialLibs)
						af.transitiveDep = true
						filesInfo = append(filesInfo, af)
						return true // track transitive dependencies
					}
				} else if cc.IsTestPerSrcDepTag(depTag) {
					if cc, ok := child.(*cc.Module); ok {
						fileToCopy, dirInApex := getCopyManifestForExecutable(cc)
						af := apexFileForExecutable(cc)
						// Handle modules created as `test_per_src` variations of a single test module:
						// use the name of the generated test binary (`fileToCopy`) instead of the name
						// of the original test module (`depName`, shared by all `test_per_src`
						// variations of that module).
						moduleName := filepath.Base(fileToCopy.String())
						filesInfo = append(filesInfo, apexFile{fileToCopy, moduleName, dirInApex, nativeTest, cc, nil})
						return true
						af.moduleName = filepath.Base(af.builtFile.String())
						af.transitiveDep = true
						filesInfo = append(filesInfo, af)
						return true // track transitive dependencies
					}
				} else if java.IsJniDepTag(depTag) {
					// Do nothing for JNI dep. JNI libraries are always embedded in APK-in-APEX.
					return true
				} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
					ctx.ModuleErrorf("unexpected tag %q for indirect dependency %q", depTag, depName)
				}
@@ -1117,7 +1125,8 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
			dirInApex := filepath.Join("javalib", arch.String())
			for _, f := range files {
				localModule := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
				filesInfo = append(filesInfo, apexFile{f, localModule, dirInApex, etc, nil, nil})
				af := newApexFile(f, localModule, dirInApex, etc, nil)
				filesInfo = append(filesInfo, af)
			}
		}
	}
+2 −1
Original line number Diff line number Diff line
@@ -2994,10 +2994,11 @@ func TestOverrideApex(t *testing.T) {
	var builder strings.Builder
	data.Custom(&builder, name, "TARGET_", "", data)
	androidMk := builder.String()
	ensureContains(t, androidMk, "LOCAL_MODULE := app.override_myapex")
	ensureContains(t, androidMk, "LOCAL_MODULE := override_app.override_myapex")
	ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.override_myapex")
	ensureContains(t, androidMk, "LOCAL_MODULE_STEM := override_myapex.apex")
	ensureNotContains(t, androidMk, "LOCAL_MODULE := app.myapex")
	ensureNotContains(t, androidMk, "LOCAL_MODULE := override_app.myapex")
	ensureNotContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex")
	ensureNotContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.apex")
}
+3 −3
Original line number Diff line number Diff line
@@ -501,8 +501,8 @@ func (a *apexBundle) buildFilesInfo(ctx android.ModuleContext) {
	if a.installable() {
		// For flattened APEX, do nothing but make sure that apex_manifest.json and apex_pubkey are also copied along
		// with other ordinary files.
		a.filesInfo = append(a.filesInfo, apexFile{a.manifestJsonOut, "apex_manifest.json." + a.Name() + a.suffix, ".", etc, nil, nil})
		a.filesInfo = append(a.filesInfo, apexFile{a.manifestPbOut, "apex_manifest.pb." + a.Name() + a.suffix, ".", etc, nil, nil})
		a.filesInfo = append(a.filesInfo, newApexFile(a.manifestJsonOut, "apex_manifest.json."+a.Name()+a.suffix, ".", etc, nil))
		a.filesInfo = append(a.filesInfo, newApexFile(a.manifestPbOut, "apex_manifest.pb."+a.Name()+a.suffix, ".", etc, nil))

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

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