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

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

Merge "Add structured representation for colon-separated jar lists." am: 910eb708 am: 434e7081

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

Change-Id: I155b303e73442fb44f38621bf0fa9f2157a77370
parents 36651b61 434e7081
Loading
Loading
Loading
Loading
+178 −28
Original line number Diff line number Diff line
@@ -913,34 +913,6 @@ func (c *config) ModulesLoadedByPrivilegedModules() []string {
	return c.productVariables.ModulesLoadedByPrivilegedModules
}

// Expected format for apexJarValue = <apex name>:<jar name>
func SplitApexJarPair(ctx PathContext, str string) (string, string) {
	pair := strings.SplitN(str, ":", 2)
	if len(pair) == 2 {
		return pair[0], pair[1]
	} else {
		reportPathErrorf(ctx, "malformed (apex, jar) pair: '%s', expected format: <apex>:<jar>", str)
		return "error-apex", "error-jar"
	}
}

func GetJarsFromApexJarPairs(ctx PathContext, apexJarPairs []string) []string {
	modules := make([]string, len(apexJarPairs))
	for i, p := range apexJarPairs {
		_, jar := SplitApexJarPair(ctx, p)
		modules[i] = jar
	}
	return modules
}

func (c *config) BootJars() []string {
	ctx := NullPathContext{Config{
		config: c,
	}}
	return append(GetJarsFromApexJarPairs(ctx, c.productVariables.BootJars),
		GetJarsFromApexJarPairs(ctx, c.productVariables.UpdatableBootJars)...)
}

