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

Commit eb285d43 authored by Ulyana Trafimovich's avatar Ulyana Trafimovich Committed by Automerger Merge Worker
Browse files

Merge "Propagate transitive SDK Java library dependencies to dexpreopt." am: b8822a06

Original change: https://android-review.googlesource.com/c/platform/build/soong/+/1408708

Change-Id: Ia5c85d4370deff72b1dbc83064440ea9f1a1440d
parents 44c0a177 b8822a06
Loading
Loading
Loading
Loading
+26 −8
Original line number Diff line number Diff line
@@ -111,12 +111,9 @@ type LibraryPath struct {
// LibraryPaths is a map from library name to on-host and on-device paths to its DEX jar.
type LibraryPaths map[string]*LibraryPath

// Add a new path to the map of library paths, unless a path for this library already exists.
func (libPaths LibraryPaths) AddLibraryPath(ctx android.PathContext, lib *string, hostPath, installPath android.Path) {
	if lib == nil {
		return
	}
	if _, present := libPaths[*lib]; !present {
// Add a new library path to the map, unless a path for this library already exists.
func (libPaths LibraryPaths) addLibraryPath(ctx android.PathContext, lib string, hostPath, installPath android.Path) {
	if _, present := libPaths[lib]; !present {
		var devicePath string
		if installPath != nil {
			devicePath = android.InstallPathToOnDevicePath(ctx, installPath.(android.InstallPath))
@@ -127,9 +124,30 @@ func (libPaths LibraryPaths) AddLibraryPath(ctx android.PathContext, lib *string
			// but we cannot use if for dexpreopt.
			devicePath = UnknownInstallLibraryPath
		}
		libPaths[*lib] = &LibraryPath{hostPath, devicePath}
		libPaths[lib] = &LibraryPath{hostPath, devicePath}
	}
}

// Add a new library path to the map. Ensure that the build path to the library exists.
func (libPaths LibraryPaths) AddLibraryPath(ctx android.PathContext, lib string, hostPath, installPath android.Path) {
	if hostPath != nil {
		// Add a library only if the build path to it is known.
		libPaths.addLibraryPath(ctx, lib, hostPath, installPath)
	} else if !ctx.Config().AllowMissingDependencies() {
		// Error on libraries with unknown build paths, unless missing dependencies are allowed.
		android.ReportPathErrorf(ctx, "unknown build path to <uses-library> '%s'", lib)
	} else {
		// Not adding a library to the map will likely result in disabling dexpreopt.
	}
}

// Add a new library path to the map, if the library exists (name is not nil).
func (libPaths LibraryPaths) MaybeAddLibraryPath(ctx android.PathContext, lib *string, hostPath, installPath android.Path) {
	if lib != nil {
		// Don't check the build paths, add in any case. Some libraries may be missing from the
		// build, but their names still need to be added to <uses-library> tags in the manifest.
		libPaths.addLibraryPath(ctx, *lib, hostPath, installPath)
	}
	return
}

// Add library paths from the second map to the first map (do not override existing entries).
+91 −41
Original line number Diff line number Diff line
@@ -81,15 +81,14 @@ func GenerateDexpreoptRule(ctx android.PathContext, globalSoong *GlobalSoongConf
	}

	if !dexpreoptDisabled(ctx, global, module) {
		// Don't preopt individual boot jars, they will be preopted together.
		if !global.BootJars.ContainsJar(module.Name) {
		if clc := genClassLoaderContext(ctx, global, module); clc != nil {
			appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) &&
				!module.NoCreateAppImage

			generateDM := shouldGenerateDM(module, global)

			for archIdx, _ := range module.Archs {
				dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage, generateDM)
				dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, *clc, profile, appImage, generateDM)
			}
		}
	}
@@ -102,6 +101,11 @@ func dexpreoptDisabled(ctx android.PathContext, global *GlobalConfig, module *Mo
		return true
	}

	// Don't preopt individual boot jars, they will be preopted together.
	if global.BootJars.ContainsJar(module.Name) {
		return true
	}

	// Don't preopt system server jars that are updatable.
	if global.UpdatableSystemServerJars.ContainsJar(module.Name) {
		return true
@@ -214,13 +218,17 @@ func (m classLoaderContextMap) getValue(sdkVer int) *classLoaderContext {
	return m[sdkVer]
}

func (m classLoaderContextMap) addLibs(sdkVer int, module *ModuleConfig, libs ...string) {
func (m classLoaderContextMap) addLibs(sdkVer int, module *ModuleConfig, libs ...string) bool {
	clc := m.getValue(sdkVer)
	for _, lib := range libs {
		p := pathForLibrary(module, lib)
		if p := pathForLibrary(module, lib); p != nil {
			clc.Host = append(clc.Host, p.Host)
			clc.Target = append(clc.Target, p.Device)
		} else {
			return false
		}
	}
	return true
}

func (m classLoaderContextMap) addSystemServerLibs(sdkVer int, ctx android.PathContext, module *ModuleConfig, libs ...string) {
@@ -231,9 +239,79 @@ func (m classLoaderContextMap) addSystemServerLibs(sdkVer int, ctx android.PathC
	}
}

// genClassLoaderContext generates host and target class loader context to be passed to the dex2oat
// command for the dexpreopted module. There are three possible cases:
//
// 1. System server jars. They have a special class loader context that includes other system
//    server jars.
//
// 2. Library jars or APKs which have precise list of their <uses-library> libs. Their class loader
//    context includes build and on-device paths to these libs. In some cases it may happen that
//    the path to a <uses-library> is unknown (e.g. the dexpreopted module may depend on stubs
//    library, whose implementation library is missing from the build altogether). In such case
//    dexpreopting with the <uses-library> is impossible, and dexpreopting without it is pointless,
//    as the runtime classpath won't match and the dexpreopted code will be discarded. Therefore in
//    such cases the function returns nil, which disables dexpreopt.
//
// 2. All other library jars or APKs for which the exact <uses-library> list is unknown. They use
//    the unsafe &-classpath workaround that means empty class loader context and absence of runtime
//    check that the class loader context provided by the PackageManager agrees with the stored
//    class loader context recorded in the .odex file.
//
func genClassLoaderContext(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) *classLoaderContextMap {
	classLoaderContexts := make(classLoaderContextMap)
	systemServerJars := NonUpdatableSystemServerJars(ctx, global)

	if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
		// System server jars should be dexpreopted together: class loader context of each jar
		// should include all preceding jars on the system server classpath.
		classLoaderContexts.addSystemServerLibs(anySdkVersion, ctx, module, systemServerJars[:jarIndex]...)

	} else if module.EnforceUsesLibraries {
		// Unconditional class loader context.
		usesLibs := append(copyOf(module.UsesLibraries), module.OptionalUsesLibraries...)
		if !classLoaderContexts.addLibs(anySdkVersion, module, usesLibs...) {
			return nil
		}

		// Conditional class loader context for API version < 28.
		const httpLegacy = "org.apache.http.legacy"
		if !contains(usesLibs, httpLegacy) {
			if !classLoaderContexts.addLibs(28, module, httpLegacy) {
				return nil
			}
		}

		// Conditional class loader context for API version < 29.
		usesLibs29 := []string{
			"android.hidl.base-V1.0-java",
			"android.hidl.manager-V1.0-java",
		}
		if !classLoaderContexts.addLibs(29, module, usesLibs29...) {
			return nil
		}

		// Conditional class loader context for API version < 30.
		const testBase = "android.test.base"
		if !contains(usesLibs, testBase) {
			if !classLoaderContexts.addLibs(30, module, testBase) {
				return nil
			}
		}

	} else {
		// Pass special class loader context to skip the classpath and collision check.
		// This will get removed once LOCAL_USES_LIBRARIES is enforced.
		// Right now LOCAL_USES_LIBRARIES is opt in, for the case where it's not specified we still default
		// to the &.
	}

	return &classLoaderContexts
}

func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
	module *ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath,
	appImage bool, generateDM bool) {
	module *ModuleConfig, rule *android.RuleBuilder, archIdx int, classLoaderContexts classLoaderContextMap,
	profile android.WritablePath, appImage bool, generateDM bool) {

	arch := module.Archs[archIdx]

@@ -264,17 +342,12 @@ func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, g

	invocationPath := odexPath.ReplaceExtension(ctx, "invocation")

	classLoaderContexts := make(classLoaderContextMap)
	systemServerJars := NonUpdatableSystemServerJars(ctx, global)

	rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String()))
	rule.Command().FlagWithOutput("rm -f ", odexPath)

	if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
		// System server jars should be dexpreopted together: class loader context of each jar
		// should include all preceding jars on the system server classpath.
		classLoaderContexts.addSystemServerLibs(anySdkVersion, ctx, module, systemServerJars[:jarIndex]...)

		// Copy the system server jar to a predefined location where dex2oat will find it.
		dexPathHost := SystemServerDexJarHostPath(ctx, module.Name)
		rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String()))
