Merge "bp2build: add converted modules to codegen metrics, and remove the compat layer."
diff --git a/android/config.go b/android/config.go
index 3e41fbb..993aaa7 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1659,6 +1659,20 @@
return ConfiguredJarList{apexes, jars}
}
+// Append a list of (apex, jar) pairs to the list.
+func (l *ConfiguredJarList) AppendList(other ConfiguredJarList) ConfiguredJarList {
+ apexes := make([]string, 0, l.Len()+other.Len())
+ jars := make([]string, 0, l.Len()+other.Len())
+
+ apexes = append(apexes, l.apexes...)
+ jars = append(jars, l.jars...)
+
+ apexes = append(apexes, other.apexes...)
+ jars = append(jars, other.jars...)
+
+ return ConfiguredJarList{apexes, jars}
+}
+
// RemoveList filters out a list of (apex, jar) pairs from the receiving list of pairs.
func (l *ConfiguredJarList) RemoveList(list ConfiguredJarList) ConfiguredJarList {
apexes := make([]string, 0, l.Len())
diff --git a/android/deapexer.go b/android/deapexer.go
index de3f635..9290481 100644
--- a/android/deapexer.go
+++ b/android/deapexer.go
@@ -73,7 +73,7 @@
// exported file name is the apex relative path, e.g. javalib/core-libart.jar.
//
// See Prebuilt.ApexInfoMutator for more information.
- exports map[string]Path
+ exports map[string]WritablePath
}
// PrebuiltExportPath provides the path, or nil if not available, of a file exported from the
@@ -82,7 +82,7 @@
// The exported file is identified by the apex relative path, e.g. "javalib/core-libart.jar".
//
// See apex/deapexer.go for more information.
-func (i DeapexerInfo) PrebuiltExportPath(apexRelativePath string) Path {
+func (i DeapexerInfo) PrebuiltExportPath(apexRelativePath string) WritablePath {
path := i.exports[apexRelativePath]
return path
}
@@ -95,7 +95,7 @@
// for use with a prebuilt_apex module.
//
// See apex/deapexer.go for more information.
-func NewDeapexerInfo(exports map[string]Path) DeapexerInfo {
+func NewDeapexerInfo(exports map[string]WritablePath) DeapexerInfo {
return DeapexerInfo{
exports: exports,
}
diff --git a/android/testing.go b/android/testing.go
index bd2faa2..7a89fc4 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -491,6 +491,66 @@
ctx.preSingletons = append(ctx.preSingletons, newPreSingleton(name, factory))
}
+// ModuleVariantForTests selects a specific variant of the module with the given
+// name by matching the variations map against the variations of each module
+// variant. A module variant matches the map if every variation that exists in
+// both have the same value. Both the module and the map are allowed to have
+// extra variations that the other doesn't have. Panics if not exactly one
+// module variant matches.
+func (ctx *TestContext) ModuleVariantForTests(name string, matchVariations map[string]string) TestingModule {
+ modules := []Module{}
+ ctx.VisitAllModules(func(m blueprint.Module) {
+ if ctx.ModuleName(m) == name {
+ am := m.(Module)
+ amMut := am.base().commonProperties.DebugMutators
+ amVar := am.base().commonProperties.DebugVariations
+ matched := true
+ for i, mut := range amMut {
+ if wantedVar, found := matchVariations[mut]; found && amVar[i] != wantedVar {
+ matched = false
+ break
+ }
+ }
+ if matched {
+ modules = append(modules, am)
+ }
+ }
+ })
+
+ if len(modules) == 0 {
+ // Show all the modules or module variants that do exist.
+ var allModuleNames []string
+ var allVariants []string
+ ctx.VisitAllModules(func(m blueprint.Module) {
+ allModuleNames = append(allModuleNames, ctx.ModuleName(m))
+ if ctx.ModuleName(m) == name {
+ allVariants = append(allVariants, m.(Module).String())
+ }
+ })
+
+ if len(allVariants) == 0 {
+ panic(fmt.Errorf("failed to find module %q. All modules:\n %s",
+ name, strings.Join(SortedUniqueStrings(allModuleNames), "\n ")))
+ } else {
+ sort.Strings(allVariants)
+ panic(fmt.Errorf("failed to find module %q matching %v. All variants:\n %s",
+ name, matchVariations, strings.Join(allVariants, "\n ")))
+ }
+ }
+
+ if len(modules) > 1 {
+ moduleStrings := []string{}
+ for _, m := range modules {
+ moduleStrings = append(moduleStrings, m.String())
+ }
+ sort.Strings(moduleStrings)
+ panic(fmt.Errorf("module %q has more than one variant that match %v:\n %s",
+ name, matchVariations, strings.Join(moduleStrings, "\n ")))
+ }
+
+ return newTestingModule(ctx.config, modules[0])
+}
+
func (ctx *TestContext) ModuleForTests(name, variant string) TestingModule {
var module Module
ctx.VisitAllModules(func(m blueprint.Module) {
diff --git a/apex/apex.go b/apex/apex.go
index e3edc68..2d153e2 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1569,6 +1569,11 @@
af.jacocoReportClassesFile = module.JacocoReportClassesFile()
af.lintDepSets = module.LintDepSets()
af.customStem = module.Stem() + ".jar"
+ if dexpreopter, ok := module.(java.DexpreopterInterface); ok {
+ for _, install := range dexpreopter.DexpreoptBuiltInstalledForApex() {
+ af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName())
+ }
+ }
return af
}
diff --git a/apex/deapexer.go b/apex/deapexer.go
index c70da15..2c1835a 100644
--- a/apex/deapexer.go
+++ b/apex/deapexer.go
@@ -97,7 +97,7 @@
// Create and remember the directory into which the .apex file's contents will be unpacked.
deapexerOutput := android.PathForModuleOut(ctx, "deapexer")
- exports := make(map[string]android.Path)
+ exports := make(map[string]android.WritablePath)
// Create mappings from apex relative path to the extracted file's path.
exportedPaths := make(android.Paths, 0, len(exports))
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index c4794dc..4833a64 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -178,13 +178,19 @@
// If the exported java module provides a dex jar path then add it to the list of apexFiles.
path := child.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath()
if path != nil {
- p.apexFilesForAndroidMk = append(p.apexFilesForAndroidMk, apexFile{
+ af := apexFile{
module: child,
moduleDir: ctx.OtherModuleDir(child),
androidMkModuleName: name,
builtFile: path,
class: javaSharedLib,
- })
+ }
+ if module, ok := child.(java.DexpreopterInterface); ok {
+ for _, install := range module.DexpreoptBuiltInstalledForApex() {
+ af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName())
+ }
+ }
+ p.apexFilesForAndroidMk = append(p.apexFilesForAndroidMk, af)
}
} else if tag == exportedBootclasspathFragmentTag {
// Visit the children of the bootclasspath_fragment.
@@ -195,6 +201,14 @@
})
}
+func (p *prebuiltCommon) addRequiredModules(entries *android.AndroidMkEntries) {
+ for _, fi := range p.apexFilesForAndroidMk {
+ entries.AddStrings("LOCAL_REQUIRED_MODULES", fi.requiredModuleNames...)
+ entries.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", fi.targetRequiredModuleNames...)
+ entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", fi.hostRequiredModuleNames...)
+ }
+}
+
func (p *prebuiltCommon) AndroidMkEntries() []android.AndroidMkEntries {
entriesList := []android.AndroidMkEntries{
{
@@ -213,6 +227,7 @@
if len(postInstallCommands) > 0 {
entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(postInstallCommands, " && "))
}
+ p.addRequiredModules(entries)
},
},
},
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 131f0ec..d731f3e 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -25,6 +25,7 @@
// be a subset of OutputFiles. (or shared libraries, this will be equal to OutputFiles,
// but general cc_library will also have dynamic libraries in output files).
RootDynamicLibraries []string
+ TocFile string
}
type getOutputFilesRequestType struct{}
@@ -100,14 +101,15 @@
func (g getCcInfoType) StarlarkFunctionBody() string {
return `
outputFiles = [f.path for f in target.files.to_list()]
+cc_info = providers(target)["CcInfo"]
-includes = providers(target)["CcInfo"].compilation_context.includes.to_list()
-system_includes = providers(target)["CcInfo"].compilation_context.system_includes.to_list()
+includes = cc_info.compilation_context.includes.to_list()
+system_includes = cc_info.compilation_context.system_includes.to_list()
ccObjectFiles = []
staticLibraries = []
rootStaticArchives = []
-linker_inputs = providers(target)["CcInfo"].linking_context.linker_inputs.to_list()
+linker_inputs = cc_info.linking_context.linker_inputs.to_list()
for linker_input in linker_inputs:
for library in linker_input.libraries:
@@ -120,11 +122,17 @@
rootDynamicLibraries = []
-if "@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo" in providers(target):
- shared_info = providers(target)["@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo"]
+shared_info_tag = "@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo"
+if shared_info_tag in providers(target):
+ shared_info = providers(target)[shared_info_tag]
for lib in shared_info.linker_input.libraries:
rootDynamicLibraries += [lib.dynamic_library.path]
+toc_file = ""
+toc_file_tag = "//build/bazel/rules:generate_toc.bzl%CcTocInfo"
+if toc_file_tag in providers(target):
+ toc_file = providers(target)[toc_file_tag].toc.path
+
returns = [
outputFiles,
staticLibraries,
@@ -132,10 +140,10 @@
includes,
system_includes,
rootStaticArchives,
- rootDynamicLibraries
+ rootDynamicLibraries,
]
-return "|".join([", ".join(r) for r in returns])`
+return "|".join([", ".join(r) for r in returns] + [toc_file])`
}
// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
@@ -146,7 +154,7 @@
var ccObjects []string
splitString := strings.Split(rawString, "|")
- if expectedLen := 7; len(splitString) != expectedLen {
+ if expectedLen := 8; len(splitString) != expectedLen {
return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString)
}
outputFilesString := splitString[0]
@@ -159,6 +167,7 @@
systemIncludes := splitOrEmpty(splitString[4], ", ")
rootStaticArchives := splitOrEmpty(splitString[5], ", ")
rootDynamicLibraries := splitOrEmpty(splitString[6], ", ")
+ tocFile := splitString[7] // NOTE: Will be the empty string if there wasn't
return CcInfo{
OutputFiles: outputFiles,
CcObjectFiles: ccObjects,
@@ -167,6 +176,7 @@
SystemIncludes: systemIncludes,
RootStaticArchives: rootStaticArchives,
RootDynamicLibraries: rootDynamicLibraries,
+ TocFile: tocFile,
}, nil
}
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 49019ab..34d0832 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -71,7 +71,7 @@
}{
{
description: "no result",
- input: "||||||",
+ input: "|||||||",
expectedOutput: CcInfo{
OutputFiles: []string{},
CcObjectFiles: []string{},
@@ -80,11 +80,12 @@
SystemIncludes: []string{},
RootStaticArchives: []string{},
RootDynamicLibraries: []string{},
+ TocFile: "",
},
},
{
description: "only output",
- input: "test||||||",
+ input: "test|||||||",
expectedOutput: CcInfo{
OutputFiles: []string{"test"},
CcObjectFiles: []string{},
@@ -93,11 +94,12 @@
SystemIncludes: []string{},
RootStaticArchives: []string{},
RootDynamicLibraries: []string{},
+ TocFile: "",
},
},
{
description: "all items set",
- input: "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|rootstaticarchive1|rootdynamiclibrary1",
+ input: "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|rootstaticarchive1|rootdynamiclibrary1|lib.so.toc",
expectedOutput: CcInfo{
OutputFiles: []string{"out1", "out2"},
CcObjectFiles: []string{"object1", "object2"},
@@ -106,19 +108,20 @@
SystemIncludes: []string{"system/dir", "system/other/dir"},
RootStaticArchives: []string{"rootstaticarchive1"},
RootDynamicLibraries: []string{"rootdynamiclibrary1"},
+ TocFile: "lib.so.toc",
},
},
{
description: "too few result splits",
input: "|",
expectedOutput: CcInfo{},
- expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 7, []string{"", ""}),
+ expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 8, []string{"", ""}),
},
{
description: "too many result splits",
input: strings.Repeat("|", 8),
expectedOutput: CcInfo{},
- expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 7, make([]string, 9)),
+ expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 8, make([]string, 9)),
},
}
for _, tc := range testCases {
diff --git a/cc/builder.go b/cc/builder.go
index b07acf1..4b0a4b6 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -199,8 +199,18 @@
// Rule for invoking clang-tidy (a clang-based linter).
clangTidy, clangTidyRE = pctx.RemoteStaticRules("clangTidy",
blueprint.RuleParams{
- Command: "rm -f $out && $tidyVars $reTemplate${config.ClangBin}/clang-tidy $tidyFlags $in -- $cFlags && touch $out",
- CommandDeps: []string{"${config.ClangBin}/clang-tidy"},
+ Depfile: "${out}.d",
+ Deps: blueprint.DepsGCC,
+ // Pick bash because some machines with old /bin/sh cannot handle arrays.
+ // All $cFlags and $tidyFlags should have single quotes escaped.
+ // Assume no single quotes in other parameters like $in, $out, $ccCmd.
+ Command: "/bin/bash -c 'SRCF=$in; TIDYF=$out; CLANGFLAGS=($cFlags); " +
+ "rm -f $$TIDYF $${TIDYF}.d && " +
+ "${config.CcWrapper}$ccCmd \"$${CLANGFLAGS[@]}\" -E -o /dev/null $$SRCF " +
+ "-MQ $$TIDYF -MD -MF $${TIDYF}.d && " +
+ "$tidyVars $reTemplate${config.ClangBin}/clang-tidy $tidyFlags $$SRCF " +
+ "-- \"$${CLANGFLAGS[@]}\" && touch $$TIDYF'",
+ CommandDeps: []string{"${config.ClangBin}/clang-tidy", "$ccCmd"},
},
&remoteexec.REParams{
Labels: map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
@@ -214,7 +224,7 @@
// (1) New timestamps trigger clang and clang-tidy compilations again.
// (2) Changing source files caused concurrent clang or clang-tidy jobs to crash.
Platform: map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
- }, []string{"cFlags", "tidyFlags", "tidyVars"}, []string{})
+ }, []string{"ccCmd", "cFlags", "tidyFlags", "tidyVars"}, []string{})
_ = pctx.SourcePathVariable("yasmCmd", "prebuilts/misc/${config.HostPrebuiltTag}/yasm/yasm")
@@ -437,6 +447,12 @@
}
}
+func escapeSingleQuotes(s string) string {
+ // Replace single quotes to work when embedded in a single quoted string for bash.
+ // Relying on string concatenation of bash to get A'B from quoted 'A'\''B'.
+ return strings.Replace(s, `'`, `'\''`, -1)
+}
+
// Generate rules for compiling multiple .c, .cpp, or .S files to individual .o files
func transformSourceToObj(ctx android.ModuleContext, subdir string, srcFiles android.Paths,
flags builderFlags, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
@@ -672,14 +688,12 @@
Description: "clang-tidy " + srcFile.Rel(),
Output: tidyFile,
Input: srcFile,
- // We must depend on objFile, since clang-tidy doesn't
- // support exporting dependencies.
- Implicit: objFile,
- Implicits: cFlagsDeps,
- OrderOnly: pathDeps,
+ Implicits: cFlagsDeps,
+ OrderOnly: pathDeps,
Args: map[string]string{
- "cFlags": shareFlags("cFlags", moduleToolingFlags),
- "tidyFlags": shareFlags("tidyFlags", config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags)),
+ "ccCmd": ccCmd,
+ "cFlags": shareFlags("cFlags", escapeSingleQuotes(moduleToolingFlags)),
+ "tidyFlags": shareFlags("tidyFlags", escapeSingleQuotes(config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags))),
"tidyVars": tidyVars, // short and not shared
},
})
diff --git a/cc/library.go b/cc/library.go
index 8a663ff..703d57f 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -370,6 +370,7 @@
module, library := NewLibrary(android.HostAndDeviceSupported)
library.BuildOnlyShared()
module.sdkMemberTypes = []android.SdkMemberType{sharedLibrarySdkMemberType}
+ module.bazelHandler = &ccLibraryBazelHandler{module: module}
return module.Init()
}
@@ -603,7 +604,10 @@
handler.module.linker.(*libraryDecorator).unstrippedOutputFile = outputFilePath
- tocFile := getTocFile(ctx, label, ccInfo.OutputFiles)
+ var tocFile android.OptionalPath
+ if len(ccInfo.TocFile) > 0 {
+ tocFile = android.OptionalPathForPath(android.PathForBazelOut(ctx, ccInfo.TocFile))
+ }
handler.module.linker.(*libraryDecorator).tocFile = tocFile
ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
@@ -616,25 +620,6 @@
return true
}
-// getTocFile looks for the .so.toc file in the target's output files, if any. The .so.toc file
-// contains the table of contents of all symbols of a shared object.
-func getTocFile(ctx android.ModuleContext, label string, outputFiles []string) android.OptionalPath {
- var tocFile string
- for _, file := range outputFiles {
- if strings.HasSuffix(file, ".so.toc") {
- if tocFile != "" {
- ctx.ModuleErrorf("The %s target cannot produce more than 1 .toc file.", label)
- }
- tocFile = file
- // Don't break to validate that there are no multiple .toc files per .so.
- }
- }
- if tocFile == "" {
- return android.OptionalPath{}
- }
- return android.OptionalPathForPath(android.PathForBazelOut(ctx, tocFile))
-}
-
func (handler *ccLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
bazelCtx := ctx.Config().BazelContext
ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
diff --git a/cc/library_test.go b/cc/library_test.go
index 6b349b6..7ddfaa7 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -320,3 +320,48 @@
libfoo.Args["ldFlags"], "-Wl,--dynamic-list,foo.dynamic.txt")
}
+
+func TestCcLibrarySharedWithBazel(t *testing.T) {
+ bp := `
+cc_library_shared {
+ name: "foo",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo/bar:bar" },
+}`
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//foo/bar:bar": cquery.CcInfo{
+ CcObjectFiles: []string{"foo.o"},
+ Includes: []string{"include"},
+ SystemIncludes: []string{"system_include"},
+ RootDynamicLibraries: []string{"foo.so"},
+ TocFile: "foo.so.toc",
+ },
+ },
+ }
+ ctx := testCcWithConfig(t, config)
+
+ sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+ producer := sharedFoo.(android.OutputFileProducer)
+ outputFiles, err := producer.OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+ }
+ expectedOutputFiles := []string{"outputbase/execroot/__main__/foo.so"}
+ android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+
+ tocFilePath := sharedFoo.(*Module).Toc()
+ if !tocFilePath.Valid() {
+ t.Errorf("Invalid tocFilePath: %s", tocFilePath)
+ }
+ tocFile := tocFilePath.Path()
+ expectedToc := "outputbase/execroot/__main__/foo.so.toc"
+ android.AssertStringEquals(t, "toc file", expectedToc, tocFile.String())
+
+ entries := android.AndroidMkEntriesForTest(t, ctx, sharedFoo)[0]
+ expectedFlags := []string{"-Ioutputbase/execroot/__main__/include", "-isystem outputbase/execroot/__main__/system_include"}
+ gotFlags := entries.EntryMap["LOCAL_EXPORT_CFLAGS"]
+ android.AssertDeepEquals(t, "androidmk exported cflags", expectedFlags, gotFlags)
+}
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 1401c75..7733c1b 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -110,17 +110,12 @@
return true
}
- // Don't preopt system server jars that are updatable.
- if global.ApexSystemServerJars.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 && !global.BootJars.ContainsJar(module.Name) &&
- !global.SystemServerJars.ContainsJar(module.Name) && !module.PreoptExtractedApk {
+ !AllSystemServerJars(ctx, global).ContainsJar(module.Name) && !module.PreoptExtractedApk {
return true
}
@@ -201,6 +196,14 @@
return profilePath
}
+// Returns the dex location of a system server java library.
+func GetSystemServerDexLocation(global *GlobalConfig, lib string) string {
+ if apex := global.ApexSystemServerJars.ApexOfJar(lib); apex != "" {
+ return fmt.Sprintf("/apex/%s/javalib/%s.jar", apex, lib)
+ }
+ return fmt.Sprintf("/system/framework/%s.jar", lib)
+}
+
func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
module *ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath,
appImage bool, generateDM bool) {
@@ -216,6 +219,13 @@
}
toOdexPath := func(path string) string {
+ if global.ApexSystemServerJars.ContainsJar(module.Name) {
+ return filepath.Join(
+ "/system/framework/oat",
+ arch.String(),
+ strings.ReplaceAll(path[1:], "/", "@")+"@classes.odex")
+ }
+
return filepath.Join(
filepath.Dir(path),
"oat",
@@ -234,20 +244,21 @@
invocationPath := odexPath.ReplaceExtension(ctx, "invocation")
- systemServerJars := NonApexSystemServerJars(ctx, global)
+ systemServerJars := AllSystemServerJars(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 {
+ if jarIndex := systemServerJars.IndexOfJar(module.Name); 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.
var clcHost android.Paths
var clcTarget []string
- for _, lib := range systemServerJars[:jarIndex] {
+ for i := 0; i < jarIndex; i++ {
+ lib := systemServerJars.Jar(i)
clcHost = append(clcHost, SystemServerDexJarHostPath(ctx, lib))
- clcTarget = append(clcTarget, filepath.Join("/system/framework", lib+".jar"))
+ clcTarget = append(clcTarget, GetSystemServerDexLocation(global, lib))
}
// Copy the system server jar to a predefined location where dex2oat will find it.
@@ -362,7 +373,7 @@
if !android.PrefixInList(preoptFlags, "--compiler-filter=") {
var compilerFilter string
- if global.SystemServerJars.ContainsJar(module.Name) {
+ if systemServerJars.ContainsJar(module.Name) {
// Jars of system server, use the product option if it is set, speed otherwise.
if global.SystemServerCompilerFilter != "" {
compilerFilter = global.SystemServerCompilerFilter
@@ -416,7 +427,7 @@
// PRODUCT_SYSTEM_SERVER_DEBUG_INFO overrides WITH_DEXPREOPT_DEBUG_INFO.
// PRODUCT_OTHER_JAVA_DEBUG_INFO overrides WITH_DEXPREOPT_DEBUG_INFO.
- if global.SystemServerJars.ContainsJar(module.Name) {
+ if systemServerJars.ContainsJar(module.Name) {
if global.AlwaysSystemServerDebugInfo {
debugInfo = true
} else if global.NeverSystemServerDebugInfo {
@@ -518,14 +529,15 @@
}
}
-var nonApexSystemServerJarsKey = android.NewOnceKey("nonApexSystemServerJars")
+var allSystemServerJarsKey = android.NewOnceKey("allSystemServerJars")
// TODO: eliminate the superficial global config parameter by moving global config definition
// from java subpackage to dexpreopt.
-func NonApexSystemServerJars(ctx android.PathContext, global *GlobalConfig) []string {
- return ctx.Config().Once(nonApexSystemServerJarsKey, func() interface{} {
- return android.RemoveListFromList(global.SystemServerJars.CopyOfJars(), global.ApexSystemServerJars.CopyOfJars())
- }).([]string)
+func AllSystemServerJars(ctx android.PathContext, global *GlobalConfig) *android.ConfiguredJarList {
+ return ctx.Config().Once(allSystemServerJarsKey, func() interface{} {
+ allSystemServerJars := global.SystemServerJars.AppendList(global.ApexSystemServerJars)
+ return &allSystemServerJars
+ }).(*android.ConfiguredJarList)
}
// A predefined location for the system server dex jars. This is needed in order to generate
@@ -551,12 +563,12 @@
mctx, isModule := ctx.(android.ModuleContext)
if isModule {
config := GetGlobalConfig(ctx)
- jars := NonApexSystemServerJars(ctx, config)
+ jars := AllSystemServerJars(ctx, config)
mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
- depIndex := android.IndexList(dep.Name(), jars)
+ depIndex := jars.IndexOfJar(dep.Name())
if jarIndex < depIndex && !config.BrokenSuboptimalOrderOfSystemServerJars {
- jar := jars[jarIndex]
- dep := jars[depIndex]
+ jar := jars.Jar(jarIndex)
+ dep := jars.Jar(depIndex)
mctx.ModuleErrorf("non-optimal order of jars on the system server classpath:"+
" '%s' precedes its dependency '%s', so dexpreopt is unable to resolve any"+
" references from '%s' to '%s'.\n", jar, dep, jar, dep)
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 4ee61b6..798d776 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -33,17 +33,35 @@
}
func testModuleConfig(ctx android.PathContext, name, partition string) *ModuleConfig {
+ return createTestModuleConfig(
+ name,
+ fmt.Sprintf("/%s/app/test/%s.apk", partition, name),
+ android.PathForOutput(ctx, fmt.Sprintf("%s/%s.apk", name, name)),
+ android.PathForOutput(ctx, fmt.Sprintf("%s/dex/%s.jar", name, name)),
+ android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name)))
+}
+
+func testApexModuleConfig(ctx android.PathContext, name, apexName string) *ModuleConfig {
+ return createTestModuleConfig(
+ name,
+ fmt.Sprintf("/apex/%s/javalib/%s.jar", apexName, name),
+ android.PathForOutput(ctx, fmt.Sprintf("%s/dexpreopt/%s.jar", name, name)),
+ android.PathForOutput(ctx, fmt.Sprintf("%s/aligned/%s.jar", name, name)),
+ android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name)))
+}
+
+func createTestModuleConfig(name, dexLocation string, buildPath, dexPath, enforceUsesLibrariesStatusFile android.OutputPath) *ModuleConfig {
return &ModuleConfig{
Name: name,
- DexLocation: fmt.Sprintf("/%s/app/test/%s.apk", partition, name),
- BuildPath: android.PathForOutput(ctx, fmt.Sprintf("%s/%s.apk", name, name)),
- DexPath: android.PathForOutput(ctx, fmt.Sprintf("%s/dex/%s.jar", name, name)),
+ DexLocation: dexLocation,
+ BuildPath: buildPath,
+ DexPath: dexPath,
UncompressedDex: false,
HasApkLibraries: false,
PreoptFlags: nil,
ProfileClassListing: android.OptionalPath{},
ProfileIsTextListing: false,
- EnforceUsesLibrariesStatusFile: android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name)),
+ EnforceUsesLibrariesStatusFile: enforceUsesLibrariesStatusFile,
EnforceUsesLibraries: false,
ClassLoaderContexts: nil,
Archs: []android.ArchType{android.Arm},
@@ -140,6 +158,29 @@
}
+func TestDexPreoptApexSystemServerJars(t *testing.T) {
+ config := android.TestConfig("out", nil, "", nil)
+ ctx := android.BuilderContextForTesting(config)
+ globalSoong := globalSoongConfigForTests()
+ global := GlobalConfigForTests(ctx)
+ module := testApexModuleConfig(ctx, "service-A", "com.android.apex1")
+
+ global.ApexSystemServerJars = android.CreateTestConfiguredJarList(
+ []string{"com.android.apex1:service-A"})
+
+ rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ wantInstalls := android.RuleBuilderInstalls{
+ {android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.odex"), "/system/framework/oat/arm/apex@com.android.apex1@javalib@service-A.jar@classes.odex"},
+ {android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.vdex"), "/system/framework/oat/arm/apex@com.android.apex1@javalib@service-A.jar@classes.vdex"},
+ }
+
+ android.AssertStringEquals(t, "installs", wantInstalls.String(), rule.Installs().String())
+}
+
func TestDexPreoptProfile(t *testing.T) {
config := android.TestConfig("out", nil, "", nil)
ctx := android.BuilderContextForTesting(config)
diff --git a/java/androidmk.go b/java/androidmk.go
index 68ccd82..71370c9 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -61,7 +61,13 @@
var entriesList []android.AndroidMkEntries
if library.hideApexVariantFromMake {
- // For a java library built for an APEX we don't need Make module
+ // For a java library built for an APEX, we don't need a Make module for itself. Otherwise, it
+ // will conflict with the platform variant because they have the same module name in the
+ // makefile. However, we need to add its dexpreopt outputs as sub-modules, if it is preopted.
+ dexpreoptEntries := library.dexpreopter.AndroidMkEntriesForApex()
+ if len(dexpreoptEntries) > 0 {
+ entriesList = append(entriesList, dexpreoptEntries...)
+ }
entriesList = append(entriesList, android.AndroidMkEntries{Disabled: true})
} else if !library.ApexModuleBase.AvailableFor(android.AvailableToPlatform) {
// Platform variant. If not available for the platform, we don't need Make module.
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 0faae36..e9dc982 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -15,13 +15,46 @@
package java
import (
+ "path/filepath"
+ "strings"
+
"android/soong/android"
"android/soong/dexpreopt"
)
-type dexpreopterInterface interface {
+type DexpreopterInterface interface {
IsInstallable() bool // Structs that embed dexpreopter must implement this.
dexpreoptDisabled(ctx android.BaseModuleContext) bool
+ DexpreoptBuiltInstalledForApex() []dexpreopterInstall
+ AndroidMkEntriesForApex() []android.AndroidMkEntries
+}
+
+type dexpreopterInstall struct {
+ // A unique name to distinguish an output from others for the same java library module. Usually in
+ // the form of `<arch>-<encoded-path>.odex/vdex/art`.
+ name string
+
+ // The name of the input java module.
+ moduleName string
+
+ // The path to the dexpreopt output on host.
+ outputPathOnHost android.Path
+
+ // The directory on the device for the output to install to.
+ installDirOnDevice android.InstallPath
+
+ // The basename (the last segment of the path) for the output to install as.
+ installFileOnDevice string
+}
+
+// The full module name of the output in the makefile.
+func (install *dexpreopterInstall) FullModuleName() string {
+ return install.moduleName + install.SubModuleName()
+}
+
+// The sub-module name of the output in the makefile (the name excluding the java module name).
+func (install *dexpreopterInstall) SubModuleName() string {
+ return "-dexpreopt-" + install.name
}
type dexpreopter struct {
@@ -39,7 +72,9 @@
enforceUsesLibs bool
classLoaderContexts dexpreopt.ClassLoaderContextMap
- builtInstalled string
+ // See the `dexpreopt` function for details.
+ builtInstalled string
+ builtInstalledForApex []dexpreopterInstall
// The config is used for two purposes:
// - Passing dexpreopt information about libraries from Soong to Make. This is needed when
@@ -74,6 +109,17 @@
dexpreopt.DexpreoptRunningInSoong = true
}
+func isApexVariant(ctx android.BaseModuleContext) bool {
+ apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ return !apexInfo.IsForPlatform()
+}
+
+func moduleName(ctx android.BaseModuleContext) string {
+ // Remove the "prebuilt_" prefix if the module is from a prebuilt because the prefix is not
+ // expected by dexpreopter.
+ return android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName())
+}
+
func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool {
global := dexpreopt.GetGlobalConfig(ctx)
@@ -81,7 +127,7 @@
return true
}
- if inList(ctx.ModuleName(), global.DisablePreoptModules) {
+ if inList(moduleName(ctx), global.DisablePreoptModules) {
return true
}
@@ -93,7 +139,7 @@
return true
}
- if !ctx.Module().(dexpreopterInterface).IsInstallable() {
+ if !ctx.Module().(DexpreopterInterface).IsInstallable() {
return true
}
@@ -101,8 +147,20 @@
return true
}
- // Don't preopt APEX variant module
- if apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo); !apexInfo.IsForPlatform() {
+ if isApexVariant(ctx) {
+ // Don't preopt APEX variant module unless the module is an APEX system server jar and we are
+ // building the entire system image.
+ if !global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) || ctx.Config().UnbundledBuild() {
+ return true
+ }
+ } else {
+ // Don't preopt the platform variant of an APEX system server jar to avoid conflicts.
+ if global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) {
+ return true
+ }
+ }
+
+ if !android.IsModulePreferred(ctx.Module()) {
return true
}
@@ -112,17 +170,40 @@
}
func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) {
- if d, ok := ctx.Module().(dexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) {
+ if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) {
return
}
dexpreopt.RegisterToolDeps(ctx)
}
-func odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool {
- return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
+func (d *dexpreopter) odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool {
+ return dexpreopt.OdexOnSystemOtherByName(moduleName(ctx), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
+}
+
+// Returns the install path of the dex jar of a module.
+//
+// Do not rely on `ApexInfo.ApexVariationName` because it can be something like "apex1000", rather
+// than the `name` in the path `/apex/<name>` as suggested in its comment.
+//
+// This function is on a best-effort basis. It cannot handle the case where an APEX jar is not a
+// system server jar, which is fine because we currently only preopt system server jars for APEXes.
+func (d *dexpreopter) getInstallPath(
+ ctx android.ModuleContext, defaultInstallPath android.InstallPath) android.InstallPath {
+ global := dexpreopt.GetGlobalConfig(ctx)
+ if global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) {
+ dexLocation := dexpreopt.GetSystemServerDexLocation(global, moduleName(ctx))
+ return android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexLocation, "/"))
+ }
+ if !d.dexpreoptDisabled(ctx) && isApexVariant(ctx) &&
+ filepath.Base(defaultInstallPath.PartitionDir()) != "apex" {
+ ctx.ModuleErrorf("unable to get the install path of the dex jar for dexpreopt")
+ }
+ return defaultInstallPath
}
func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.WritablePath) {
+ global := dexpreopt.GetGlobalConfig(ctx)
+
// TODO(b/148690468): The check on d.installPath is to bail out in cases where
// the dexpreopter struct hasn't been fully initialized before we're called,
// e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively
@@ -133,7 +214,7 @@
dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
- providesUsesLib := ctx.ModuleName()
+ providesUsesLib := moduleName(ctx)
if ulib, ok := ctx.Module().(ProvidesUsesLib); ok {
name := ulib.ProvidesUsesLib()
if name != nil {
@@ -147,17 +228,15 @@
return
}
- global := dexpreopt.GetGlobalConfig(ctx)
-
- isSystemServerJar := global.SystemServerJars.ContainsJar(ctx.ModuleName())
+ isSystemServerJar := global.SystemServerJars.ContainsJar(moduleName(ctx)) ||
+ global.ApexSystemServerJars.ContainsJar(moduleName(ctx))
bootImage := defaultBootImageConfig(ctx)
if global.UseArtImage {
bootImage = artBootImageConfig(ctx)
}
- // System server jars are an exception: they are dexpreopted without updatable bootclasspath.
- dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp && !isSystemServerJar)
+ dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
targets := ctx.MultiTargets()
if len(targets) == 0 {
@@ -199,15 +278,15 @@
profileIsTextListing = true
} else if global.ProfileDir != "" {
profileClassListing = android.ExistentPathForSource(ctx,
- global.ProfileDir, ctx.ModuleName()+".prof")
+ global.ProfileDir, moduleName(ctx)+".prof")
}
}
// Full dexpreopt config, used to create dexpreopt build rules.
dexpreoptConfig := &dexpreopt.ModuleConfig{
- Name: ctx.ModuleName(),
+ Name: moduleName(ctx),
DexLocation: dexLocation,
- BuildPath: android.PathForModuleOut(ctx, "dexpreopt", ctx.ModuleName()+".jar").OutputPath,
+ BuildPath: android.PathForModuleOut(ctx, "dexpreopt", moduleName(ctx)+".jar").OutputPath,
DexPath: dexJarFile,
ManifestPath: android.OptionalPathForPath(d.manifestFile),
UncompressedDex: d.uncompressedDex,
@@ -256,5 +335,53 @@
dexpreoptRule.Build("dexpreopt", "dexpreopt")
- d.builtInstalled = dexpreoptRule.Installs().String()
+ if global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) {
+ // APEX variants of java libraries are hidden from Make, so their dexpreopt outputs need special
+ // handling. Currently, for APEX variants of java libraries, only those in the system server
+ // classpath are handled here. Preopting of boot classpath jars in the ART APEX are handled in
+ // java/dexpreopt_bootjars.go, and other APEX jars are not preopted.
+ for _, install := range dexpreoptRule.Installs() {
+ // Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT.
+ installDir := strings.TrimPrefix(filepath.Dir(install.To), "/")
+ installBase := filepath.Base(install.To)
+ arch := filepath.Base(installDir)
+ installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir)
+ // The installs will be handled by Make as sub-modules of the java library.
+ d.builtInstalledForApex = append(d.builtInstalledForApex, dexpreopterInstall{
+ name: arch + "-" + installBase,
+ moduleName: moduleName(ctx),
+ outputPathOnHost: install.From,
+ installDirOnDevice: installPath,
+ installFileOnDevice: installBase,
+ })
+ }
+ } else {
+ // The installs will be handled by Make as LOCAL_SOONG_BUILT_INSTALLED of the java library
+ // module.
+ d.builtInstalled = dexpreoptRule.Installs().String()
+ }
+}
+
+func (d *dexpreopter) DexpreoptBuiltInstalledForApex() []dexpreopterInstall {
+ return d.builtInstalledForApex
+}
+
+func (d *dexpreopter) AndroidMkEntriesForApex() []android.AndroidMkEntries {
+ var entries []android.AndroidMkEntries
+ for _, install := range d.builtInstalledForApex {
+ install := install
+ entries = append(entries, android.AndroidMkEntries{
+ Class: "ETC",
+ SubName: install.SubModuleName(),
+ OutputFile: android.OptionalPathForPath(install.outputPathOnHost),
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.ToMakePath().String())
+ entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice)
+ entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false")
+ },
+ },
+ })
+ }
+ return entries
}
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index 8dc7b79..1c1070a 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -17,6 +17,7 @@
import (
"fmt"
"runtime"
+ "strings"
"testing"
"android/soong/android"
@@ -24,11 +25,17 @@
"android/soong/dexpreopt"
)
+func init() {
+ RegisterFakeRuntimeApexMutator()
+}
+
func TestDexpreoptEnabled(t *testing.T) {
tests := []struct {
- name string
- bp string
- enabled bool
+ name string
+ bp string
+ moduleName string
+ apexVariant bool
+ enabled bool
}{
{
name: "app",
@@ -148,13 +155,81 @@
}`,
enabled: true,
},
+ {
+ name: "apex variant",
+ bp: `
+ java_library {
+ name: "foo",
+ installable: true,
+ srcs: ["a.java"],
+ apex_available: ["com.android.apex1"],
+ }`,
+ apexVariant: true,
+ enabled: false,
+ },
+ {
+ name: "apex variant of apex system server jar",
+ bp: `
+ java_library {
+ name: "service-foo",
+ installable: true,
+ srcs: ["a.java"],
+ apex_available: ["com.android.apex1"],
+ }`,
+ moduleName: "service-foo",
+ apexVariant: true,
+ enabled: true,
+ },
+ {
+ name: "apex variant of prebuilt apex system server jar",
+ bp: `
+ java_library {
+ name: "prebuilt_service-foo",
+ installable: true,
+ srcs: ["a.java"],
+ apex_available: ["com.android.apex1"],
+ }`,
+ moduleName: "prebuilt_service-foo",
+ apexVariant: true,
+ enabled: true,
+ },
+ {
+ name: "platform variant of apex system server jar",
+ bp: `
+ java_library {
+ name: "service-foo",
+ installable: true,
+ srcs: ["a.java"],
+ apex_available: ["com.android.apex1"],
+ }`,
+ moduleName: "service-foo",
+ apexVariant: false,
+ enabled: false,
+ },
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
- ctx, _ := testJava(t, test.bp)
+ preparers := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithFakeApexMutator,
+ dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"),
+ )
- dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeRule("dexpreopt")
+ result := preparers.RunTestWithBp(t, test.bp)
+ ctx := result.TestContext
+
+ moduleName := "foo"
+ if test.moduleName != "" {
+ moduleName = test.moduleName
+ }
+
+ variant := "android_common"
+ if test.apexVariant {
+ variant += "_apex1000"
+ }
+
+ dexpreopt := ctx.ModuleForTests(moduleName, variant).MaybeRule("dexpreopt")
enabled := dexpreopt.Rule != nil
if enabled != test.enabled {
@@ -220,3 +295,145 @@
testDex2oatToolDep(true, true, true, prebuiltDex2oatPath)
testDex2oatToolDep(false, true, false, prebuiltDex2oatPath)
}
+
+func TestDexpreoptBuiltInstalledForApex(t *testing.T) {
+ preparers := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithFakeApexMutator,
+ dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"),
+ )
+
+ // An APEX system server jar.
+ result := preparers.RunTestWithBp(t, `
+ java_library {
+ name: "service-foo",
+ installable: true,
+ srcs: ["a.java"],
+ apex_available: ["com.android.apex1"],
+ }`)
+ ctx := result.TestContext
+ module := ctx.ModuleForTests("service-foo", "android_common_apex1000")
+ library := module.Module().(*Library)
+
+ installs := library.dexpreopter.DexpreoptBuiltInstalledForApex()
+
+ android.AssertIntEquals(t, "install count", 2, len(installs))
+
+ android.AssertStringEquals(t, "installs[0] FullModuleName",
+ "service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
+ installs[0].FullModuleName())
+
+ android.AssertStringEquals(t, "installs[0] SubModuleName",
+ "-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
+ installs[0].SubModuleName())
+
+ android.AssertStringEquals(t, "installs[1] FullModuleName",
+ "service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
+ installs[1].FullModuleName())
+
+ android.AssertStringEquals(t, "installs[1] SubModuleName",
+ "-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
+ installs[1].SubModuleName())
+
+ // Not an APEX system server jar.
+ result = preparers.RunTestWithBp(t, `
+ java_library {
+ name: "foo",
+ installable: true,
+ srcs: ["a.java"],
+ }`)
+ ctx = result.TestContext
+ module = ctx.ModuleForTests("foo", "android_common")
+ library = module.Module().(*Library)
+
+ installs = library.dexpreopter.DexpreoptBuiltInstalledForApex()
+
+ android.AssertIntEquals(t, "install count", 0, len(installs))
+}
+
+func filterDexpreoptEntriesList(entriesList []android.AndroidMkEntries) []android.AndroidMkEntries {
+ var results []android.AndroidMkEntries
+ for _, entries := range entriesList {
+ if strings.Contains(entries.EntryMap["LOCAL_MODULE"][0], "-dexpreopt-") {
+ results = append(results, entries)
+ }
+ }
+ return results
+}
+
+func verifyEntries(t *testing.T, message string, expectedModule string,
+ expectedPrebuiltModuleFile string, expectedModulePath string, expectedInstalledModuleStem string,
+ entries android.AndroidMkEntries) {
+ android.AssertStringEquals(t, message+" LOCAL_MODULE", expectedModule,
+ entries.EntryMap["LOCAL_MODULE"][0])
+
+ android.AssertStringEquals(t, message+" LOCAL_MODULE_CLASS", "ETC",
+ entries.EntryMap["LOCAL_MODULE_CLASS"][0])
+
+ android.AssertStringDoesContain(t, message+" LOCAL_PREBUILT_MODULE_FILE",
+ entries.EntryMap["LOCAL_PREBUILT_MODULE_FILE"][0], expectedPrebuiltModuleFile)
+
+ android.AssertStringDoesContain(t, message+" LOCAL_MODULE_PATH",
+ entries.EntryMap["LOCAL_MODULE_PATH"][0], expectedModulePath)
+
+ android.AssertStringEquals(t, message+" LOCAL_INSTALLED_MODULE_STEM",
+ expectedInstalledModuleStem, entries.EntryMap["LOCAL_INSTALLED_MODULE_STEM"][0])
+
+ android.AssertStringEquals(t, message+" LOCAL_NOT_AVAILABLE_FOR_PLATFORM",
+ "false", entries.EntryMap["LOCAL_NOT_AVAILABLE_FOR_PLATFORM"][0])
+}
+
+func TestAndroidMkEntriesForApex(t *testing.T) {
+ preparers := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithFakeApexMutator,
+ dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"),
+ )
+
+ // An APEX system server jar.
+ result := preparers.RunTestWithBp(t, `
+ java_library {
+ name: "service-foo",
+ installable: true,
+ srcs: ["a.java"],
+ apex_available: ["com.android.apex1"],
+ }`)
+ ctx := result.TestContext
+ module := ctx.ModuleForTests("service-foo", "android_common_apex1000")
+
+ entriesList := android.AndroidMkEntriesForTest(t, ctx, module.Module())
+ entriesList = filterDexpreoptEntriesList(entriesList)
+
+ android.AssertIntEquals(t, "entries count", 2, len(entriesList))
+
+ verifyEntries(t,
+ "entriesList[0]",
+ "service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
+ "/dexpreopt/oat/arm64/javalib.odex",
+ "/system/framework/oat/arm64",
+ "apex@com.android.apex1@javalib@service-foo.jar@classes.odex",
+ entriesList[0])
+
+ verifyEntries(t,
+ "entriesList[1]",
+ "service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
+ "/dexpreopt/oat/arm64/javalib.vdex",
+ "/system/framework/oat/arm64",
+ "apex@com.android.apex1@javalib@service-foo.jar@classes.vdex",
+ entriesList[1])
+
+ // Not an APEX system server jar.
+ result = preparers.RunTestWithBp(t, `
+ java_library {
+ name: "foo",
+ installable: true,
+ srcs: ["a.java"],
+ }`)
+ ctx = result.TestContext
+ module = ctx.ModuleForTests("foo", "android_common")
+
+ entriesList = android.AndroidMkEntriesForTest(t, ctx, module.Module())
+ entriesList = filterDexpreoptEntriesList(entriesList)
+
+ android.AssertIntEquals(t, "entries count", 0, len(entriesList))
+}
diff --git a/java/java.go b/java/java.go
index 1a052b4..e2665ef 100644
--- a/java/java.go
+++ b/java/java.go
@@ -487,7 +487,7 @@
}
// Store uncompressed dex files that are preopted on /system.
- if !dexpreopter.dexpreoptDisabled(ctx) && (ctx.Host() || !odexOnSystemOther(ctx, dexpreopter.installPath)) {
+ if !dexpreopter.dexpreoptDisabled(ctx) && (ctx.Host() || !dexpreopter.odexOnSystemOther(ctx, dexpreopter.installPath)) {
return true
}
if ctx.Config().UncompressPrivAppDex() &&
@@ -508,7 +508,8 @@
}
j.checkSdkVersions(ctx)
- j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")
+ j.dexpreopter.installPath = j.dexpreopter.getInstallPath(
+ ctx, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar"))
j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary
if j.dexProperties.Uncompress_dex == nil {
// If the value was not force-set by the user, use reasonable default based on the module.
@@ -1368,7 +1369,8 @@
// Dex compilation
- j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", jarName)
+ j.dexpreopter.installPath = j.dexpreopter.getInstallPath(
+ ctx, android.PathForModuleInstall(ctx, "framework", jarName))
if j.dexProperties.Uncompress_dex == nil {
// If the value was not force-set by the user, use reasonable default based on the module.
j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
@@ -1509,7 +1511,7 @@
return Bool(j.properties.Installable)
}
-var _ dexpreopterInterface = (*Import)(nil)
+var _ DexpreopterInterface = (*Import)(nil)
// java_import imports one or more `.jar` files into the build graph as if they were built by a java_library module.
//
@@ -1622,7 +1624,8 @@
j.hideApexVariantFromMake = true
}
- j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")
+ j.dexpreopter.installPath = j.dexpreopter.getInstallPath(
+ ctx, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar"))
j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter)
inputJar := ctx.ExpandSource(j.properties.Jars[0], "jars")
diff --git a/java/sdk_library.go b/java/sdk_library.go
index ce8f179..1d39071 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1907,6 +1907,7 @@
android.SdkBase
hiddenAPI
+ dexpreopter
properties sdkLibraryImportProperties
@@ -2111,6 +2112,14 @@
}
}
+func (module *SdkLibraryImport) AndroidMkEntries() []android.AndroidMkEntries {
+ // For an SDK library imported from a prebuilt APEX, we don't need a Make module for itself, as we
+ // don't need to install it. However, we need to add its dexpreopt outputs as sub-modules, if it
+ // is preopted.
+ dexpreoptEntries := module.dexpreopter.AndroidMkEntriesForApex()
+ return append(dexpreoptEntries, android.AndroidMkEntries{Disabled: true})
+}
+
var _ android.ApexModule = (*SdkLibraryImport)(nil)
// Implements android.ApexModule
@@ -2208,8 +2217,16 @@
di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(module.BaseModuleName())); dexOutputPath != nil {
module.dexJarFile = dexOutputPath
- module.installFile = android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(module.BaseModuleName()))
+ installPath := android.PathForModuleInPartitionInstall(
+ ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(module.BaseModuleName()))
+ module.installFile = installPath
module.initHiddenAPI(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil)
+
+ // Dexpreopting.
+ module.dexpreopter.installPath = module.dexpreopter.getInstallPath(ctx, installPath)
+ module.dexpreopter.isSDKLibrary = true
+ module.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &module.dexpreopter)
+ module.dexpreopt(ctx, dexOutputPath)
} else {
// This should never happen as a variant for a prebuilt_apex is only created if the
// prebuilt_apex has been configured to export the java library dex file.
@@ -2328,6 +2345,11 @@
}
}
+// to satisfy java.DexpreopterInterface interface
+func (module *SdkLibraryImport) IsInstallable() bool {
+ return true
+}
+
var _ android.RequiredFilesFromPrebuiltApex = (*SdkLibraryImport)(nil)
func (module *SdkLibraryImport) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 938bb28..d6c0946 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -598,6 +598,7 @@
}
CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
+ `dex2oatd`,
`prebuilt_sdklib.stubs`,
`prebuilt_sdklib.stubs.source.test`,
`prebuilt_sdklib.stubs.system`,
@@ -674,7 +675,6 @@
`)
CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
- `dex2oatd`,
`prebuilt_sdklib`,
`sdklib.impl`,
`sdklib.stubs`,
@@ -683,6 +683,7 @@
})
CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{
+ `dex2oatd`,
`prebuilt_sdklib.stubs`,
`sdklib.impl`,
`sdklib.xml`,
diff --git a/java/testing.go b/java/testing.go
index 8860b45..d8a77cf 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -431,3 +431,45 @@
output := sourceGlobalCompatConfig.Output(allOutputs[0])
android.AssertPathsRelativeToTopEquals(t, message+": inputs", expectedPaths, output.Implicits)
}
+
+// Register the fake APEX mutator to `android.InitRegistrationContext` as if the real mutator exists
+// at runtime. This must be called in `init()` of a test if the test is going to use the fake APEX
+// mutator. Otherwise, we will be missing the runtime mutator because "soong-apex" is not a
+// dependency, which will cause an inconsistency between testing and runtime mutators.
+func RegisterFakeRuntimeApexMutator() {
+ registerFakeApexMutator(android.InitRegistrationContext)
+}
+
+var PrepareForTestWithFakeApexMutator = android.GroupFixturePreparers(
+ android.FixtureRegisterWithContext(registerFakeApexMutator),
+)
+
+func registerFakeApexMutator(ctx android.RegistrationContext) {
+ ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("apex", fakeApexMutator).Parallel()
+ })
+}
+
+type apexModuleBase interface {
+ ApexAvailable() []string
+}
+
+var _ apexModuleBase = (*Library)(nil)
+var _ apexModuleBase = (*SdkLibrary)(nil)
+
+// A fake APEX mutator that creates a platform variant and an APEX variant for modules with
+// `apex_available`. It helps us avoid a dependency on the real mutator defined in "soong-apex",
+// which will cause a cyclic dependency, and it provides an easy way to create an APEX variant for
+// testing without dealing with all the complexities in the real mutator.
+func fakeApexMutator(mctx android.BottomUpMutatorContext) {
+ switch mctx.Module().(type) {
+ case *Library, *SdkLibrary:
+ if len(mctx.Module().(apexModuleBase).ApexAvailable()) > 0 {
+ modules := mctx.CreateVariations("", "apex1000")
+ apexInfo := android.ApexInfo{
+ ApexVariationName: "apex1000",
+ }
+ mctx.SetVariationProvider(modules[1], android.ApexInfoProvider, apexInfo)
+ }
+ }
+}
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 63a8f04..47ca3a7 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -28,6 +28,7 @@
"system/librustutils",
"system/logging/liblog",
"system/logging/rust",
+ "system/nfc",
"system/security",
"system/tools/aidl",
"tools/security/fuzzing/example_rust_fuzzer",