func (c *config) DexpreoptGlobalConfig(ctx PathContext) ([]byte, error) {
	if c.productVariables.DexpreoptGlobalConfig == nil {
		return nil, nil
@@ -1275,3 +1247,181 @@ func (c *deviceConfig) BoardUsesRecoveryAsBoot() bool {
func (c *deviceConfig) BoardKernelBinaries() []string {
	return c.config.productVariables.BoardKernelBinaries
}

// The ConfiguredJarList struct provides methods for handling a list of (apex, jar) pairs.
// Such lists are used in the build system for things like bootclasspath jars or system server jars.
// The apex part is either an apex name, or a special names "platform" or "system_ext". Jar is a
// module name. The pairs come from Make product variables as a list of colon-separated strings.
//
// Examples:
//   - "com.android.art:core-oj"
//   - "platform:framework"
//   - "system_ext:foo"
//
type ConfiguredJarList struct {
	apexes []string // A list of apex components.
	jars   []string // A list of jar components.
}

// The length of the list.
func (l *ConfiguredJarList) Len() int {
	return len(l.jars)
}

// Apex component of idx-th pair on the list.
func (l *ConfiguredJarList) apex(idx int) string {
	return l.apexes[idx]
}

// Jar component of idx-th pair on the list.
func (l *ConfiguredJarList) Jar(idx int) string {
	return l.jars[idx]
}

// If the list contains a pair with the given jar.
func (l *ConfiguredJarList) ContainsJar(jar string) bool {
	return InList(jar, l.jars)
}

// If the list contains the given (apex, jar) pair.
func (l *ConfiguredJarList) containsApexJarPair(apex, jar string) bool {
	for i := 0; i < l.Len(); i++ {
		if apex == l.apex(i) && jar == l.Jar(i) {
			return true
		}
	}
	return false
}

// Index of the first pair with the given jar on the list, or -1 if none.
func (l *ConfiguredJarList) IndexOfJar(jar string) int {
	return IndexList(jar, l.jars)
}

// Append an (apex, jar) pair to the list.
func (l *ConfiguredJarList) Append(apex string, jar string) {
	l.apexes = append(l.apexes, apex)
	l.jars = append(l.jars, jar)
}

// Filter out sublist.
func (l *ConfiguredJarList) RemoveList(list ConfiguredJarList) {
	apexes := make([]string, 0, l.Len())
	jars := make([]string, 0, l.Len())

	for i, jar := range l.jars {
		apex := l.apex(i)
		if !list.containsApexJarPair(apex, jar) {
			apexes = append(apexes, apex)
			jars = append(jars, jar)
		}
	}

	l.apexes = apexes
	l.jars = jars
}

// A copy of itself.
func (l *ConfiguredJarList) CopyOf() ConfiguredJarList {
	return ConfiguredJarList{CopyOf(l.apexes), CopyOf(l.jars)}
}

// A copy of the list of strings containing jar components.
func (l *ConfiguredJarList) CopyOfJars() []string {
	return CopyOf(l.jars)
}

// A copy of the list of strings with colon-separated (apex, jar) pairs.
func (l *ConfiguredJarList) CopyOfApexJarPairs() []string {
	pairs := make([]string, 0, l.Len())

	for i, jar := range l.jars {
		apex := l.apex(i)
		pairs = append(pairs, apex+":"+jar)
	}

	return pairs
}

// A list of build paths based on the given directory prefix.
func (l *ConfiguredJarList) BuildPaths(ctx PathContext, dir OutputPath) WritablePaths {
	paths := make(WritablePaths, l.Len())
	for i, jar := range l.jars {
		paths[i] = dir.Join(ctx, ModuleStem(jar)+".jar")
	}
	return paths
}

func ModuleStem(module string) string {
	// b/139391334: the stem of framework-minus-apex is framework. This is hard coded here until we
	// find a good way to query the stem of a module before any other mutators are run.
	if module == "framework-minus-apex" {
		return "framework"
	}
	return module
}

// A list of on-device paths.
func (l *ConfiguredJarList) DevicePaths(cfg Config, ostype OsType) []string {
	paths := make([]string, l.Len())
	for i, jar := range l.jars {
		apex := l.apexes[i]
		name := ModuleStem(jar) + ".jar"

		var subdir string
		if apex == "platform" {
			subdir = "system/framework"
		} else if apex == "system_ext" {
			subdir = "system_ext/framework"
		} else {
			subdir = filepath.Join("apex", apex, "javalib")
		}

		if ostype.Class == Host {
			paths[i] = filepath.Join(cfg.Getenv("OUT_DIR"), "host", cfg.PrebuiltOS(), subdir, name)
		} else {
			paths[i] = filepath.Join("/", subdir, name)
		}
	}
	return paths
}

// Expected format for apexJarValue = <apex name>:<jar name>
func splitConfiguredJarPair(ctx PathContext, str string) (string, string) {
	pair := strings.SplitN(str, ":", 2)
	if len(pair) == 2 {
		return pair[0], pair[1]
	} else {
		reportPathErrorf(ctx, "malformed (apex, jar) pair: '%s', expected format: <apex>:<jar>", str)
		return "error-apex", "error-jar"
	}
}

func CreateConfiguredJarList(ctx PathContext, list []string) ConfiguredJarList {
	apexes := make([]string, 0, len(list))
	jars := make([]string, 0, len(list))

	l := ConfiguredJarList{apexes, jars}

	for _, apexjar := range list {
		apex, jar := splitConfiguredJarPair(ctx, apexjar)
		l.Append(apex, jar)
	}

	return l
}

func EmptyConfiguredJarList() ConfiguredJarList {
	return ConfiguredJarList{}
}

var earlyBootJarsKey = NewOnceKey("earlyBootJars")

func (c *config) BootJars() []string {
	return c.Once(earlyBootJarsKey, func() interface{} {
		ctx := NullPathContext{Config{c}}
		list := CreateConfiguredJarList(ctx,
			append(CopyOf(c.productVariables.BootJars), c.productVariables.UpdatableBootJars...))
		return list.CopyOfJars()
	}).([]string)
}
+13 −11
Original line number Diff line number Diff line
@@ -5602,13 +5602,15 @@ func TestUpdatable_should_set_min_sdk_version(t *testing.T) {
}

func TestNoUpdatableJarsInBootImage(t *testing.T) {

	var err string
	var transform func(*dexpreopt.GlobalConfig)

	config := android.TestArchConfig(buildDir, nil, "", nil)
	ctx := android.PathContextForTesting(config)

	t.Run("updatable jar from ART apex in the ART boot image => ok", func(t *testing.T) {
		transform = func(config *dexpreopt.GlobalConfig) {
			config.ArtApexJars = []string{"com.android.art.something:some-art-lib"}
			config.ArtApexJars = android.CreateConfiguredJarList(ctx, []string{"com.android.art.something:some-art-lib"})
		}
		testNoUpdatableJarsInBootImage(t, "", transform)
	})
@@ -5616,7 +5618,7 @@ func TestNoUpdatableJarsInBootImage(t *testing.T) {
	t.Run("updatable jar from ART apex in the framework boot image => error", func(t *testing.T) {
		err = "module 'some-art-lib' from updatable apex 'com.android.art.something' is not allowed in the framework boot image"
		transform = func(config *dexpreopt.GlobalConfig) {
			config.BootJars = []string{"com.android.art.something:some-art-lib"}
			config.BootJars = android.CreateConfiguredJarList(ctx, []string{"com.android.art.something:some-art-lib"})
		}
		testNoUpdatableJarsInBootImage(t, err, transform)
	})
@@ -5624,7 +5626,7 @@ func TestNoUpdatableJarsInBootImage(t *testing.T) {
	t.Run("updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
		err = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the ART boot image"
		transform = func(config *dexpreopt.GlobalConfig) {
			config.ArtApexJars = []string{"some-updatable-apex:some-updatable-apex-lib"}
			config.ArtApexJars = android.CreateConfiguredJarList(ctx, []string{"some-updatable-apex:some-updatable-apex-lib"})
		}
		testNoUpdatableJarsInBootImage(t, err, transform)
	})
@@ -5632,7 +5634,7 @@ func TestNoUpdatableJarsInBootImage(t *testing.T) {
	t.Run("non-updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
		err = "module 'some-non-updatable-apex-lib' is not allowed in the ART boot image"
		transform = func(config *dexpreopt.GlobalConfig) {
			config.ArtApexJars = []string{"some-non-updatable-apex:some-non-updatable-apex-lib"}
			config.ArtApexJars = android.CreateConfiguredJarList(ctx, []string{"some-non-updatable-apex:some-non-updatable-apex-lib"})
		}
		testNoUpdatableJarsInBootImage(t, err, transform)
	})
@@ -5640,14 +5642,14 @@ func TestNoUpdatableJarsInBootImage(t *testing.T) {
	t.Run("updatable jar from some other apex in the framework boot image => error", func(t *testing.T) {
		err = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the framework boot image"
		transform = func(config *dexpreopt.GlobalConfig) {
			config.BootJars = []string{"some-updatable-apex:some-updatable-apex-lib"}
			config.BootJars = android.CreateConfiguredJarList(ctx, []string{"some-updatable-apex:some-updatable-apex-lib"})
		}
		testNoUpdatableJarsInBootImage(t, err, transform)
	})

	t.Run("non-updatable jar from some other apex in the framework boot image => ok", func(t *testing.T) {
		transform = func(config *dexpreopt.GlobalConfig) {
			config.BootJars = []string{"some-non-updatable-apex:some-non-updatable-apex-lib"}
			config.BootJars = android.CreateConfiguredJarList(ctx, []string{"some-non-updatable-apex:some-non-updatable-apex-lib"})
		}
		testNoUpdatableJarsInBootImage(t, "", transform)
	})
@@ -5655,7 +5657,7 @@ func TestNoUpdatableJarsInBootImage(t *testing.T) {
	t.Run("nonexistent jar in the ART boot image => error", func(t *testing.T) {
		err = "failed to find a dex jar path for module 'nonexistent'"
		transform = func(config *dexpreopt.GlobalConfig) {
			config.ArtApexJars = []string{"platform:nonexistent"}
			config.ArtApexJars = android.CreateConfiguredJarList(ctx, []string{"platform:nonexistent"})
		}
		testNoUpdatableJarsInBootImage(t, err, transform)
	})
@@ -5663,7 +5665,7 @@ func TestNoUpdatableJarsInBootImage(t *testing.T) {
	t.Run("nonexistent jar in the framework boot image => error", func(t *testing.T) {
		err = "failed to find a dex jar path for module 'nonexistent'"
		transform = func(config *dexpreopt.GlobalConfig) {
			config.BootJars = []string{"platform:nonexistent"}
			config.BootJars = android.CreateConfiguredJarList(ctx, []string{"platform:nonexistent"})
		}
		testNoUpdatableJarsInBootImage(t, err, transform)
	})
@@ -5671,14 +5673,14 @@ func TestNoUpdatableJarsInBootImage(t *testing.T) {
	t.Run("platform jar in the ART boot image => error", func(t *testing.T) {
		err = "module 'some-platform-lib' is not allowed in the ART boot image"
		transform = func(config *dexpreopt.GlobalConfig) {
			config.ArtApexJars = []string{"platform:some-platform-lib"}
			config.ArtApexJars = android.CreateConfiguredJarList(ctx, []string{"platform:some-platform-lib"})
		}
		testNoUpdatableJarsInBootImage(t, err, transform)
	})

	t.Run("platform jar in the framework boot image => ok", func(t *testing.T) {
		transform = func(config *dexpreopt.GlobalConfig) {
			config.BootJars = []string{"platform:some-platform-lib"}
			config.BootJars = android.CreateConfiguredJarList(ctx, []string{"platform:some-platform-lib"})
		}
		testNoUpdatableJarsInBootImage(t, "", transform)
	})
+21 −13
Original line number Diff line number Diff line
@@ -40,14 +40,14 @@ type GlobalConfig struct {
	DisableGenerateProfile bool   // don't generate profiles
	ProfileDir             string // directory to find profiles in

	BootJars          []string // modules for jars that form the boot class path
	UpdatableBootJars []string // jars within apex that form the boot class path
	BootJars          android.ConfiguredJarList // modules for jars that form the boot class path
	UpdatableBootJars android.ConfiguredJarList // jars within apex that form the boot class path

	ArtApexJars []string // modules for jars that are in the ART APEX
	ArtApexJars android.ConfiguredJarList // modules for jars that are in the ART APEX

	SystemServerJars          []string                  // jars that form the system server
	SystemServerApps          []string                  // apps that are loaded into system server
	UpdatableSystemServerJars []string // jars within apex that are loaded into system server
	UpdatableSystemServerJars android.ConfiguredJarList // jars within apex that are loaded into system server
	SpeedApps                 []string                  // apps that should be speed optimized

	BrokenSuboptimalOrderOfSystemServerJars bool // if true, sub-optimal order does not cause a build error
@@ -189,6 +189,10 @@ func ParseGlobalConfig(ctx android.PathContext, data []byte) (*GlobalConfig, err

		// Copies of entries in GlobalConfig that are not constructable without extra parameters.  They will be
		// used to construct the real value manually below.
		BootJars                  []string
		UpdatableBootJars         []string
		ArtApexJars               []string
		UpdatableSystemServerJars []string
		DirtyImageObjects         string
		BootImageProfiles         []string
	}
@@ -200,6 +204,10 @@ func ParseGlobalConfig(ctx android.PathContext, data []byte) (*GlobalConfig, err
	}

	// Construct paths that require a PathContext.
	config.GlobalConfig.BootJars = android.CreateConfiguredJarList(ctx, config.BootJars)
	config.GlobalConfig.UpdatableBootJars = android.CreateConfiguredJarList(ctx, config.UpdatableBootJars)
	config.GlobalConfig.ArtApexJars = android.CreateConfiguredJarList(ctx, config.ArtApexJars)
	config.GlobalConfig.UpdatableSystemServerJars = android.CreateConfiguredJarList(ctx, config.UpdatableSystemServerJars)
	config.GlobalConfig.DirtyImageObjects = android.OptionalPathForPath(constructPath(ctx, config.DirtyImageObjects))
	config.GlobalConfig.BootImageProfiles = constructPaths(ctx, config.BootImageProfiles)

@@ -530,12 +538,12 @@ func GlobalConfigForTests(ctx android.PathContext) *GlobalConfig {
		PatternsOnSystemOther:              nil,
		DisableGenerateProfile:             false,
		ProfileDir:                         "",
		BootJars:                           nil,
		UpdatableBootJars:                  nil,
		ArtApexJars:                        nil,
		BootJars:                           android.EmptyConfiguredJarList(),
		UpdatableBootJars:                  android.EmptyConfiguredJarList(),
		ArtApexJars:                        android.EmptyConfiguredJarList(),
		SystemServerJars:                   nil,
		SystemServerApps:                   nil,
		UpdatableSystemServerJars:          nil,
		UpdatableSystemServerJars:          android.EmptyConfiguredJarList(),
		SpeedApps:                          nil,
		PreoptFlags:                        nil,
		DefaultCompilerFilter:              "",
+5 −14
Original line number Diff line number Diff line
@@ -83,7 +83,7 @@ func GenerateDexpreoptRule(ctx android.PathContext, globalSoong *GlobalSoongConf

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

@@ -104,17 +104,15 @@ func dexpreoptDisabled(ctx android.PathContext, global *GlobalConfig, module *Mo
	}

	// Don't preopt system server jars that are updatable.
	for _, p := range global.UpdatableSystemServerJars {
		if _, jar := android.SplitApexJarPair(ctx, p); jar == module.Name {
	if global.UpdatableSystemServerJars.ContainsJar(module.Name) {
		return true
	}
	}

	// If OnlyPreoptBootImageAndSystemServer=true and module is not in boot class path skip
	// Also preopt system server jars since selinux prevents system server from loading anything from
	// /data. If we don't do this they will need to be extracted which is not favorable for RAM usage
	// or performance. If PreoptExtractedApk is true, we ignore the only preopt boot image options.
	if global.OnlyPreoptBootImageAndSystemServer && !contains(android.GetJarsFromApexJarPairs(ctx, global.BootJars), module.Name) &&
	if global.OnlyPreoptBootImageAndSystemServer && !global.BootJars.ContainsJar(module.Name) &&
		!contains(global.SystemServerJars, module.Name) && !module.PreoptExtractedApk {
		return true
	}
@@ -571,20 +569,13 @@ func makefileMatch(pattern, s string) bool {
	}
}

// Expected format for apexJarValue = <apex name>:<jar name>
func GetJarLocationFromApexJarPair(ctx android.PathContext, apexJarValue string) string {
	apex, jar := android.SplitApexJarPair(ctx, apexJarValue)
	return filepath.Join("/apex", apex, "javalib", jar+".jar")
}

var nonUpdatableSystemServerJarsKey = android.NewOnceKey("nonUpdatableSystemServerJars")

// TODO: eliminate the superficial global config parameter by moving global config definition
// from java subpackage to dexpreopt.
func NonUpdatableSystemServerJars(ctx android.PathContext, global *GlobalConfig) []string {
	return ctx.Config().Once(nonUpdatableSystemServerJarsKey, func() interface{} {
		return android.RemoveListFromList(global.SystemServerJars,
			android.GetJarsFromApexJarPairs(ctx, global.UpdatableSystemServerJars))
		return android.RemoveListFromList(global.SystemServerJars, global.UpdatableSystemServerJars.CopyOfJars())
	}).([]string)
}

+11 −11
Original line number Diff line number Diff line
@@ -49,8 +49,8 @@ type bootImageConfig struct {
	// Subdirectory where the image files are installed.
	installSubdir string

	// The names of jars that constitute this image.
	modules []string
	// A list of (location, jar) pairs for the Java modules in this image.
	modules android.ConfiguredJarList

	// File paths to jars.
	dexPaths     android.WritablePaths // for this image
@@ -113,16 +113,16 @@ func (image bootImageConfig) moduleName(ctx android.PathContext, idx int) string
	// Dexpreopt on the boot class path produces multiple files. The first dex file
	// is converted into 'name'.art (to match the legacy assumption that 'name'.art
	// exists), and the rest are converted to 'name'-<jar>.art.
	_, m := android.SplitApexJarPair(ctx, image.modules[idx])
	m := image.modules.Jar(idx)
	name := image.stem
	if idx != 0 || image.extends != nil {
		name += "-" + stemOf(m)
		name += "-" + android.ModuleStem(m)
	}
	return name
}

func (image bootImageConfig) firstModuleNameOrStem(ctx android.PathContext) string {
	if len(image.modules) > 0 {
	if image.modules.Len() > 0 {
		return image.moduleName(ctx, 0)
	} else {
		return image.stem
@@ -130,8 +130,8 @@ func (image bootImageConfig) firstModuleNameOrStem(ctx android.PathContext) stri
}

func (image bootImageConfig) moduleFiles(ctx android.PathContext, dir android.OutputPath, exts ...string) android.OutputPaths {
	ret := make(android.OutputPaths, 0, len(image.modules)*len(exts))
	for i := range image.modules {
	ret := make(android.OutputPaths, 0, image.modules.Len()*len(exts))
	for i := 0; i < image.modules.Len(); i++ {
		name := image.moduleName(ctx, i)
		for _, ext := range exts {
			ret = append(ret, dir.Join(ctx, name+ext))
@@ -253,7 +253,7 @@ func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, modul
	}

	name := ctx.ModuleName(module)
	index := android.IndexList(name, android.GetJarsFromApexJarPairs(ctx, image.modules))
	index := image.modules.IndexOfJar(name)
	if index == -1 {
		return -1, nil
	}
@@ -295,7 +295,7 @@ func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, modul
func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootImageConfig {
	// Collect dex jar paths for the boot image modules.
	// This logic is tested in the apex package to avoid import cycle apex <-> java.
	bootDexJars := make(android.Paths, len(image.modules))
	bootDexJars := make(android.Paths, image.modules.Len())
	ctx.VisitAllModules(func(module android.Module) {
		if i, j := getBootImageJar(ctx, image, module); i != -1 {
			bootDexJars[i] = j
@@ -306,7 +306,7 @@ func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootI
	// Ensure all modules were converted to paths
	for i := range bootDexJars {
		if bootDexJars[i] == nil {
			_, m := android.SplitApexJarPair(ctx, image.modules[i])
			m := image.modules.Jar(i)
			if ctx.Config().AllowMissingDependencies() {
				missingDeps = append(missingDeps, m)
				bootDexJars[i] = android.PathForOutput(ctx, "missing")
@@ -608,7 +608,7 @@ func updatableBcpPackagesRule(ctx android.SingletonContext, image *bootImageConf

	return ctx.Config().Once(updatableBcpPackagesRuleKey, func() interface{} {
		global := dexpreopt.GetGlobalConfig(ctx)
		updatableModules := android.GetJarsFromApexJarPairs(ctx, global.UpdatableBootJars)
		updatableModules := global.UpdatableBootJars.CopyOfJars()

		// Collect `permitted_packages` for updatable boot jars.
		var updatablePackages []string
Loading