@@ -288,29 +361,6 @@ func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, g
			Implicits(clc.Host).
			Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clc.Target, ":") + "]")
	} else if module.EnforceUsesLibraries {
		// Unconditional class loader context.
		usesLibs := append(copyOf(module.UsesLibraries), module.OptionalUsesLibraries...)
		classLoaderContexts.addLibs(anySdkVersion, module, usesLibs...)

		// Conditional class loader context for API version < 28.
		const httpLegacy = "org.apache.http.legacy"
		if !contains(usesLibs, httpLegacy) {
			classLoaderContexts.addLibs(28, module, httpLegacy)
		}

		// Conditional class loader context for API version < 29.
		usesLibs29 := []string{
			"android.hidl.base-V1.0-java",
			"android.hidl.manager-V1.0-java",
		}
		classLoaderContexts.addLibs(29, module, usesLibs29...)

		// Conditional class loader context for API version < 30.
		const testBase = "android.test.base"
		if !contains(usesLibs, testBase) {
			classLoaderContexts.addLibs(30, module, testBase)
		}

		// Generate command that saves target SDK version in a shell variable.
		if module.ManifestPath != nil {
			rule.Command().Text(`target_sdk_version="$(`).
@@ -540,11 +590,11 @@ func PathToLocation(path android.Path, arch android.ArchType) string {
}

func pathForLibrary(module *ModuleConfig, lib string) *LibraryPath {
	path, ok := module.LibraryPaths[lib]
	if !ok {
		panic(fmt.Errorf("unknown library path for %q", lib))
	}
	if path, ok := module.LibraryPaths[lib]; ok && path.Host != nil && path.Device != "error" {
		return path
	} else {
		return nil
	}
}

func makefileMatch(pattern, s string) bool {
+1 −1
Original line number Diff line number Diff line
@@ -390,7 +390,7 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStati
			// (including the java_sdk_library) itself then append any implicit sdk library
			// names to the list of sdk libraries to be added to the manifest.
			if component, ok := module.(SdkLibraryComponentDependency); ok {
				sdkLibraries.AddLibraryPath(ctx, component.OptionalImplicitSdkLibrary(),
				sdkLibraries.MaybeAddLibraryPath(ctx, component.OptionalImplicitSdkLibrary(),
					component.DexJarBuildPath(), component.DexJarInstallPath())
			}

+28 −2
Original line number Diff line number Diff line
@@ -586,7 +586,7 @@ func (a *AndroidApp) installPath(ctx android.ModuleContext) android.InstallPath
	return android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
}

func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path {
func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext, sdkLibs dexpreopt.LibraryPaths) android.Path {
	a.dexpreopter.installPath = a.installPath(ctx)
	if a.dexProperties.Uncompress_dex == nil {
		// If the value was not force-set by the user, use reasonable default based on the module.
@@ -597,6 +597,7 @@ func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path {
	a.dexpreopter.usesLibs = a.usesLibrary.usesLibraryProperties.Uses_libs
	a.dexpreopter.optionalUsesLibs = a.usesLibrary.presentOptionalUsesLibs(ctx)
	a.dexpreopter.libraryPaths = a.usesLibrary.usesLibraryPaths(ctx)
	a.dexpreopter.libraryPaths.AddLibraryPaths(sdkLibs)
	a.dexpreopter.manifestFile = a.mergedManifestFile
	a.exportedSdkLibs = make(dexpreopt.LibraryPaths)

@@ -767,6 +768,15 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
	// Process all building blocks, from AAPT to certificates.
	a.aaptBuildActions(ctx)

	// The decision to enforce <uses-library> checks is made before adding implicit SDK libraries.
	a.usesLibrary.freezeEnforceUsesLibraries()

	// Add implicit SDK libraries to <uses-library> list.
	for _, usesLib := range android.SortedStringKeys(a.aapt.sdkLibraries) {
		a.usesLibrary.addLib(usesLib, inList(usesLib, optionalUsesLibs))
	}

	// Check that the <uses-library> list is coherent with the manifest.
	if a.usesLibrary.enforceUsesLibraries() {
		manifestCheckFile := a.usesLibrary.verifyUsesLibrariesManifest(ctx, a.mergedManifestFile)
		apkDeps = append(apkDeps, manifestCheckFile)
@@ -779,7 +789,7 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
	a.linter.resources = a.aapt.resourceFiles
	a.linter.buildModuleReportZip = ctx.Config().UnbundledBuildApps()

	dexJarFile := a.dexBuildActions(ctx)
	dexJarFile := a.dexBuildActions(ctx, a.aapt.sdkLibraries)

	jniLibs, certificateDeps := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis))
	jniJarFile := a.jniBuildActions(jniLibs, ctx)
@@ -1888,6 +1898,16 @@ type usesLibrary struct {
	usesLibraryProperties UsesLibraryProperties
}

func (u *usesLibrary) addLib(lib string, optional bool) {
	if !android.InList(lib, u.usesLibraryProperties.Uses_libs) && !android.InList(lib, u.usesLibraryProperties.Optional_uses_libs) {
		if optional {
			u.usesLibraryProperties.Optional_uses_libs = append(u.usesLibraryProperties.Optional_uses_libs, lib)
		} else {
			u.usesLibraryProperties.Uses_libs = append(u.usesLibraryProperties.Uses_libs, lib)
		}
	}
}

func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, hasFrameworkLibs bool) {
	if !ctx.Config().UnbundledBuild() {
		ctx.AddVariationDependencies(nil, usesLibTag, u.usesLibraryProperties.Uses_libs...)
@@ -1960,6 +1980,12 @@ func (u *usesLibrary) enforceUsesLibraries() bool {
	return BoolDefault(u.usesLibraryProperties.Enforce_uses_libs, defaultEnforceUsesLibs)
}

// Freeze the value of `enforce_uses_libs` based on the current values of `uses_libs` and `optional_uses_libs`.
func (u *usesLibrary) freezeEnforceUsesLibraries() {
	enforce := u.enforceUsesLibraries()
	u.usesLibraryProperties.Enforce_uses_libs = &enforce
}

// verifyUsesLibrariesManifest checks the <uses-library> tags in an AndroidManifest.xml against the ones specified
// in the uses_libs and optional_uses_libs properties.  It returns the path to a copy of the manifest.
func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path) android.Path {
+27 −4
Original line number Diff line number Diff line
@@ -2618,6 +2618,19 @@ func TestUsesLibraries(t *testing.T) {
		android_app {
			name: "app",
			srcs: ["a.java"],
			libs: ["qux", "quuz"],
			static_libs: ["static-runtime-helper"],
			uses_libs: ["foo"],
			sdk_version: "current",
			optional_uses_libs: [
				"bar",
				"baz",
			],
		}

		android_app {
			name: "app_with_stub_deps",
			srcs: ["a.java"],
			libs: ["qux", "quuz.stubs"],
			static_libs: ["static-runtime-helper"],
			uses_libs: ["foo"],
@@ -2648,6 +2661,7 @@ func TestUsesLibraries(t *testing.T) {
	run(t, ctx, config)

	app := ctx.ModuleForTests("app", "android_common")
	appWithStubDeps := ctx.ModuleForTests("app_with_stub_deps", "android_common")
	prebuilt := ctx.ModuleForTests("prebuilt", "android_common")

	// Test that implicit dependencies on java_sdk_library instances are passed to the manifest.
@@ -2678,15 +2692,24 @@ func TestUsesLibraries(t *testing.T) {
		t.Errorf("wanted %q in %q", w, cmd)
	}

	// Test that only present libraries are preopted
	// Test that all present libraries are preopted, including implicit SDK dependencies
	cmd = app.Rule("dexpreopt").RuleParams.Command

	if w := `--target-classpath-for-sdk any /system/framework/foo.jar:/system/framework/bar.jar`; !strings.Contains(cmd, w) {
	w := `--target-classpath-for-sdk any` +
		` /system/framework/foo.jar` +
		`:/system/framework/quuz.jar` +
		`:/system/framework/qux.jar` +
		`:/system/framework/runtime-library.jar` +
		`:/system/framework/bar.jar`
	if !strings.Contains(cmd, w) {
		t.Errorf("wanted %q in %q", w, cmd)
	}

	cmd = prebuilt.Rule("dexpreopt").RuleParams.Command
	// TODO(skvadrik) fix dexpreopt for stub libraries for which the implementation is present
	if appWithStubDeps.MaybeRule("dexpreopt").RuleParams.Command != "" {
		t.Errorf("dexpreopt should be disabled for apps with dependencies on stub libraries")
	}

	cmd = prebuilt.Rule("dexpreopt").RuleParams.Command
	if w := `--target-classpath-for-sdk any /system/framework/foo.jar:/system/framework/bar.jar`; !strings.Contains(cmd, w) {
		t.Errorf("wanted %q in %q", w, cmd)
	}
Loading