diff options
Diffstat (limited to 'java')
| -rw-r--r-- | java/Android.bp | 1 | ||||
| -rw-r--r-- | java/aapt2.go | 40 | ||||
| -rw-r--r-- | java/aar.go | 35 | ||||
| -rw-r--r-- | java/android_manifest.go | 4 | ||||
| -rw-r--r-- | java/androidmk.go | 15 | ||||
| -rw-r--r-- | java/androidmk_test.go | 177 | ||||
| -rwxr-xr-x | java/app.go | 47 | ||||
| -rw-r--r-- | java/app_test.go | 42 | ||||
| -rw-r--r-- | java/boot_jars.go | 6 | ||||
| -rw-r--r-- | java/builder.go | 9 | ||||
| -rw-r--r-- | java/device_host_converter.go | 6 | ||||
| -rw-r--r-- | java/dexpreopt.go | 2 | ||||
| -rw-r--r-- | java/dexpreopt_bootjars.go | 359 | ||||
| -rw-r--r-- | java/droiddoc.go | 85 | ||||
| -rw-r--r-- | java/gen.go | 4 | ||||
| -rw-r--r-- | java/hiddenapi.go | 6 | ||||
| -rw-r--r-- | java/hiddenapi_singleton.go | 35 | ||||
| -rw-r--r-- | java/java.go | 235 | ||||
| -rw-r--r-- | java/java_test.go | 232 | ||||
| -rw-r--r-- | java/kotlin.go | 4 | ||||
| -rw-r--r-- | java/kotlin_test.go | 158 | ||||
| -rw-r--r-- | java/lint.go | 18 | ||||
| -rw-r--r-- | java/platform_compat_config.go | 12 | ||||
| -rw-r--r-- | java/proto.go | 8 | ||||
| -rw-r--r-- | java/robolectric.go | 14 | ||||
| -rw-r--r-- | java/sdk.go | 12 | ||||
| -rw-r--r-- | java/sdk_library.go | 72 | ||||
| -rw-r--r-- | java/sdk_library_external.go | 109 | ||||
| -rw-r--r-- | java/sysprop.go | 4 |
29 files changed, 1176 insertions, 575 deletions
diff --git a/java/Android.bp b/java/Android.bp index 9e8dc786b..39502b3e1 100644 --- a/java/Android.bp +++ b/java/Android.bp @@ -48,6 +48,7 @@ bootstrap_go_package { "robolectric.go", "sdk.go", "sdk_library.go", + "sdk_library_external.go", "support_libraries.go", "sysprop.go", "system_modules.go", diff --git a/java/aapt2.go b/java/aapt2.go index 04e4de52c..5346ddf89 100644 --- a/java/aapt2.go +++ b/java/aapt2.go @@ -25,12 +25,9 @@ import ( "android/soong/android" ) -const AAPT2_SHARD_SIZE = 100 - // Convert input resource file path to output file path. // values-[config]/<file>.xml -> values-[config]_<file>.arsc.flat; -// For other resource file, just replace the last "/" with "_" and -// add .flat extension. +// For other resource file, just replace the last "/" with "_" and add .flat extension. func pathToAapt2Path(ctx android.ModuleContext, res android.Path) android.WritablePath { name := res.Base() @@ -43,6 +40,7 @@ func pathToAapt2Path(ctx android.ModuleContext, res android.Path) android.Writab return android.PathForModuleOut(ctx, "aapt2", subDir, name) } +// pathsToAapt2Paths Calls pathToAapt2Path on each entry of the given Paths, i.e. []Path. func pathsToAapt2Paths(ctx android.ModuleContext, resPaths android.Paths) android.WritablePaths { outPaths := make(android.WritablePaths, len(resPaths)) @@ -53,6 +51,9 @@ func pathsToAapt2Paths(ctx android.ModuleContext, resPaths android.Paths) androi return outPaths } +// Shard resource files for efficiency. See aapt2Compile for details. +const AAPT2_SHARD_SIZE = 100 + var aapt2CompileRule = pctx.AndroidStaticRule("aapt2Compile", blueprint.RuleParams{ Command: `${config.Aapt2Cmd} compile -o $outDir $cFlags $in`, @@ -60,14 +61,26 @@ var aapt2CompileRule = pctx.AndroidStaticRule("aapt2Compile", }, "outDir", "cFlags") +// aapt2Compile compiles resources and puts the results in the requested directory. func aapt2Compile(ctx android.ModuleContext, dir android.Path, paths android.Paths, flags []string) android.WritablePaths { + // Shard the input paths so that they can be processed in parallel. If we shard them into too + // small chunks, the additional cost of spinning up aapt2 outweighs the performance gain. The + // current shard size, 100, seems to be a good balance between the added cost and the gain. + // The aapt2 compile actions are trivially short, but each action in ninja takes on the order of + // ~10 ms to run. frameworks/base/core/res/res has >10k resource files, so compiling each one + // with an individual action could take 100 CPU seconds. Sharding them reduces the overhead of + // starting actions by a factor of 100, at the expense of recompiling more files when one + // changes. Since the individual compiles are trivial it's a good tradeoff. shards := android.ShardPaths(paths, AAPT2_SHARD_SIZE) ret := make(android.WritablePaths, 0, len(paths)) for i, shard := range shards { + // This should be kept in sync with pathToAapt2Path. The aapt2 compile command takes an + // output directory path, but not output file paths. So, outPaths is just where we expect + // the output files will be located. outPaths := pathsToAapt2Paths(ctx, shard) ret = append(ret, outPaths...) @@ -82,6 +95,12 @@ func aapt2Compile(ctx android.ModuleContext, dir android.Path, paths android.Pat Inputs: shard, Outputs: outPaths, Args: map[string]string{ + // The aapt2 compile command takes an output directory path, but not output file paths. + // outPaths specified above is only used for dependency management purposes. In order for + // the outPaths values to match the actual outputs from aapt2, the dir parameter value + // must be a common prefix path of the paths values, and the top-level path segment used + // below, "aapt2", must always be kept in sync with the one in pathToAapt2Path. + // TODO(b/174505750): Make this easier and robust to use. "outDir": android.PathForModuleOut(ctx, "aapt2", dir.String()).String(), "cFlags": strings.Join(flags, " "), }, @@ -104,6 +123,8 @@ var aapt2CompileZipRule = pctx.AndroidStaticRule("aapt2CompileZip", }, }, "cFlags", "resZipDir", "zipSyncFlags") +// Unzips the given compressed file and compiles the resource source files in it. The zipPrefix +// parameter points to the subdirectory in the zip file where the resource files are located. func aapt2CompileZip(ctx android.ModuleContext, flata android.WritablePath, zip android.Path, zipPrefix string, flags []string) { @@ -163,6 +184,7 @@ func aapt2Link(ctx android.ModuleContext, var inFlags []string if len(compiledRes) > 0 { + // Create a file that contains the list of all compiled resource file paths. resFileList := android.PathForModuleOut(ctx, "aapt2", "res.list") // Write out file lists to files ctx.Build(pctx, android.BuildParams{ @@ -174,10 +196,12 @@ func aapt2Link(ctx android.ModuleContext, deps = append(deps, compiledRes...) deps = append(deps, resFileList) + // aapt2 filepath arguments that start with "@" mean file-list files. inFlags = append(inFlags, "@"+resFileList.String()) } if len(compiledOverlay) > 0 { + // Compiled overlay files are processed the same way as compiled resources. overlayFileList := android.PathForModuleOut(ctx, "aapt2", "overlay.list") ctx.Build(pctx, android.BuildParams{ Rule: fileListToFileRule, @@ -188,9 +212,11 @@ func aapt2Link(ctx android.ModuleContext, deps = append(deps, compiledOverlay...) deps = append(deps, overlayFileList) + // Compiled overlay files are passed over to aapt2 using -R option. inFlags = append(inFlags, "-R", "@"+overlayFileList.String()) } + // Set auxiliary outputs as implicit outputs to establish correct dependency chains. implicitOutputs := append(splitPackages, proguardOptions, genJar, rTxt, extraPackages) linkOutput := packageRes @@ -212,6 +238,10 @@ func aapt2Link(ctx android.ModuleContext, Implicits: deps, Output: linkOutput, ImplicitOutputs: implicitOutputs, + // Note the absence of splitPackages. The caller is supposed to compose and provide --split flag + // values via the flags parameter when it wants to split outputs. + // TODO(b/174509108): Perhaps we can process it in this func while keeping the code reasonably + // tidy. Args: map[string]string{ "flags": strings.Join(flags, " "), "inFlags": strings.Join(inFlags, " "), @@ -230,6 +260,8 @@ var aapt2ConvertRule = pctx.AndroidStaticRule("aapt2Convert", CommandDeps: []string{"${config.Aapt2Cmd}"}, }) +// Converts xml files and resource tables (resources.arsc) in the given jar/apk file to a proto +// format. The proto definition is available at frameworks/base/tools/aapt2/Resources.proto. func aapt2Convert(ctx android.ModuleContext, out android.WritablePath, in android.Path) { ctx.Build(pctx, android.BuildParams{ Rule: aapt2ConvertRule, diff --git a/java/aar.go b/java/aar.go index 7c3840bb1..3b6b34e27 100644 --- a/java/aar.go +++ b/java/aar.go @@ -259,16 +259,16 @@ var extractAssetsRule = pctx.AndroidStaticRule("extractAssets", }) func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext sdkContext, - sdkLibraries dexpreopt.ClassLoaderContextMap, extraLinkFlags ...string) { + classLoaderContexts dexpreopt.ClassLoaderContextMap, extraLinkFlags ...string) { transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assetPackages, libDeps, libFlags := - aaptLibs(ctx, sdkContext, sdkLibraries) + aaptLibs(ctx, sdkContext, classLoaderContexts) // App manifest file manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml") manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile) - manifestPath := manifestFixer(ctx, manifestSrcPath, sdkContext, sdkLibraries, + manifestPath := manifestFixer(ctx, manifestSrcPath, sdkContext, classLoaderContexts, a.isLibrary, a.useEmbeddedNativeLibs, a.usesNonSdkApis, a.useEmbeddedDex, a.hasNoCode, a.LoggingParent) @@ -389,15 +389,15 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext sdkContext, } // aaptLibs collects libraries from dependencies and sdk_version and converts them into paths -func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext, sdkLibraries dexpreopt.ClassLoaderContextMap) ( +func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext, classLoaderContexts dexpreopt.ClassLoaderContextMap) ( transitiveStaticLibs, transitiveStaticLibManifests android.Paths, staticRRODirs []rroDir, assets, deps android.Paths, flags []string) { var sharedLibs android.Paths - if sdkLibraries == nil { + if classLoaderContexts == nil { // Not all callers need to compute class loader context, those who don't just pass nil. // Create a temporary class loader context here (it will be computed, but not used). - sdkLibraries = make(dexpreopt.ClassLoaderContextMap) + classLoaderContexts = make(dexpreopt.ClassLoaderContextMap) } sdkDep := decodeSdkDep(ctx, sdkContext) @@ -407,6 +407,7 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext, sdkLibraries dex ctx.VisitDirectDeps(func(module android.Module) { depName := ctx.OtherModuleName(module) + depTag := ctx.OtherModuleDependencyTag(module) var exportPackage android.Path aarDep, _ := module.(AndroidLibraryDependency) @@ -414,7 +415,7 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext, sdkLibraries dex exportPackage = aarDep.ExportPackage() } - switch ctx.OtherModuleDependencyTag(module) { + switch depTag { case instrumentationForTag: // Nothing, instrumentationForTag is treated as libTag for javac but not for aapt2. case libTag: @@ -426,7 +427,7 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext, sdkLibraries dex // (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.MaybeAddContext(ctx, component.OptionalImplicitSdkLibrary(), + classLoaderContexts.MaybeAddContext(ctx, component.OptionalImplicitSdkLibrary(), component.DexJarBuildPath(), component.DexJarInstallPath()) } @@ -439,7 +440,6 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext, sdkLibraries dex transitiveStaticLibs = append(transitiveStaticLibs, aarDep.ExportedStaticPackages()...) transitiveStaticLibs = append(transitiveStaticLibs, exportPackage) transitiveStaticLibManifests = append(transitiveStaticLibManifests, aarDep.ExportedManifests()...) - sdkLibraries.AddContextMap(aarDep.ExportedSdkLibs(), depName) if aarDep.ExportedAssets().Valid() { assets = append(assets, aarDep.ExportedAssets().Path()) } @@ -458,11 +458,8 @@ func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext, sdkLibraries dex } } - // Add nested dependencies after processing the direct dependency: if it is a <uses-library>, - // nested context is added as its subcontext, and should not be re-added at the top-level. - if dep, ok := module.(Dependency); ok { - sdkLibraries.AddContextMap(dep.ExportedSdkLibs(), depName) - } + // Merge dep's CLC after processing the dep itself (which may add its own <uses-library>). + maybeAddCLCFromDep(module, depTag, depName, classLoaderContexts) }) deps = append(deps, sharedLibs...) @@ -514,8 +511,8 @@ func (a *AndroidLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.aapt.isLibrary = true - a.exportedSdkLibs = make(dexpreopt.ClassLoaderContextMap) - a.aapt.buildActions(ctx, sdkContext(a), a.exportedSdkLibs) + a.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap) + a.aapt.buildActions(ctx, sdkContext(a), a.classLoaderContexts) a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform() @@ -832,12 +829,12 @@ func (a *AARImport) AidlIncludeDirs() android.Paths { return nil } -func (a *AARImport) ExportedSdkLibs() dexpreopt.ClassLoaderContextMap { +func (a *AARImport) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap { return nil } -func (d *AARImport) ExportedPlugins() (android.Paths, []string) { - return nil, nil +func (d *AARImport) ExportedPlugins() (android.Paths, []string, bool) { + return nil, nil, false } func (a *AARImport) SrcJarArgs() ([]string, android.Paths) { diff --git a/java/android_manifest.go b/java/android_manifest.go index 6b39c3584..c76bb2fda 100644 --- a/java/android_manifest.go +++ b/java/android_manifest.go @@ -44,7 +44,7 @@ var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger", // Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext, - sdkLibraries dexpreopt.ClassLoaderContextMap, isLibrary, useEmbeddedNativeLibs, usesNonSdkApis, + classLoaderContexts dexpreopt.ClassLoaderContextMap, isLibrary, useEmbeddedNativeLibs, usesNonSdkApis, useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path { var args []string @@ -71,7 +71,7 @@ func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext args = append(args, "--use-embedded-dex") } - for _, usesLib := range sdkLibraries.UsesLibs() { + for _, usesLib := range classLoaderContexts.UsesLibs() { if inList(usesLib, dexpreopt.OptionalCompatUsesLibs) { args = append(args, "--optional-uses-library", usesLib) } else { diff --git a/java/androidmk.go b/java/androidmk.go index c6062457f..fc573c8b6 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -85,7 +85,6 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { } else { entriesList = append(entriesList, android.AndroidMkEntries{ Class: "JAVA_LIBRARIES", - DistFiles: library.distFiles, OutputFile: android.OptionalPathForPath(library.outputFile), Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk", ExtraEntries: []android.AndroidMkExtraEntriesFunc{ @@ -115,7 +114,7 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { entries.SetPath("LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR", library.jacocoReportClassesFile) } - entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", library.exportedSdkLibs.UsesLibs()...) + entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", library.classLoaderContexts.UsesLibs()...) if len(library.additionalCheckedModules) != 0 { entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", library.additionalCheckedModules.Strings()...) @@ -140,9 +139,9 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { func testSuiteComponent(entries *android.AndroidMkEntries, test_suites []string) { entries.SetString("LOCAL_MODULE_TAGS", "tests") if len(test_suites) > 0 { - entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", test_suites...) + entries.AddCompatibilityTestSuites(test_suites...) } else { - entries.SetString("LOCAL_COMPATIBILITY_SUITE", "null-suite") + entries.AddCompatibilityTestSuites("null-suite") } } @@ -160,6 +159,9 @@ func (j *Test) AndroidMkEntries() []android.AndroidMkEntries { entries.SetString("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", "true") } entries.AddStrings("LOCAL_TEST_MAINLINE_MODULES", j.testProperties.Test_mainline_modules...) + if Bool(j.testProperties.Test_options.Unit_test) { + entries.SetBool("LOCAL_IS_UNIT_TEST", true) + } }) return entriesList @@ -521,17 +523,12 @@ func (dstubs *Droidstubs) AndroidMkEntries() []android.AndroidMkEntries { // Note that dstubs.apiFile can be also be nil if WITHOUT_CHECKS_API is true. // TODO(b/146727827): Revert when we do not need to generate stubs and API separately. - var distFiles android.TaggedDistFiles - if dstubs.apiFile != nil { - distFiles = android.MakeDefaultDistFiles(dstubs.apiFile) - } outputFile := android.OptionalPathForPath(dstubs.stubsSrcJar) if !outputFile.Valid() { outputFile = android.OptionalPathForPath(dstubs.apiFile) } return []android.AndroidMkEntries{android.AndroidMkEntries{ Class: "JAVA_LIBRARIES", - DistFiles: distFiles, OutputFile: outputFile, Include: "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk", ExtraEntries: []android.AndroidMkExtraEntriesFunc{ diff --git a/java/androidmk_test.go b/java/androidmk_test.go index 075b7aa6f..233e9d5c5 100644 --- a/java/androidmk_test.go +++ b/java/androidmk_test.go @@ -16,7 +16,6 @@ package java import ( "reflect" - "strings" "testing" "android/soong/android" @@ -135,182 +134,6 @@ func TestHostdexSpecificRequired(t *testing.T) { } } -func TestDistWithTag(t *testing.T) { - ctx, config := testJava(t, ` - java_library { - name: "foo_without_tag", - srcs: ["a.java"], - compile_dex: true, - dist: { - targets: ["hi"], - }, - } - java_library { - name: "foo_with_tag", - srcs: ["a.java"], - compile_dex: true, - dist: { - targets: ["hi"], - tag: ".jar", - }, - } - `) - - withoutTagEntries := android.AndroidMkEntriesForTest(t, config, "", ctx.ModuleForTests("foo_without_tag", "android_common").Module()) - withTagEntries := android.AndroidMkEntriesForTest(t, config, "", ctx.ModuleForTests("foo_with_tag", "android_common").Module()) - - if len(withoutTagEntries) != 2 || len(withTagEntries) != 2 { - t.Errorf("two mk entries per module expected, got %d and %d", len(withoutTagEntries), len(withTagEntries)) - } - if len(withTagEntries[0].DistFiles[".jar"]) != 1 || - !strings.Contains(withTagEntries[0].DistFiles[".jar"][0].String(), "/javac/foo_with_tag.jar") { - t.Errorf("expected DistFiles to contain classes.jar, got %v", withTagEntries[0].DistFiles) - } - if len(withoutTagEntries[0].DistFiles[".jar"]) > 0 { - t.Errorf("did not expect explicit DistFile for .jar tag, got %v", withoutTagEntries[0].DistFiles[".jar"]) - } -} - -func TestDistWithDest(t *testing.T) { - ctx, config := testJava(t, ` - java_library { - name: "foo", - srcs: ["a.java"], - compile_dex: true, - dist: { - targets: ["my_goal"], - dest: "my/custom/dest/dir", - }, - } - `) - - module := ctx.ModuleForTests("foo", "android_common").Module() - entries := android.AndroidMkEntriesForTest(t, config, "", module) - if len(entries) != 2 { - t.Errorf("Expected 2 AndroidMk entries, got %d", len(entries)) - } - - distStrings := entries[0].GetDistForGoals(module) - - if len(distStrings) != 2 { - t.Errorf("Expected 2 entries for dist: PHONY and dist-for-goals, but got %q", distStrings) - } - - if distStrings[0] != ".PHONY: my_goal\n" { - t.Errorf("Expected .PHONY entry to declare my_goal, but got: %s", distStrings[0]) - } - - if !strings.Contains(distStrings[1], "$(call dist-for-goals,my_goal") || - !strings.Contains(distStrings[1], ".intermediates/foo/android_common/dex/foo.jar:my/custom/dest/dir") { - t.Errorf( - "Expected dist-for-goals entry to contain my_goal and new dest dir, but got: %s", distStrings[1]) - } -} - -func TestDistsWithAllProperties(t *testing.T) { - ctx, config := testJava(t, ` - java_library { - name: "foo", - srcs: ["a.java"], - compile_dex: true, - dist: { - targets: ["baz"], - }, - dists: [ - { - targets: ["bar"], - tag: ".jar", - dest: "bar.jar", - dir: "bar/dir", - suffix: ".qux", - }, - ] - } - `) - - module := ctx.ModuleForTests("foo", "android_common").Module() - entries := android.AndroidMkEntriesForTest(t, config, "", module) - if len(entries) != 2 { - t.Errorf("Expected 2 AndroidMk entries, got %d", len(entries)) - } - - distStrings := entries[0].GetDistForGoals(module) - - if len(distStrings) != 4 { - t.Errorf("Expected 4 entries for dist: PHONY and dist-for-goals, but got %d", len(distStrings)) - } - - if distStrings[0] != ".PHONY: bar\n" { - t.Errorf("Expected .PHONY entry to declare bar, but got: %s", distStrings[0]) - } - - if !strings.Contains(distStrings[1], "$(call dist-for-goals,bar") || - !strings.Contains( - distStrings[1], - ".intermediates/foo/android_common/javac/foo.jar:bar/dir/bar.qux.jar") { - t.Errorf( - "Expected dist-for-goals entry to contain bar and new dest dir, but got: %s", distStrings[1]) - } - - if distStrings[2] != ".PHONY: baz\n" { - t.Errorf("Expected .PHONY entry to declare baz, but got: %s", distStrings[2]) - } - - if !strings.Contains(distStrings[3], "$(call dist-for-goals,baz") || - !strings.Contains(distStrings[3], ".intermediates/foo/android_common/dex/foo.jar:foo.jar") { - t.Errorf( - "Expected dist-for-goals entry to contain my_other_goal and new dest dir, but got: %s", - distStrings[3]) - } -} - -func TestDistsWithTag(t *testing.T) { - ctx, config := testJava(t, ` - java_library { - name: "foo_without_tag", - srcs: ["a.java"], - compile_dex: true, - dists: [ - { - targets: ["hi"], - }, - ], - } - java_library { - name: "foo_with_tag", - srcs: ["a.java"], - compile_dex: true, - dists: [ - { - targets: ["hi"], - tag: ".jar", - }, - ], - } - `) - - moduleWithoutTag := ctx.ModuleForTests("foo_without_tag", "android_common").Module() - moduleWithTag := ctx.ModuleForTests("foo_with_tag", "android_common").Module() - - withoutTagEntries := android.AndroidMkEntriesForTest(t, config, "", moduleWithoutTag) - withTagEntries := android.AndroidMkEntriesForTest(t, config, "", moduleWithTag) - - if len(withoutTagEntries) != 2 || len(withTagEntries) != 2 { - t.Errorf("two mk entries per module expected, got %d and %d", len(withoutTagEntries), len(withTagEntries)) - } - - distFilesWithoutTag := withoutTagEntries[0].DistFiles - distFilesWithTag := withTagEntries[0].DistFiles - - if len(distFilesWithTag[".jar"]) != 1 || - !strings.Contains(distFilesWithTag[".jar"][0].String(), "/javac/foo_with_tag.jar") { - t.Errorf("expected foo_with_tag's .jar-tagged DistFiles to contain classes.jar, got %v", distFilesWithTag[".jar"]) - } - if len(distFilesWithoutTag[".jar"]) > 0 { - t.Errorf("did not expect foo_without_tag's .jar-tagged DistFiles to contain files, but got %v", distFilesWithoutTag[".jar"]) - } -} - func TestJavaSdkLibrary_RequireXmlPermissionFile(t *testing.T) { ctx, config := testJava(t, ` java_sdk_library { diff --git a/java/app.go b/java/app.go index 9ff413cc1..e6d9550ec 100755 --- a/java/app.go +++ b/java/app.go @@ -566,7 +566,7 @@ func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) { a.aapt.splitNames = a.appProperties.Package_splits a.aapt.LoggingParent = String(a.overridableAppProperties.Logging_parent) - a.aapt.buildActions(ctx, sdkContext(a), a.exportedSdkLibs, aaptLinkFlags...) + a.aapt.buildActions(ctx, sdkContext(a), a.classLoaderContexts, aaptLinkFlags...) // apps manifests are handled by aapt, don't let Module see them a.properties.Manifest = nil @@ -608,7 +608,7 @@ func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path { } a.dexpreopter.uncompressedDex = *a.dexProperties.Uncompress_dex a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries() - a.dexpreopter.classLoaderContexts = a.exportedSdkLibs + a.dexpreopter.classLoaderContexts = a.classLoaderContexts a.dexpreopter.manifestFile = a.mergedManifestFile if ctx.ModuleName() != "framework-res" { @@ -779,7 +779,7 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { a.aapt.noticeFile = a.noticeOutputs.HtmlGzOutput } - a.exportedSdkLibs = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx) + a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx) // Process all building blocks, from AAPT to certificates. a.aaptBuildActions(ctx) @@ -788,7 +788,7 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { a.usesLibrary.freezeEnforceUsesLibraries() // Add implicit SDK libraries to <uses-library> list. - for _, usesLib := range a.exportedSdkLibs.UsesLibs() { + for _, usesLib := range a.classLoaderContexts.UsesLibs() { a.usesLibrary.addLib(usesLib, inList(usesLib, dexpreopt.OptionalCompatUsesLibs)) } @@ -890,7 +890,7 @@ func collectAppDeps(ctx android.ModuleContext, app appDepsInterface, if IsJniDepTag(tag) || cc.IsSharedDepTag(tag) { if dep, ok := module.(*cc.Module); ok { - if dep.IsNdk() || dep.IsStubs() { + if dep.IsNdk(ctx.Config()) || dep.IsStubs() { return false } @@ -1115,8 +1115,8 @@ func (a *AndroidTest) FixTestConfig(ctx android.ModuleContext, testConfig androi } fixedConfig := android.PathForModuleOut(ctx, "test_config_fixer", "AndroidTest.xml") - rule := android.NewRuleBuilder() - command := rule.Command().BuiltTool(ctx, "test_config_fixer").Input(testConfig).Output(fixedConfig) + rule := android.NewRuleBuilder(pctx, ctx) + command := rule.Command().BuiltTool("test_config_fixer").Input(testConfig).Output(fixedConfig) fixNeeded := false if ctx.ModuleName() != a.installApkName { @@ -1131,7 +1131,7 @@ func (a *AndroidTest) FixTestConfig(ctx android.ModuleContext, testConfig androi } if fixNeeded { - rule.Build(pctx, ctx, "fix_test_config", "fix test config") + rule.Build("fix_test_config", "fix test config") return fixedConfig } return testConfig @@ -1400,6 +1400,13 @@ func (a *AndroidAppImport) processVariants(ctx android.LoadHookContext) { archProps := reflect.ValueOf(a.archVariants).Elem().FieldByName("Arch") archType := ctx.Config().AndroidFirstDeviceTarget.Arch.ArchType MergePropertiesFromVariant(ctx, &a.properties, archProps, archType.Name) + + if String(a.properties.Apk) == "" { + // Disable this module since the apk property is still empty after processing all matching + // variants. This likely means there is no matching variant, and the default variant doesn't + // have an apk property value either. + a.Disable() + } } func MergePropertiesFromVariant(ctx android.EarlyModuleContext, @@ -1440,15 +1447,15 @@ func (a *AndroidAppImport) uncompressEmbeddedJniLibs( }) return } - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) rule.Command(). Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath). - BuiltTool(ctx, "zip2zip"). + BuiltTool("zip2zip"). FlagWithInput("-i ", inputPath). FlagWithOutput("-o ", outputPath). FlagWithArg("-0 ", "'lib/**/*.so'"). Textf(`; else cp -f %s %s; fi`, inputPath, outputPath) - rule.Build(pctx, ctx, "uncompress-embedded-jni-libs", "Uncompress embedded JIN libs") + rule.Build("uncompress-embedded-jni-libs", "Uncompress embedded JIN libs") } // Returns whether this module should have the dex file stored uncompressed in the APK. @@ -1467,15 +1474,15 @@ func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool { func (a *AndroidAppImport) uncompressDex( ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) { - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) rule.Command(). Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath). - BuiltTool(ctx, "zip2zip"). + BuiltTool("zip2zip"). FlagWithInput("-i ", inputPath). FlagWithOutput("-o ", outputPath). FlagWithArg("-0 ", "'classes*.dex'"). Textf(`; else cp -f %s %s; fi`, inputPath, outputPath) - rule.Build(pctx, ctx, "uncompress-dex", "Uncompress dex files") + rule.Build("uncompress-dex", "Uncompress dex files") } func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -1982,7 +1989,7 @@ func (u *usesLibrary) classLoaderContextForUsesLibDeps(ctx android.ModuleContext dep := ctx.OtherModuleName(m) if lib, ok := m.(Dependency); ok { clcMap.AddContextForSdk(ctx, tag.sdkVersion, dep, - lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ExportedSdkLibs()) + lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ClassLoaderContexts()) } else if ctx.Config().AllowMissingDependencies() { ctx.AddMissingDependencies([]string{dep}) } else { @@ -2015,8 +2022,8 @@ func (u *usesLibrary) freezeEnforceUsesLibraries() { func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path) android.Path { outputFile := android.PathForModuleOut(ctx, "manifest_check", "AndroidManifest.xml") - rule := android.NewRuleBuilder() - cmd := rule.Command().BuiltTool(ctx, "manifest_check"). + rule := android.NewRuleBuilder(pctx, ctx) + cmd := rule.Command().BuiltTool("manifest_check"). Flag("--enforce-uses-libraries"). Input(manifest). FlagWithOutput("-o ", outputFile) @@ -2029,7 +2036,7 @@ func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, man cmd.FlagWithArg("--optional-uses-library ", lib) } - rule.Build(pctx, ctx, "verify_uses_libraries", "verify <uses-library>") + rule.Build("verify_uses_libraries", "verify <uses-library>") return outputFile } @@ -2039,7 +2046,7 @@ func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, man func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk android.Path) android.Path { outputFile := android.PathForModuleOut(ctx, "verify_uses_libraries", apk.Base()) - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) aapt := ctx.Config().HostToolPath(ctx, "aapt") rule.Command(). Textf("aapt_binary=%s", aapt.String()).Implicit(aapt). @@ -2048,7 +2055,7 @@ func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk andr Tool(android.PathForSource(ctx, "build/make/core/verify_uses_libraries.sh")).Input(apk) rule.Command().Text("cp -f").Input(apk).Output(outputFile) - rule.Build(pctx, ctx, "verify_uses_libraries", "verify <uses-library>") + rule.Build("verify_uses_libraries", "verify <uses-library>") return outputFile } diff --git a/java/app_test.go b/java/app_test.go index 6429ab836..ef5e84dd2 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -291,7 +291,7 @@ func TestAndroidAppLinkType(t *testing.T) { } `) - testJavaError(t, "Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source.", ` + testJavaError(t, "consider adjusting sdk_version: OR platform_apis:", ` android_app { name: "foo", srcs: ["a.java"], @@ -335,7 +335,7 @@ func TestAndroidAppLinkType(t *testing.T) { } `) - testJavaError(t, "Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source.", ` + testJavaError(t, "consider adjusting sdk_version: OR platform_apis:", ` android_app { name: "foo", srcs: ["a.java"], @@ -2524,6 +2524,24 @@ func TestAndroidAppImport_ArchVariants(t *testing.T) { `, expected: "prebuilts/apk/app.apk", }, + { + name: "no matching arch without default", + bp: ` + android_app_import { + name: "foo", + arch: { + arm: { + apk: "prebuilts/apk/app_arm.apk", + }, + }, + presigned: true, + dex_preopt: { + enabled: true, + }, + } + `, + expected: "", + }, } jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)") @@ -2531,6 +2549,12 @@ func TestAndroidAppImport_ArchVariants(t *testing.T) { ctx, _ := testJava(t, test.bp) variant := ctx.ModuleForTests("foo", "android_common") + if test.expected == "" { + if variant.Module().Enabled() { + t.Error("module should have been disabled, but wasn't") + } + continue + } jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command matches := jniRuleRe.FindStringSubmatch(jniRuleCommand) if len(matches) != 2 { @@ -2730,6 +2754,13 @@ func TestUsesLibraries(t *testing.T) { } java_sdk_library { + name: "fred", + srcs: ["a.java"], + api_packages: ["fred"], + sdk_version: "current", + } + + java_sdk_library { name: "bar", srcs: ["a.java"], api_packages: ["bar"], @@ -2753,7 +2784,12 @@ func TestUsesLibraries(t *testing.T) { name: "app", srcs: ["a.java"], libs: ["qux", "quuz.stubs"], - static_libs: ["static-runtime-helper"], + static_libs: [ + "static-runtime-helper", + // statically linked component libraries should not pull their SDK libraries, + // so "fred" should not be added to class loader context + "fred.stubs", + ], uses_libs: ["foo"], sdk_version: "current", optional_uses_libs: [ diff --git a/java/boot_jars.go b/java/boot_jars.go index e70654781..823275b1d 100644 --- a/java/boot_jars.go +++ b/java/boot_jars.go @@ -85,8 +85,8 @@ func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) { timestamp := android.PathForOutput(ctx, "boot-jars-package-check/stamp") - rule := android.NewRuleBuilder() - checkBootJars := rule.Command().BuiltTool(ctx, "check_boot_jars"). + rule := android.NewRuleBuilder(pctx, ctx) + checkBootJars := rule.Command().BuiltTool("check_boot_jars"). Input(ctx.Config().HostToolPath(ctx, "dexdump")). Input(android.PathForSource(ctx, "build/soong/scripts/check_boot_jars/package_allowed_list.txt")) @@ -109,7 +109,7 @@ func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) { } checkBootJars.Text("&& touch").Output(timestamp) - rule.Build(pctx, ctx, "boot_jars_package_check", "check boot jar packages") + rule.Build("boot_jars_package_check", "check boot jar packages") // The check-boot-jars phony target depends on the timestamp created if the check succeeds. ctx.Phony("check-boot-jars", timestamp) diff --git a/java/builder.go b/java/builder.go index 3043e46db..cd3524542 100644 --- a/java/builder.go +++ b/java/builder.go @@ -572,14 +572,7 @@ func TransformJetifier(ctx android.ModuleContext, outputFile android.WritablePat } func GenerateMainClassManifest(ctx android.ModuleContext, outputFile android.WritablePath, mainClass string) { - ctx.Build(pctx, android.BuildParams{ - Rule: android.WriteFile, - Description: "manifest", - Output: outputFile, - Args: map[string]string{ - "content": "Main-Class: " + mainClass + "\n", - }, - }) + android.WriteFileRule(ctx, outputFile, "Main-Class: "+mainClass+"\n") } func TransformZipAlign(ctx android.ModuleContext, outputFile android.WritablePath, inputFile android.Path) { diff --git a/java/device_host_converter.go b/java/device_host_converter.go index d8b617e7d..4914d74f6 100644 --- a/java/device_host_converter.go +++ b/java/device_host_converter.go @@ -163,12 +163,12 @@ func (d *DeviceHostConverter) AidlIncludeDirs() android.Paths { return nil } -func (d *DeviceHostConverter) ExportedSdkLibs() dexpreopt.ClassLoaderContextMap { +func (d *DeviceHostConverter) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap { return nil } -func (d *DeviceHostConverter) ExportedPlugins() (android.Paths, []string) { - return nil, nil +func (d *DeviceHostConverter) ExportedPlugins() (android.Paths, []string, bool) { + return nil, nil, false } func (d *DeviceHostConverter) SrcJarArgs() ([]string, android.Paths) { diff --git a/java/dexpreopt.go b/java/dexpreopt.go index a21fb7640..67738d49b 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -216,7 +216,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo return dexJarFile } - dexpreoptRule.Build(pctx, ctx, "dexpreopt", "dexpreopt") + dexpreoptRule.Build("dexpreopt", "dexpreopt") d.builtInstalled = dexpreoptRule.Installs().String() diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index 629d34f25..062005b5b 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -25,11 +25,177 @@ import ( "github.com/google/blueprint/proptools" ) +// This comment describes: +// 1. ART boot images in general (their types, structure, file layout, etc.) +// 2. build system support for boot images +// +// 1. ART boot images +// ------------------ +// +// A boot image in ART is a set of files that contain AOT-compiled native code and a heap snapshot +// of AOT-initialized classes for the bootclasspath Java libraries. A boot image is compiled from a +// set of DEX jars by the dex2oat compiler. A boot image is used for two purposes: 1) it is +// installed on device and loaded at runtime, and 2) other Java libraries and apps are compiled +// against it (compilation may take place either on host, known as "dexpreopt", or on device, known +// as "dexopt"). +// +// A boot image is not a single file, but a collection of interrelated files. Each boot image has a +// number of components that correspond to the Java libraries that constitute it. For each component +// there are multiple files: +// - *.oat or *.odex file with native code (architecture-specific, one per instruction set) +// - *.art file with pre-initialized Java classes (architecture-specific, one per instruction set) +// - *.vdex file with verification metadata for the DEX bytecode (architecture independent) +// +// *.vdex files for the boot images do not contain the DEX bytecode itself, because the +// bootclasspath DEX files are stored on disk in uncompressed and aligned form. Consequently a boot +// image is not self-contained and cannot be used without its DEX files. To simplify the management +// of boot image files, ART uses a certain naming scheme and associates the following metadata with +// each boot image: +// - A stem, which is a symbolic name that is prepended to boot image file names. +// - A location (on-device path to the boot image files). +// - A list of boot image locations (on-device paths to dependency boot images). +// - A set of DEX locations (on-device paths to the DEX files, one location for one DEX file used +// to compile the boot image). +// +// There are two kinds of boot images: +// - primary boot images +// - boot image extensions +// +// 1.1. Primary boot images +// ------------------------ +// +// A primary boot image is compiled for a core subset of bootclasspath Java libraries. It does not +// depend on any other images, and other boot images may depend on it. +// +// For example, assuming that the stem is "boot", the location is /apex/com.android.art/javalib/, +// the set of core bootclasspath libraries is A B C, and the boot image is compiled for ARM targets +// (32 and 64 bits), it will have three components with the following files: +// - /apex/com.android.art/javalib/{arm,arm64}/boot.{art,oat,vdex} +// - /apex/com.android.art/javalib/{arm,arm64}/boot-B.{art,oat,vdex} +// - /apex/com.android.art/javalib/{arm,arm64}/boot-C.{art,oat,vdex} +// +// The files of the first component are special: they do not have the component name appended after +// the stem. This naming convention dates back to the times when the boot image was not split into +// components, and there were just boot.oat and boot.art. The decision to split was motivated by +// licensing reasons for one of the bootclasspath libraries. +// +// As of November 2020 the only primary boot image in Android is the image in the ART APEX +// com.android.art. The primary ART boot image contains the Core libraries that are part of the ART +// module. When the ART module gets updated, the primary boot image will be updated with it, and all +// dependent images will get invalidated (the checksum of the primary image stored in dependent +// images will not match), unless they are updated in sync with the ART module. +// +// 1.2. Boot image extensions +// -------------------------- +// +// A boot image extension is compiled for a subset of bootclasspath Java libraries (in particular, +// this subset does not include the Core bootclasspath libraries that go into the primary boot +// image). A boot image extension depends on the primary boot image and optionally some other boot +// image extensions. Other images may depend on it. In other words, boot image extensions can form +// acyclic dependency graphs. +// +// The motivation for boot image extensions comes from the Mainline project. Consider a situation +// when the list of bootclasspath libraries is A B C, and both A and B are parts of the Android +// platform, but C is part of an updatable APEX com.android.C. When the APEX is updated, the Java +// code for C might have changed compared to the code that was used to compile the boot image. +// Consequently, the whole boot image is obsolete and invalidated (even though the code for A and B +// that does not depend on C is up to date). To avoid this, the original monolithic boot image is +// split in two parts: the primary boot image that contains A B, and the boot image extension that +// contains C and depends on the primary boot image (extends it). +// +// For example, assuming that the stem is "boot", the location is /system/framework, the set of +// bootclasspath libraries is D E (where D is part of the platform and is located in +// /system/framework, and E is part of a non-updatable APEX com.android.E and is located in +// /apex/com.android.E/javalib), and the boot image is compiled for ARM targets (32 and 64 bits), +// it will have two components with the following files: +// - /system/framework/{arm,arm64}/boot-D.{art,oat,vdex} +// - /system/framework/{arm,arm64}/boot-E.{art,oat,vdex} +// +// As of November 2020 the only boot image extension in Android is the Framework boot image +// extension. It extends the primary ART boot image and contains Framework libraries and other +// bootclasspath libraries from the platform and non-updatable APEXes that are not included in the +// ART image. The Framework boot image extension is updated together with the platform. In the +// future other boot image extensions may be added for some updatable modules. +// +// +// 2. Build system support for boot images +// --------------------------------------- +// +// The primary ART boot image needs to be compiled with one dex2oat invocation that depends on DEX +// jars for the core libraries. Framework boot image extension needs to be compiled with one dex2oat +// invocation that depends on the primary ART boot image and all bootclasspath DEX jars except the +// Core libraries. +// +// 2.1. Libraries that go in the boot images +// ----------------------------------------- +// +// The contents of each boot image are determined by the PRODUCT variables. The primary ART APEX +// boot image contains libraries listed in the ART_APEX_JARS variable in the AOSP makefiles. The +// Framework boot image extension contains libraries specified in the PRODUCT_BOOT_JARS and +// PRODUCT_BOOT_JARS_EXTRA variables. The AOSP makefiles specify some common Framework libraries, +// but more product-specific libraries can be added in the product makefiles. +// +// Each component of the PRODUCT_BOOT_JARS and PRODUCT_BOOT_JARS_EXTRA variables is either a simple +// name (if the library is a part of the Platform), or a colon-separated pair <apex, name> (if the +// library is a part of a non-updatable APEX). +// +// A related variable PRODUCT_UPDATABLE_BOOT_JARS contains bootclasspath libraries that are in +// updatable APEXes. They are not included in the boot image. +// +// One exception to the above rules are "coverage" builds (a special build flavor which requires +// setting environment variable EMMA_INSTRUMENT_FRAMEWORK=true). In coverage builds the Java code in +// boot image libraries is instrumented, which means that the instrumentation library (jacocoagent) +// needs to be added to the list of bootclasspath DEX jars. +// +// In general, there is a requirement that the source code for a boot image library must be +// available at build time (e.g. it cannot be a stub that has a separate implementation library). +// +// 2.2. Static configs +// ------------------- +// +// Because boot images are used to dexpreopt other Java modules, the paths to boot image files must +// be known by the time dexpreopt build rules for the dependent modules are generated. Boot image +// configs are constructed very early during the build, before build rule generation. The configs +// provide predefined paths to boot image files (these paths depend only on static build +// configuration, such as PRODUCT variables, and use hard-coded directory names). +// +// 2.3. Singleton +// -------------- +// +// Build rules for the boot images are generated with a Soong singleton. Because a singleton has no +// dependencies on other modules, it has to find the modules for the DEX jars using VisitAllModules. +// Soong loops through all modules and compares each module against a list of bootclasspath library +// names. Then it generates build rules that copy DEX jars from their intermediate module-specific +// locations to the hard-coded locations predefined in the boot image configs. +// +// It would be possible to use a module with proper dependencies instead, but that would require +// changes in the way Soong generates variables for Make: a singleton can use one MakeVars() method +// that writes variables to out/soong/make_vars-*.mk, which is included early by the main makefile, +// but module(s) would have to use out/soong/Android-*.mk which has a group of LOCAL_* variables +// for each module, and is included later. +// +// 2.4. Install rules +// ------------------ +// +// The primary boot image and the Framework extension are installed in different ways. The primary +// boot image is part of the ART APEX: it is copied into the APEX intermediate files, packaged +// together with other APEX contents, extracted and mounted on device. The Framework boot image +// extension is installed by the rules defined in makefiles (make/core/dex_preopt_libart.mk). Soong +// writes out a few DEXPREOPT_IMAGE_* variables for Make; these variables contain boot image names, +// paths and so on. +// +// 2.5. JIT-Zygote configuration +// ----------------------------- +// +// One special configuration is JIT-Zygote build, when the primary ART image is used for compiling +// apps instead of the Framework boot image extension (see DEXPREOPT_USE_ART_IMAGE and UseArtImage). +// + func init() { RegisterDexpreoptBootJarsComponents(android.InitRegistrationContext) } -// Target-independent description of pre-compiled boot image. +// Target-independent description of a boot image. type bootImageConfig struct { // If this image is an extension, the image that it extends. extends *bootImageConfig @@ -66,7 +232,7 @@ type bootImageConfig struct { variants []*bootImageVariant } -// Target-dependent description of pre-compiled boot image. +// Target-dependent description of a boot image. type bootImageVariant struct { *bootImageConfig @@ -90,6 +256,7 @@ type bootImageVariant struct { unstrippedInstalls android.RuleBuilderInstalls } +// Get target-specific boot image variant for the given boot image config and target. func (image bootImageConfig) getVariant(target android.Target) *bootImageVariant { for _, variant := range image.variants { if variant.target.Os == target.Os && variant.target.Arch.ArchType == target.Arch.ArchType { @@ -99,7 +266,7 @@ func (image bootImageConfig) getVariant(target android.Target) *bootImageVariant return nil } -// Return any (the first) variant which is for the device (as opposed to for the host) +// Return any (the first) variant which is for the device (as opposed to for the host). func (image bootImageConfig) getAnyAndroidVariant() *bootImageVariant { for _, variant := range image.variants { if variant.target.Os == android.Android { @@ -109,10 +276,12 @@ func (image bootImageConfig) getAnyAndroidVariant() *bootImageVariant { return nil } +// Return the name of a boot image module given a boot image config and a component (module) index. +// A module name is a combination of the Java library name, and the boot image stem (that is stored +// in the config). 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. + // The first module of the primary boot image is special: its module name has only the stem, but + // not the library name. All other module names are of the form <stem>-<library name> m := image.modules.Jar(idx) name := image.stem if idx != 0 || image.extends != nil { @@ -121,6 +290,7 @@ func (image bootImageConfig) moduleName(ctx android.PathContext, idx int) string return name } +// Return the name of the first boot image module, or stem if the list of modules is empty. func (image bootImageConfig) firstModuleNameOrStem(ctx android.PathContext) string { if image.modules.Len() > 0 { return image.moduleName(ctx, 0) @@ -129,6 +299,8 @@ func (image bootImageConfig) firstModuleNameOrStem(ctx android.PathContext) stri } } +// Return filenames for the given boot image component, given the output directory and a list of +// extensions. func (image bootImageConfig) moduleFiles(ctx android.PathContext, dir android.OutputPath, exts ...string) android.OutputPaths { ret := make(android.OutputPaths, 0, image.modules.Len()*len(exts)) for i := 0; i < image.modules.Len(); i++ { @@ -140,17 +312,26 @@ func (image bootImageConfig) moduleFiles(ctx android.PathContext, dir android.Ou return ret } +// Return boot image locations (as a list of symbolic paths). +// // The image "location" is a symbolic path that, with multiarchitecture support, doesn't really // exist on the device. Typically it is /apex/com.android.art/javalib/boot.art and should be the // same for all supported architectures on the device. The concrete architecture specific files // actually end up in architecture-specific sub-directory such as arm, arm64, x86, or x86_64. // -// For example a physical file -// "/apex/com.android.art/javalib/x86/boot.art" has "image location" -// "/apex/com.android.art/javalib/boot.art" (which is not an actual file). +// For example a physical file /apex/com.android.art/javalib/x86/boot.art has "image location" +// /apex/com.android.art/javalib/boot.art (which is not an actual file). +// +// For a primary boot image the list of locations has a single element. +// +// For a boot image extension the list of locations contains a location for all dependency images +// (including the primary image) and the location of the extension itself. For example, for the +// Framework boot image extension that depends on the primary ART boot image the list contains two +// elements. // // The location is passed as an argument to the ART tools like dex2oat instead of the real path. // ART tools will then reconstruct the architecture-specific real path. +// func (image *bootImageVariant) imageLocations() (imageLocations []string) { if image.extends != nil { imageLocations = image.extends.getVariant(image.target).imageLocations() @@ -158,18 +339,6 @@ func (image *bootImageVariant) imageLocations() (imageLocations []string) { return append(imageLocations, dexpreopt.PathToLocation(image.images, image.target.Arch.ArchType)) } -func concat(lists ...[]string) []string { - var size int - for _, l := range lists { - size += len(l) - } - ret := make([]string, 0, size) - for _, l := range lists { - ret = append(ret, l...) - } - return ret -} - func dexpreoptBootJarsFactory() android.Singleton { return &dexpreoptBootJars{} } @@ -182,10 +351,21 @@ func skipDexpreoptBootJars(ctx android.PathContext) bool { return dexpreopt.GetGlobalConfig(ctx).DisablePreopt } +// Singleton for generating boot image build rules. type dexpreoptBootJars struct { + // Default boot image config (currently always the Framework boot image extension). It should be + // noted that JIT-Zygote builds use ART APEX image instead of the Framework boot image extension, + // but the switch is handled not here, but in the makefiles (triggered with + // DEXPREOPT_USE_ART_IMAGE=true). defaultBootImage *bootImageConfig - otherImages []*bootImageConfig + // Other boot image configs (currently the list contains only the primary ART APEX image. It + // used to contain an experimental JIT-Zygote image (now replaced with the ART APEX image). In + // the future other boot image extensions may be added. + otherImages []*bootImageConfig + + // Build path to a config file that Soong writes for Make (to be used in makefiles that install + // the default boot image). dexpreoptConfigForMake android.WritablePath } @@ -205,7 +385,7 @@ func DexpreoptedArtApexJars(ctx android.BuilderContext) map[android.ArchType]and return files } -// dexpreoptBoot singleton rules +// Generate build rules for boot images. func (d *dexpreoptBootJars) GenerateBuildActions(ctx android.SingletonContext) { if skipDexpreoptBootJars(ctx) { return @@ -238,43 +418,53 @@ func (d *dexpreoptBootJars) GenerateBuildActions(ctx android.SingletonContext) { dumpOatRules(ctx, d.defaultBootImage) } -func isHostdex(module android.Module) bool { - if lib, ok := module.(*Library); ok { - return Bool(lib.deviceProperties.Hostdex) - } - return false -} - // Inspect this module to see if it contains a bootclasspath dex jar. // Note that the same jar may occur in multiple modules. // This logic is tested in the apex package to avoid import cycle apex <-> java. func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, module android.Module) (int, android.Path) { - // All apex Java libraries have non-installable platform variants, skip them. - if module.IsSkipInstall() { + // Ignore any module that is not listed in the boot image configuration. + name := ctx.ModuleName(module) + index := image.modules.IndexOfJar(name) + if index == -1 { return -1, nil } + // It is an error if a module configured in the boot image does not support + // accessing the dex jar. This is safe because every module that has the same + // name has to have the same module type. jar, hasJar := module.(interface{ DexJarBuildPath() android.Path }) if !hasJar { + ctx.Errorf("module %q configured in boot image %q does not support accessing dex jar", module, image.name) return -1, nil } - name := ctx.ModuleName(module) - index := image.modules.IndexOfJar(name) - if index == -1 { + // It is also an error if the module is not an ApexModule. + if _, ok := module.(android.ApexModule); !ok { + ctx.Errorf("module %q configured in boot image %q does not support being added to an apex", module, image.name) return -1, nil } - // Check that this module satisfies constraints for a particular boot image. - _, isApexModule := module.(android.ApexModule) apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo) - fromUpdatableApex := isApexModule && apexInfo.Updatable - if image.name == artBootImageName { - if isApexModule && len(apexInfo.InApexes) > 0 && allHavePrefix(apexInfo.InApexes, "com.android.art") { - // ok: found the jar in the ART apex - } else if isApexModule && apexInfo.IsForPlatform() && isHostdex(module) { - // exception (skip and continue): special "hostdex" platform variant + + // Now match the apex part of the boot image configuration. + requiredApex := image.modules.Apex(index) + if requiredApex == "platform" { + if len(apexInfo.InApexes) != 0 { + // A platform variant is required but this is for an apex so ignore it. return -1, nil + } + } else if !android.InList(requiredApex, apexInfo.InApexes) { + // An apex variant for a specific apex is required but this is the wrong apex. + return -1, nil + } + + // Check that this module satisfies any boot image specific constraints. + fromUpdatableApex := apexInfo.Updatable + + switch image.name { + case artBootImageName: + if len(apexInfo.InApexes) > 0 && allHavePrefix(apexInfo.InApexes, "com.android.art") { + // ok: found the jar in the ART apex } else if name == "jacocoagent" && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") { // exception (skip and continue): Jacoco platform variant for a coverage build return -1, nil @@ -285,14 +475,15 @@ func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, modul // error: this jar is part of the platform or a non-updatable apex ctx.Errorf("module %q is not allowed in the ART boot image", name) } - } else if image.name == frameworkBootImageName { + + case frameworkBootImageName: if !fromUpdatableApex { // ok: this jar is part of the platform or a non-updatable apex } else { // error: this jar is part of an updatable apex ctx.Errorf("module %q from updatable apexes %q is not allowed in the framework boot image", name, apexInfo.InApexes) } - } else { + default: panic("unknown boot image: " + image.name) } @@ -315,6 +506,12 @@ func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootI bootDexJars := make(android.Paths, image.modules.Len()) ctx.VisitAllModules(func(module android.Module) { if i, j := getBootImageJar(ctx, image, module); i != -1 { + if existing := bootDexJars[i]; existing != nil { + ctx.Errorf("Multiple dex jars found for %s:%s - %s and %s", + image.modules.Apex(i), image.modules.Jar(i), existing, j) + return + } + bootDexJars[i] = j } }) @@ -326,7 +523,7 @@ func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootI m := image.modules.Jar(i) if ctx.Config().AllowMissingDependencies() { missingDeps = append(missingDeps, m) - bootDexJars[i] = android.PathForOutput(ctx, "missing") + bootDexJars[i] = android.PathForOutput(ctx, "missing/module", m, "from/apex", image.modules.Apex(i)) } else { ctx.Errorf("failed to find a dex jar path for module '%s'"+ ", note that some jars may be filtered out by module constraints", m) @@ -334,9 +531,10 @@ func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootI } } - // The path to bootclasspath dex files needs to be known at module GenerateAndroidBuildAction time, before - // the bootclasspath modules have been compiled. Copy the dex jars there so the module rules that have - // already been set up can find them. + // The paths to bootclasspath DEX files need to be known at module GenerateAndroidBuildAction + // time, before the boot images are built (these paths are used in dexpreopt rule generation for + // Java libraries and apps). Generate rules that copy bootclasspath DEX jars to the predefined + // paths. for i := range bootDexJars { ctx.Build(pctx, android.BuildParams{ Rule: android.Cp, @@ -358,19 +556,20 @@ func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootI } if image.zip != nil { - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) rule.Command(). - BuiltTool(ctx, "soong_zip"). + BuiltTool("soong_zip"). FlagWithOutput("-o ", image.zip). FlagWithArg("-C ", image.dir.Join(ctx, android.Android.String()).String()). FlagWithInputList("-f ", zipFiles, " -f ") - rule.Build(pctx, ctx, "zip_"+image.name, "zip "+image.name+" image") + rule.Build("zip_"+image.name, "zip "+image.name+" image") } return image } +// Generate boot image build rules for a specific target. func buildBootImageVariant(ctx android.SingletonContext, image *bootImageVariant, profile android.Path, missingDeps []string) android.WritablePaths { @@ -386,7 +585,7 @@ func buildBootImageVariant(ctx android.SingletonContext, image *bootImageVariant oatLocation := dexpreopt.PathToLocation(outputPath, arch) imagePath := outputPath.ReplaceExtension(ctx, "art") - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) rule.MissingDeps(missingDeps) rule.Command().Text("mkdir").Flag("-p").Flag(symbolsDir.String()) @@ -428,12 +627,15 @@ func buildBootImageVariant(ctx android.SingletonContext, image *bootImageVariant } if image.extends != nil { + // It is a boot image extension, so it needs the boot image it depends on (in this case the + // primary ART APEX image). artImage := image.primaryImages cmd. Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", image.dexPathsDeps.Paths(), ":"). Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", image.dexLocationsDeps, ":"). FlagWithArg("--boot-image=", dexpreopt.PathToLocation(artImage, arch)).Implicit(artImage) } else { + // It is a primary image, so it needs a base address. cmd.FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress()) } @@ -504,7 +706,7 @@ func buildBootImageVariant(ctx android.SingletonContext, image *bootImageVariant android.RuleBuilderInstall{unstrippedOat, filepath.Join(installDir, unstrippedOat.Base())}) } - rule.Build(pctx, ctx, image.name+"JarsDexpreopt_"+image.target.String(), "dexpreopt "+image.name+" jars "+arch.String()) + rule.Build(image.name+"JarsDexpreopt_"+image.target.String(), "dexpreopt "+image.name+" jars "+arch.String()) // save output and installed files for makevars image.installs = rule.Installs() @@ -528,7 +730,7 @@ func bootImageProfileRule(ctx android.SingletonContext, image *bootImageConfig, profile := ctx.Config().Once(bootImageProfileRuleKey, func() interface{} { defaultProfile := "frameworks/base/config/boot-image-profile.txt" - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) rule.MissingDeps(missingDeps) var bootImageProfile android.Path @@ -559,7 +761,7 @@ func bootImageProfileRule(ctx android.SingletonContext, image *bootImageConfig, rule.Install(profile, "/system/etc/boot-image.prof") - rule.Build(pctx, ctx, "bootJarsProfile", "profile boot jars") + rule.Build("bootJarsProfile", "profile boot jars") image.profileInstalls = rule.Installs() @@ -581,7 +783,7 @@ func bootFrameworkProfileRule(ctx android.SingletonContext, image *bootImageConf return nil } return ctx.Config().Once(bootFrameworkProfileRuleKey, func() interface{} { - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) rule.MissingDeps(missingDeps) // Some branches like master-art-host don't have frameworks/base, so manually @@ -594,7 +796,7 @@ func bootFrameworkProfileRule(ctx android.SingletonContext, image *bootImageConf bootFrameworkProfile = path.Path() } else { missingDeps = append(missingDeps, defaultProfile) - bootFrameworkProfile = android.PathForOutput(ctx, "missing") + bootFrameworkProfile = android.PathForOutput(ctx, "missing", defaultProfile) } profile := image.dir.Join(ctx, "boot.bprof") @@ -609,7 +811,7 @@ func bootFrameworkProfileRule(ctx android.SingletonContext, image *bootImageConf FlagWithOutput("--reference-profile-file=", profile) rule.Install(profile, "/system/etc/boot-image.bprof") - rule.Build(pctx, ctx, "bootFrameworkProfile", "profile boot framework jars") + rule.Build("bootFrameworkProfile", "profile boot framework jars") image.profileInstalls = append(image.profileInstalls, rule.Installs()...) return profile @@ -651,16 +853,10 @@ func updatableBcpPackagesRule(ctx android.SingletonContext, image *bootImageConf updatableBcpPackagesName := "updatable-bcp-packages.txt" updatableBcpPackages := image.dir.Join(ctx, updatableBcpPackagesName) - ctx.Build(pctx, android.BuildParams{ - Rule: android.WriteFile, - Output: updatableBcpPackages, - Args: map[string]string{ - // WriteFile automatically adds the last end-of-line. - "content": strings.Join(updatablePackages, "\\n"), - }, - }) + // WriteFileRule automatically adds the last end-of-line. + android.WriteFileRule(ctx, updatableBcpPackages, strings.Join(updatablePackages, "\n")) - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) rule.MissingDeps(missingDeps) rule.Install(updatableBcpPackages, "/system/etc/"+updatableBcpPackagesName) // TODO: Rename `profileInstalls` to `extraInstalls`? @@ -684,25 +880,25 @@ func dumpOatRules(ctx android.SingletonContext, image *bootImageConfig) { } // Create a rule to call oatdump. output := android.PathForOutput(ctx, "boot."+suffix+".oatdump.txt") - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) rule.Command(). // TODO: for now, use the debug version for better error reporting - BuiltTool(ctx, "oatdumpd"). + BuiltTool("oatdumpd"). FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPathsDeps.Paths(), ":"). FlagWithList("--runtime-arg -Xbootclasspath-locations:", image.dexLocationsDeps, ":"). FlagWithArg("--image=", strings.Join(image.imageLocations(), ":")).Implicits(image.imagesDeps.Paths()). FlagWithOutput("--output=", output). FlagWithArg("--instruction-set=", arch.String()) - rule.Build(pctx, ctx, "dump-oat-boot-"+suffix, "dump oat boot "+arch.String()) + rule.Build("dump-oat-boot-"+suffix, "dump oat boot "+arch.String()) // Create a phony rule that depends on the output file and prints the path. phony := android.PathForPhony(ctx, "dump-oat-boot-"+suffix) - rule = android.NewRuleBuilder() + rule = android.NewRuleBuilder(pctx, ctx) rule.Command(). Implicit(output). ImplicitOutput(phony). Text("echo").FlagWithArg("Output in ", output.String()) - rule.Build(pctx, ctx, "phony-dump-oat-boot-"+suffix, "dump oat boot "+arch.String()) + rule.Build("phony-dump-oat-boot-"+suffix, "dump oat boot "+arch.String()) allPhonies = append(allPhonies, phony) } @@ -720,16 +916,12 @@ func dumpOatRules(ctx android.SingletonContext, image *bootImageConfig) { func writeGlobalConfigForMake(ctx android.SingletonContext, path android.WritablePath) { data := dexpreopt.GetGlobalConfigRawData(ctx) - ctx.Build(pctx, android.BuildParams{ - Rule: android.WriteFile, - Output: path, - Args: map[string]string{ - "content": string(data), - }, - }) + android.WriteFileRule(ctx, path, string(data)) } -// Export paths for default boot image to Make +// Define Make variables for boot image names, paths, etc. These variables are used in makefiles +// (make/core/dex_preopt_libart.mk) to generate install rules that copy boot image files to the +// correct output directories. func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) { if d.dexpreoptConfigForMake != nil { ctx.Strict("DEX_PREOPT_CONFIG_FOR_MAKE", d.dexpreoptConfigForMake.String()) @@ -743,6 +935,11 @@ func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) { ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(image.getAnyAndroidVariant().dexLocationsDeps, " ")) var imageNames []string + // TODO: the primary ART boot image should not be exposed to Make, as it is installed in a + // different way as a part of the ART APEX. However, there is a special JIT-Zygote build + // configuration which uses the primary ART image instead of the Framework boot image + // extension, and it relies on the ART image being exposed to Make. To fix this, it is + // necessary to rework the logic in makefiles. for _, current := range append(d.otherImages, image) { imageNames = append(imageNames, current.name) for _, variant := range current.variants { diff --git a/java/droiddoc.go b/java/droiddoc.go index c7a27c2a4..9c88a3ca2 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -671,7 +671,7 @@ func (j *Javadoc) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.stubsSrcJar = nil - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) rule.Command().Text("rm -rf").Text(outDir.String()) rule.Command().Text("mkdir -p").Text(outDir.String()) @@ -689,7 +689,7 @@ func (j *Javadoc) GenerateAndroidBuildActions(ctx android.ModuleContext) { Flag("-Xdoclint:none") rule.Command(). - BuiltTool(ctx, "soong_zip"). + BuiltTool("soong_zip"). Flag("-write_if_changed"). Flag("-d"). FlagWithOutput("-o ", j.docZip). @@ -700,7 +700,7 @@ func (j *Javadoc) GenerateAndroidBuildActions(ctx android.ModuleContext) { zipSyncCleanupCmd(rule, srcJarDir) - rule.Build(pctx, ctx, "javadoc", "javadoc") + rule.Build("javadoc", "javadoc") } // @@ -845,7 +845,7 @@ func javadocCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs andro outDir, srcJarDir, srcJarList android.Path, sourcepaths android.Paths) *android.RuleBuilderCommand { cmd := rule.Command(). - BuiltTool(ctx, "soong_javac_wrapper").Tool(config.JavadocCmd(ctx)). + BuiltTool("soong_javac_wrapper").Tool(config.JavadocCmd(ctx)). Flag(config.JavacVmFlags). FlagWithArg("-encoding ", "UTF-8"). FlagWithRspFileInputList("@", srcs). @@ -914,7 +914,7 @@ func dokkaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, dokkaClasspath := append(bootclasspath.Paths(), classpath.Paths()...) return rule.Command(). - BuiltTool(ctx, "dokka"). + BuiltTool("dokka"). Flag(config.JavacVmFlags). Flag(srcJarDir.String()). FlagWithInputList("-classpath ", dokkaClasspath, ":"). @@ -934,7 +934,7 @@ func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) { outDir := android.PathForModuleOut(ctx, "out") srcJarDir := android.PathForModuleOut(ctx, "srcjars") - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars) @@ -968,7 +968,7 @@ func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) { } rule.Command(). - BuiltTool(ctx, "soong_zip"). + BuiltTool("soong_zip"). Flag("-write_if_changed"). Flag("-d"). FlagWithOutput("-o ", d.docZip). @@ -979,7 +979,7 @@ func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) { zipSyncCleanupCmd(rule, srcJarDir) - rule.Build(pctx, ctx, "javadoc", desc) + rule.Build("javadoc", desc) } // @@ -1050,7 +1050,8 @@ func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) { return android.Paths{d.stubsSrcJar}, nil case ".docs.zip": return android.Paths{d.docZip}, nil - case ".api.txt": + case ".api.txt", android.DefaultDistTag: + // This is the default dist path for dist properties that have no tag property. return android.Paths{d.apiFilePath}, nil case ".removed-api.txt": return android.Paths{d.removedApiFilePath}, nil @@ -1277,7 +1278,7 @@ func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersi }).NoVarTemplate(ctx.Config())) } - cmd.BuiltTool(ctx, "metalava"). + cmd.BuiltTool("metalava"). Flag(config.JavacVmFlags). Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED"). FlagWithArg("-encoding ", "UTF-8"). @@ -1332,7 +1333,7 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { srcJarDir := android.PathForModuleOut(ctx, "srcjars") - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) if BoolDefault(d.properties.High_mem, false) { // This metalava run uses lots of memory, restrict the number of metalava jobs that can run in parallel. @@ -1479,19 +1480,19 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { cmd.FlagWithArg("--error-message:compatibility:released ", msg) } - impRule := android.NewRuleBuilder() + impRule := android.NewRuleBuilder(pctx, ctx) impCmd := impRule.Command() // An action that copies the ninja generated rsp file to a new location. This allows us to // add a large number of inputs to a file without exceeding bash command length limits (which // would happen if we use the WriteFile rule). The cp is needed because RuleBuilder sets the // rsp file to be ${output}.rsp. impCmd.Text("cp").FlagWithRspFileInputList("", cmd.GetImplicits()).Output(implicitsRsp) - impRule.Build(pctx, ctx, "implicitsGen", "implicits generation") + impRule.Build("implicitsGen", "implicits generation") cmd.Implicit(implicitsRsp) if generateStubs { rule.Command(). - BuiltTool(ctx, "soong_zip"). + BuiltTool("soong_zip"). Flag("-write_if_changed"). Flag("-jar"). FlagWithOutput("-o ", d.Javadoc.stubsSrcJar). @@ -1502,7 +1503,7 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { if Bool(d.properties.Write_sdk_values) { d.metadataZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-metadata.zip") rule.Command(). - BuiltTool(ctx, "soong_zip"). + BuiltTool("soong_zip"). Flag("-write_if_changed"). Flag("-d"). FlagWithOutput("-o ", d.metadataZip). @@ -1523,7 +1524,7 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { zipSyncCleanupCmd(rule, srcJarDir) - rule.Build(pctx, ctx, "metalava", "metalava merged") + rule.Build("metalava", "metalava merged") if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") { @@ -1541,7 +1542,7 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "check_current_api.timestamp") - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) // Diff command line. // -F matches the closest "opening" line, such as "package android {" @@ -1563,7 +1564,7 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { ` 1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+ ` to the new methods, etc. shown in the above diff.\n\n`+ ` 2. You can update current.txt and/or removed.txt by executing the following command:\n`+ - ` make %s-update-current-api\n\n`+ + ` m %s-update-current-api\n\n`+ ` To submit the revised current.txt to the main Android repository,\n`+ ` you will need approval.\n`+ `******************************\n`, ctx.ModuleName()) @@ -1575,12 +1576,12 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { Text("; exit 38"). Text(")") - rule.Build(pctx, ctx, "metalavaCurrentApiCheck", "check current API") + rule.Build("metalavaCurrentApiCheck", "check current API") d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "update_current_api.timestamp") // update API rule - rule = android.NewRuleBuilder() + rule = android.NewRuleBuilder(pctx, ctx) rule.Command().Text("( true") @@ -1601,7 +1602,7 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { Text("; exit 38"). Text(")") - rule.Build(pctx, ctx, "metalavaCurrentApiUpdate", "update current API") + rule.Build("metalavaCurrentApiUpdate", "update current API") } if String(d.properties.Check_nullability_warnings) != "" { @@ -1624,7 +1625,7 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { ` and submitting the updated file as part of your change.`, d.nullabilityWarningsFile, checkNullabilityWarnings) - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) rule.Command(). Text("("). @@ -1636,7 +1637,7 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { Text("; exit 38"). Text(")") - rule.Build(pctx, ctx, "nullabilityWarningsCheck", "nullability warnings check") + rule.Build("nullabilityWarningsCheck", "nullability warnings check") } } @@ -1721,7 +1722,7 @@ func zipSyncCmd(ctx android.ModuleContext, rule *android.RuleBuilder, rule.Temporary(srcJarList) - rule.Command().BuiltTool(ctx, "zipsync"). + rule.Command().BuiltTool("zipsync"). FlagWithArg("-d ", srcJarDir.String()). FlagWithOutput("-l ", srcJarList). FlagWithArg("-f ", `"*.java"`). @@ -1748,8 +1749,6 @@ type PrebuiltStubsSources struct { properties PrebuiltStubsSourcesProperties - // The source directories containing stubs source files. - srcDirs android.Paths stubsSrcJar android.ModuleOutPath } @@ -1769,25 +1768,33 @@ func (d *PrebuiltStubsSources) StubsSrcJar() android.Path { func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) { p.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar") - p.srcDirs = android.PathsForModuleSrc(ctx, p.properties.Srcs) + if len(p.properties.Srcs) != 1 { + ctx.PropertyErrorf("srcs", "must only specify one directory path, contains %d paths", len(p.properties.Srcs)) + return + } + + localSrcDir := p.properties.Srcs[0] + // Although PathForModuleSrc can return nil if either the path doesn't exist or + // the path components are invalid it won't in this case because no components + // are specified and the module directory must exist in order to get this far. + srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, localSrcDir) + + // Glob the contents of the directory just in case the directory does not exist. + srcGlob := localSrcDir + "/**/*" + srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob}) - rule := android.NewRuleBuilder() - command := rule.Command(). - BuiltTool(ctx, "soong_zip"). + rule := android.NewRuleBuilder(pctx, ctx) + rule.Command(). + BuiltTool("soong_zip"). Flag("-write_if_changed"). Flag("-jar"). - FlagWithOutput("-o ", p.stubsSrcJar) - - for _, d := range p.srcDirs { - dir := d.String() - command. - FlagWithArg("-C ", dir). - FlagWithInput("-D ", d) - } + FlagWithOutput("-o ", p.stubsSrcJar). + FlagWithArg("-C ", srcDir.String()). + FlagWithRspFileInputList("-r ", srcPaths) rule.Restat() - rule.Build(pctx, ctx, "zip src", "Create srcjar from prebuilt source") + rule.Build("zip src", "Create srcjar from prebuilt source") } func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt { diff --git a/java/gen.go b/java/gen.go index d50a6653e..5766a9471 100644 --- a/java/gen.go +++ b/java/gen.go @@ -57,7 +57,7 @@ func genAidl(ctx android.ModuleContext, aidlFiles android.Paths, aidlFlags strin outDir := srcJarFile.ReplaceExtension(ctx, "tmp") - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) rule.Command().Text("rm -rf").Flag(outDir.String()) rule.Command().Text("mkdir -p").Flag(outDir.String()) @@ -98,7 +98,7 @@ func genAidl(ctx android.ModuleContext, aidlFiles android.Paths, aidlFlags strin ruleDesc += " " + strconv.Itoa(i) } - rule.Build(pctx, ctx, ruleName, ruleDesc) + rule.Build(ruleName, ruleDesc) } return srcJarFiles diff --git a/java/hiddenapi.go b/java/hiddenapi.go index 63b801a5c..71f1e576d 100644 --- a/java/hiddenapi.go +++ b/java/hiddenapi.go @@ -135,12 +135,12 @@ func (h *hiddenAPI) hiddenAPIGenerateCSV(ctx android.ModuleContext, flagsCSV, me }) h.metadataCSVPath = metadataCSV - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) rule.Command(). - BuiltTool(ctx, "merge_csv"). + BuiltTool("merge_csv"). FlagWithInput("--zip_input=", classesJar). FlagWithOutput("--output=", indexCSV) - rule.Build(pctx, ctx, "merged-hiddenapi-index", "Merged Hidden API index") + rule.Build("merged-hiddenapi-index", "Merged Hidden API index") h.indexCSVPath = indexCSV } diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go index 1f80e777f..419dc3424 100644 --- a/java/hiddenapi_singleton.go +++ b/java/hiddenapi_singleton.go @@ -177,19 +177,20 @@ func stubFlagsRule(ctx android.SingletonContext) { for moduleList, pathList := range moduleListToPathList { for i := range pathList { if pathList[i] == nil { - pathList[i] = android.PathForOutput(ctx, "missing") + moduleName := (*moduleList)[i] + pathList[i] = android.PathForOutput(ctx, "missing/module", moduleName) if ctx.Config().AllowMissingDependencies() { - missingDeps = append(missingDeps, (*moduleList)[i]) + missingDeps = append(missingDeps, moduleName) } else { ctx.Errorf("failed to find dex jar path for module %q", - (*moduleList)[i]) + moduleName) } } } } // Singleton rule which applies hiddenapi on all boot class path dex files. - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) outputPath := hiddenAPISingletonPaths(ctx).stubFlags tempPath := android.PathForOutput(ctx, outputPath.Rel()+".tmp") @@ -208,7 +209,7 @@ func stubFlagsRule(ctx android.SingletonContext) { commitChangeForRestat(rule, tempPath, outputPath) - rule.Build(pctx, ctx, "hiddenAPIStubFlagsFile", "hiddenapi stub flags") + rule.Build("hiddenAPIStubFlagsFile", "hiddenapi stub flags") } // flagsRule creates a rule to build hiddenapi-flags.csv out of flags.csv files generated for boot image modules and @@ -236,7 +237,7 @@ func flagsRule(ctx android.SingletonContext) android.Path { ctx.Errorf("Failed to find combined-removed-dex.") } - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) outputPath := hiddenAPISingletonPaths(ctx).flags tempPath := android.PathForOutput(ctx, outputPath.Rel()+".tmp") @@ -255,18 +256,18 @@ func flagsRule(ctx android.SingletonContext) android.Path { FlagWithInput("--max-target-p ", android.PathForSource(ctx, "frameworks/base/config/hiddenapi-max-target-p.txt")). FlagWithInput("--max-target-o ", android.PathForSource( - ctx, "frameworks/base/config/hiddenapi-max-target-o.txt")).Flag("--ignore-conflicts "). + ctx, "frameworks/base/config/hiddenapi-max-target-o.txt")).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio"). FlagWithInput("--blocked ", android.PathForSource(ctx, "frameworks/base/config/hiddenapi-force-blocked.txt")). FlagWithInput("--blocked ", - android.PathForSource(ctx, "frameworks/base/config/hiddenapi-temp-blocklist.txt")). + android.PathForSource(ctx, "frameworks/base/config/hiddenapi-temp-blocklist.txt")).FlagWithArg("--tag ", "lo-prio"). FlagWithInput("--unsupported ", android.PathForSource( ctx, "frameworks/base/config/hiddenapi-unsupported-packages.txt")).Flag("--packages "). FlagWithOutput("--output ", tempPath) commitChangeForRestat(rule, tempPath, outputPath) - rule.Build(pctx, ctx, "hiddenAPIFlagsFile", "hiddenapi flags") + rule.Build("hiddenAPIFlagsFile", "hiddenapi flags") return outputPath } @@ -274,14 +275,14 @@ func flagsRule(ctx android.SingletonContext) android.Path { // emptyFlagsRule creates a rule to build an empty hiddenapi-flags.csv, which is needed by master-art-host builds that // have a partial manifest without frameworks/base but still need to build a boot image. func emptyFlagsRule(ctx android.SingletonContext) android.Path { - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) outputPath := hiddenAPISingletonPaths(ctx).flags rule.Command().Text("rm").Flag("-f").Output(outputPath) rule.Command().Text("touch").Output(outputPath) - rule.Build(pctx, ctx, "emptyHiddenAPIFlagsFile", "empty hiddenapi flags") + rule.Build("emptyHiddenAPIFlagsFile", "empty hiddenapi flags") return outputPath } @@ -299,16 +300,16 @@ func metadataRule(ctx android.SingletonContext) android.Path { } }) - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) outputPath := hiddenAPISingletonPaths(ctx).metadata rule.Command(). - BuiltTool(ctx, "merge_csv"). + BuiltTool("merge_csv"). FlagWithOutput("--output=", outputPath). Inputs(metadataCSV) - rule.Build(pctx, ctx, "hiddenAPIGreylistMetadataFile", "hiddenapi greylist metadata") + rule.Build("hiddenAPIGreylistMetadataFile", "hiddenapi greylist metadata") return outputPath } @@ -399,13 +400,13 @@ func (h *hiddenAPIIndexSingleton) GenerateBuildActions(ctx android.SingletonCont } }) - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) rule.Command(). - BuiltTool(ctx, "merge_csv"). + BuiltTool("merge_csv"). FlagWithArg("--header=", "signature,file,startline,startcol,endline,endcol,properties"). FlagWithOutput("--output=", hiddenAPISingletonPaths(ctx).index). Inputs(indexes) - rule.Build(pctx, ctx, "singleton-merged-hiddenapi-index", "Singleton merged Hidden API index") + rule.Build("singleton-merged-hiddenapi-index", "Singleton merged Hidden API index") h.index = hiddenAPISingletonPaths(ctx).index } diff --git a/java/java.go b/java/java.go index d6dc148a6..d44719e99 100644 --- a/java/java.go +++ b/java/java.go @@ -201,7 +201,10 @@ type CompilerProperties struct { // List of modules to use as annotation processors Plugins []string - // List of modules to export to libraries that directly depend on this library as annotation processors + // List of modules to export to libraries that directly depend on this library as annotation + // processors. Note that if the plugins set generates_api: true this will disable the turbine + // optimization on modules that depend on this module, which will reduce parallelism and cause + // more recompilation. Exported_plugins []string // The number of Java source entries each Javac instance can process @@ -248,6 +251,9 @@ type CompilerProperties struct { Errorprone struct { // List of javac flags that should only be used when running errorprone. Javacflags []string + + // List of java_plugin modules that provide extra errorprone checks. + Extra_check_modules []string } Proto struct { @@ -417,7 +423,7 @@ type Module struct { overrideManifest android.OptionalPath // map of SDK version to class loader context - exportedSdkLibs dexpreopt.ClassLoaderContextMap + classLoaderContexts dexpreopt.ClassLoaderContextMap // list of plugins that this java module is exporting exportedPluginJars android.Paths @@ -425,6 +431,9 @@ type Module struct { // list of plugins that this java module is exporting exportedPluginClasses []string + // if true, the exported plugins generate API and require disabling turbine. + exportedDisableTurbine bool + // list of source files, collected from srcFiles with unique java and all kt files, // will be used by android.IDEInfo struct expandIDEInfoCompiledSrcs []string @@ -447,8 +456,6 @@ type Module struct { // list of the xref extraction files kytheFiles android.Paths - distFiles android.TaggedDistFiles - // Collect the module directory for IDE info in java/jdeps.go. modulePaths []string @@ -477,6 +484,8 @@ func (j *Module) OutputFiles(tag string) (android.Paths, error) { switch tag { case "": return append(android.Paths{j.outputFile}, j.extraOutputFiles...), nil + case android.DefaultDistTag: + return android.Paths{j.outputFile}, nil case ".jar": return android.Paths{j.implementationAndResourcesJar}, nil case ".proguard_map": @@ -509,8 +518,8 @@ type Dependency interface { ImplementationJars() android.Paths ResourceJars() android.Paths AidlIncludeDirs() android.Paths - ExportedSdkLibs() dexpreopt.ClassLoaderContextMap - ExportedPlugins() (android.Paths, []string) + ClassLoaderContexts() dexpreopt.ClassLoaderContextMap + ExportedPlugins() (android.Paths, []string, bool) SrcJarArgs() ([]string, android.Paths) BaseModuleName() string JacocoReportClassesFile() android.Path @@ -547,6 +556,14 @@ type dependencyTag struct { name string } +// installDependencyTag is a dependency tag that is annotated to cause the installed files of the +// dependency to be installed when the parent module is installed. +type installDependencyTag struct { + blueprint.BaseDependencyTag + android.InstallAlwaysNeededDependencyTag + name string +} + type usesLibraryDependencyTag struct { dependencyTag sdkVersion int // SDK version in which the library appared as a standalone library. @@ -569,6 +586,7 @@ var ( libTag = dependencyTag{name: "javalib"} java9LibTag = dependencyTag{name: "java9lib"} pluginTag = dependencyTag{name: "plugin"} + errorpronePluginTag = dependencyTag{name: "errorprone-plugin"} exportedPluginTag = dependencyTag{name: "exported-plugin"} bootClasspathTag = dependencyTag{name: "bootclasspath"} systemModulesTag = dependencyTag{name: "system modules"} @@ -580,6 +598,8 @@ var ( instrumentationForTag = dependencyTag{name: "instrumentation_for"} extraLintCheckTag = dependencyTag{name: "extra-lint-check"} jniLibTag = dependencyTag{name: "jnilib"} + jniInstallTag = installDependencyTag{name: "jni install"} + binaryInstallTag = installDependencyTag{name: "binary install"} usesLibTag = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion) usesLibCompat28Tag = makeUsesLibraryDependencyTag(28) usesLibCompat29Tag = makeUsesLibraryDependencyTag(29) @@ -752,6 +772,37 @@ func (j *Module) deps(ctx android.BottomUpMutatorContext) { libDeps := ctx.AddVariationDependencies(nil, libTag, rewriteSyspropLibs(j.properties.Libs, "libs")...) ctx.AddVariationDependencies(nil, staticLibTag, rewriteSyspropLibs(j.properties.Static_libs, "static_libs")...) + if ctx.DeviceConfig().VndkVersion() != "" && ctx.Config().EnforceInterPartitionJavaSdkLibrary() { + // Require java_sdk_library at inter-partition java dependency to ensure stable + // interface between partitions. If inter-partition java_library dependency is detected, + // raise build error because java_library doesn't have a stable interface. + // + // Inputs: + // PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY + // if true, enable enforcement + // PRODUCT_INTER_PARTITION_JAVA_LIBRARY_ALLOWLIST + // exception list of java_library names to allow inter-partition dependency + for idx, lib := range j.properties.Libs { + if libDeps[idx] == nil { + continue + } + + if _, ok := syspropPublicStubs[lib]; ok { + continue + } + + if javaDep, ok := libDeps[idx].(javaSdkLibraryEnforceContext); ok { + // java_sdk_library is always allowed at inter-partition dependency. + // So, skip check. + if _, ok := javaDep.(*SdkLibrary); ok { + continue + } + + j.checkPartitionsForJavaDependency(ctx, "libs", javaDep) + } + } + } + // For library dependencies that are component libraries (like stubs), add the implementation // as a dependency (dexpreopt needs to be against the implementation library, not stubs). for _, dep := range libDeps { @@ -765,6 +816,7 @@ func (j *Module) deps(ctx android.BottomUpMutatorContext) { } ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), pluginTag, j.properties.Plugins...) + ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), errorpronePluginTag, j.properties.Errorprone.Extra_check_modules...) ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), exportedPluginTag, j.properties.Exported_plugins...) android.ProtoDeps(ctx, &j.protoProperties) @@ -852,21 +904,22 @@ func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.Opt } type deps struct { - classpath classpath - java9Classpath classpath - bootClasspath classpath - processorPath classpath - processorClasses []string - staticJars android.Paths - staticHeaderJars android.Paths - staticResourceJars android.Paths - aidlIncludeDirs android.Paths - srcs android.Paths - srcJars android.Paths - systemModules *systemModules - aidlPreprocess android.OptionalPath - kotlinStdlib android.Paths - kotlinAnnotations android.Paths + classpath classpath + java9Classpath classpath + bootClasspath classpath + processorPath classpath + errorProneProcessorPath classpath + processorClasses []string + staticJars android.Paths + staticHeaderJars android.Paths + staticResourceJars android.Paths + aidlIncludeDirs android.Paths + srcs android.Paths + srcJars android.Paths + systemModules *systemModules + aidlPreprocess android.OptionalPath + kotlinStdlib android.Paths + kotlinAnnotations android.Paths disableTurbine bool } @@ -952,7 +1005,9 @@ func checkLinkType(ctx android.ModuleContext, from *Module, to linkTypeContext, return } otherLinkType, _ := to.getLinkType(ctx.OtherModuleName(to)) - commonMessage := "Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source." + commonMessage := " In order to fix this, consider adjusting sdk_version: OR platform_apis: " + + "property of the source or target module so that target module is built with the same " + + "or smaller API set when compared to the source." switch myLinkType { case javaCore: @@ -1026,8 +1081,7 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { switch tag { case libTag: deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...) - // names of sdk libs that are directly depended are exported - j.exportedSdkLibs.MaybeAddContext(ctx, dep.OptionalImplicitSdkLibrary(), + j.classLoaderContexts.MaybeAddContext(ctx, dep.OptionalImplicitSdkLibrary(), dep.DexJarBuildPath(), dep.DexJarInstallPath()) case staticLibTag: ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName) @@ -1038,11 +1092,11 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars()...) case libTag, instrumentationForTag: deps.classpath = append(deps.classpath, dep.HeaderJars()...) - // sdk lib names from dependencies are re-exported - j.exportedSdkLibs.AddContextMap(dep.ExportedSdkLibs(), otherName) + j.classLoaderContexts.AddContextMap(dep.ClassLoaderContexts(), otherName) deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...) - pluginJars, pluginClasses := dep.ExportedPlugins() + pluginJars, pluginClasses, disableTurbine := dep.ExportedPlugins() addPlugins(&deps, pluginJars, pluginClasses...) + deps.disableTurbine = deps.disableTurbine || disableTurbine case java9LibTag: deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars()...) case staticLibTag: @@ -1050,11 +1104,13 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { deps.staticJars = append(deps.staticJars, dep.ImplementationJars()...) deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars()...) deps.staticResourceJars = append(deps.staticResourceJars, dep.ResourceJars()...) - // sdk lib names from dependencies are re-exported - j.exportedSdkLibs.AddContextMap(dep.ExportedSdkLibs(), otherName) deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...) - pluginJars, pluginClasses := dep.ExportedPlugins() + pluginJars, pluginClasses, disableTurbine := dep.ExportedPlugins() addPlugins(&deps, pluginJars, pluginClasses...) + // Turbine doesn't run annotation processors, so any module that uses an + // annotation processor that generates API is incompatible with the turbine + // optimization. + deps.disableTurbine = deps.disableTurbine || disableTurbine case pluginTag: if plugin, ok := dep.(*Plugin); ok { if plugin.pluginProperties.Processor_class != nil { @@ -1062,19 +1118,29 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { } else { addPlugins(&deps, plugin.ImplementationAndResourcesJars()) } + // Turbine doesn't run annotation processors, so any module that uses an + // annotation processor that generates API is incompatible with the turbine + // optimization. deps.disableTurbine = deps.disableTurbine || Bool(plugin.pluginProperties.Generates_api) } else { ctx.PropertyErrorf("plugins", "%q is not a java_plugin module", otherName) } + case errorpronePluginTag: + if plugin, ok := dep.(*Plugin); ok { + deps.errorProneProcessorPath = append(deps.errorProneProcessorPath, plugin.ImplementationAndResourcesJars()...) + } else { + ctx.PropertyErrorf("plugins", "%q is not a java_plugin module", otherName) + } case exportedPluginTag: if plugin, ok := dep.(*Plugin); ok { - if plugin.pluginProperties.Generates_api != nil && *plugin.pluginProperties.Generates_api { - ctx.PropertyErrorf("exported_plugins", "Cannot export plugins with generates_api = true, found %v", otherName) - } j.exportedPluginJars = append(j.exportedPluginJars, plugin.ImplementationAndResourcesJars()...) if plugin.pluginProperties.Processor_class != nil { j.exportedPluginClasses = append(j.exportedPluginClasses, *plugin.pluginProperties.Processor_class) } + // Turbine doesn't run annotation processors, so any module that uses an + // annotation processor that generates API is incompatible with the turbine + // optimization. + j.exportedDisableTurbine = Bool(plugin.pluginProperties.Generates_api) } else { ctx.PropertyErrorf("exported_plugins", "%q is not a java_plugin module", otherName) } @@ -1112,6 +1178,9 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { deps.systemModules = &systemModules{outputDir, outputDeps} } } + + // Merge dep's CLC after processing the dep itself (which may add its own <uses-library>). + maybeAddCLCFromDep(module, tag, otherName, j.classLoaderContexts) }) return deps @@ -1189,7 +1258,7 @@ func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaB flags.javaVersion = getJavaVersion(ctx, String(j.properties.Java_version), sdkContext(j)) if ctx.Config().RunErrorProne() { - if config.ErrorProneClasspath == nil { + if config.ErrorProneClasspath == nil && ctx.Config().TestProductVariables == nil { ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?") } @@ -1209,6 +1278,7 @@ func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaB flags.classpath = append(flags.classpath, deps.classpath...) flags.java9Classpath = append(flags.java9Classpath, deps.java9Classpath...) flags.processorPath = append(flags.processorPath, deps.processorPath...) + flags.errorProneProcessorPath = append(flags.errorProneProcessorPath, deps.errorProneProcessorPath...) flags.processors = append(flags.processors, deps.processorClasses...) flags.processors = android.FirstUniqueStrings(flags.processors) @@ -1375,6 +1445,9 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { kotlincFlags := j.properties.Kotlincflags CheckKotlincFlags(ctx, kotlincFlags) + // Dogfood the JVM_IR backend. + kotlincFlags = append(kotlincFlags, "-Xuse-ir") + // If there are kotlin files, compile them first but pass all the kotlin and java files // kotlinc will use the java files to resolve types referenced by the kotlin files, but // won't emit any classes for them. @@ -1903,12 +1976,15 @@ func (j *Module) AidlIncludeDirs() android.Paths { return j.exportAidlIncludeDirs } -func (j *Module) ExportedSdkLibs() dexpreopt.ClassLoaderContextMap { - return j.exportedSdkLibs +func (j *Module) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap { + return j.classLoaderContexts } -func (j *Module) ExportedPlugins() (android.Paths, []string) { - return j.exportedPluginJars, j.exportedPluginClasses +// ExportedPlugins returns the list of jars needed to run the exported plugins, the list of +// classes for the plugins, and a boolean for whether turbine needs to be disabled due to plugins +// that generate APIs. +func (j *Module) ExportedPlugins() (android.Paths, []string, bool) { + return j.exportedPluginJars, j.exportedPluginClasses, j.exportedDisableTurbine } func (j *Module) SrcJarArgs() ([]string, android.Paths) { @@ -2042,7 +2118,7 @@ func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter)) } j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex - j.exportedSdkLibs = make(dexpreopt.ClassLoaderContextMap) + j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap) j.compile(ctx, nil) // Collect the module directory for IDE info in java/jdeps.go. @@ -2062,15 +2138,13 @@ func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { // add the name of that java_sdk_library to the exported sdk libs to make sure // that, if necessary, a <uses-library> element for that java_sdk_library is // added to the Android manifest. - j.exportedSdkLibs.MaybeAddContext(ctx, j.OptionalImplicitSdkLibrary(), + j.classLoaderContexts.MaybeAddContext(ctx, j.OptionalImplicitSdkLibrary(), j.DexJarBuildPath(), j.DexJarInstallPath()) // A non-SDK library may provide a <uses-library> (the name may be different from the module name). if lib := proptools.String(j.usesLibraryProperties.Provides_uses_lib); lib != "" { - j.exportedSdkLibs.AddContext(ctx, lib, j.DexJarBuildPath(), j.DexJarInstallPath()) + j.classLoaderContexts.AddContext(ctx, lib, j.DexJarBuildPath(), j.DexJarInstallPath()) } - - j.distFiles = j.GenerateTaggedDistFiles(ctx) } func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) { @@ -2226,6 +2300,9 @@ func LibraryHostFactory() android.Module { type TestOptions struct { // a list of extra test configuration files that should be installed with the module. Extra_test_configs []string `android:"path,arch_variant"` + + // If the test is a hostside(no device required) unittest that shall be run during presubmit check. + Unit_test *bool } type testProperties struct { @@ -2322,7 +2399,7 @@ func (j *TestHost) DepsMutator(ctx android.BottomUpMutatorContext) { func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template, - j.testProperties.Test_suites, j.testProperties.Auto_gen_config) + j.testProperties.Test_suites, j.testProperties.Auto_gen_config, j.testProperties.Test_options.Unit_test) j.data = android.PathsForModuleSrc(ctx, j.testProperties.Data) @@ -2341,7 +2418,7 @@ func (j *TestHelperLibrary) GenerateAndroidBuildActions(ctx android.ModuleContex func (j *JavaTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.prebuiltTestProperties.Test_config, nil, - j.prebuiltTestProperties.Test_suites, nil) + j.prebuiltTestProperties.Test_suites, nil, nil) j.Import.GenerateAndroidBuildActions(ctx) } @@ -2550,9 +2627,12 @@ func (j *Binary) DepsMutator(ctx android.BottomUpMutatorContext) { if ctx.Arch().ArchType == android.Common { j.deps(ctx) } else { - // This dependency ensures the host installation rules will install the jni libraries - // when the wrapper is installed. - ctx.AddVariationDependencies(nil, jniLibTag, j.binaryProperties.Jni_libs...) + // These dependencies ensure the host installation rules will install the jar file and + // the jni libraries when the wrapper is installed. + ctx.AddVariationDependencies(nil, jniInstallTag, j.binaryProperties.Jni_libs...) + ctx.AddVariationDependencies( + []blueprint.Variation{{Mutator: "arch", Variation: android.CommonArch.String()}}, + binaryInstallTag, ctx.ModuleName()) } } @@ -2646,7 +2726,7 @@ type Import struct { dexJarFile android.Path combinedClasspathFile android.Path - exportedSdkLibs dexpreopt.ClassLoaderContextMap + classLoaderContexts dexpreopt.ClassLoaderContextMap exportAidlIncludeDirs android.Paths hideApexVariantFromMake bool @@ -2721,7 +2801,7 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { TransformJetifier(ctx, outputFile, inputFile) } j.combinedClasspathFile = outputFile - j.exportedSdkLibs = make(dexpreopt.ClassLoaderContextMap) + j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap) var flags javaBuilderFlags @@ -2734,8 +2814,6 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { switch tag { case libTag, staticLibTag: flags.classpath = append(flags.classpath, dep.HeaderJars()...) - // sdk lib names from dependencies are re-exported - j.exportedSdkLibs.AddContextMap(dep.ExportedSdkLibs(), otherName) case bootClasspathTag: flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars()...) } @@ -2743,11 +2821,12 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { switch tag { case libTag: flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...) - // names of sdk libs that are directly depended are exported - j.exportedSdkLibs.AddContext(ctx, otherName, - dep.DexJarBuildPath(), dep.DexJarInstallPath()) + j.classLoaderContexts.AddContext(ctx, otherName, dep.DexJarBuildPath(), dep.DexJarInstallPath()) } } + + // Merge dep's CLC after processing the dep itself (which may add its own <uses-library>). + maybeAddCLCFromDep(module, tag, otherName, j.classLoaderContexts) }) var installFile android.Path @@ -2760,7 +2839,7 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { // add the name of that java_sdk_library to the exported sdk libs to make sure // that, if necessary, a <uses-library> element for that java_sdk_library is // added to the Android manifest. - j.exportedSdkLibs.MaybeAddContext(ctx, j.OptionalImplicitSdkLibrary(), + j.classLoaderContexts.MaybeAddContext(ctx, j.OptionalImplicitSdkLibrary(), outputFile, installFile) j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs) @@ -2843,12 +2922,12 @@ func (j *Import) AidlIncludeDirs() android.Paths { return j.exportAidlIncludeDirs } -func (j *Import) ExportedSdkLibs() dexpreopt.ClassLoaderContextMap { - return j.exportedSdkLibs +func (j *Import) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap { + return j.classLoaderContexts } -func (j *Import) ExportedPlugins() (android.Paths, []string) { - return nil, nil +func (j *Import) ExportedPlugins() (android.Paths, []string, bool) { + return nil, nil, false } func (j *Import) SrcJarArgs() ([]string, android.Paths) { @@ -3003,21 +3082,21 @@ func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { dexOutputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".jar") if j.dexpreopter.uncompressedDex { - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) temporary := android.PathForModuleOut(ctx, ctx.ModuleName()+".jar.unaligned") rule.Temporary(temporary) // use zip2zip to uncompress classes*.dex files rule.Command(). - BuiltTool(ctx, "zip2zip"). + BuiltTool("zip2zip"). FlagWithInput("-i ", inputJar). FlagWithOutput("-o ", temporary). FlagWithArg("-0 ", "'classes*.dex'") // use zipalign to align uncompressed classes*.dex files rule.Command(). - BuiltTool(ctx, "zipalign"). + BuiltTool("zipalign"). Flag("-f"). Text("4"). Input(temporary). @@ -3025,7 +3104,7 @@ func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { rule.DeleteTemporaryFiles() - rule.Build(pctx, ctx, "uncompress_dex", "uncompress dex") + rule.Build("uncompress_dex", "uncompress dex") } else { ctx.Build(pctx, android.BuildParams{ Rule: android.Cp, @@ -3168,3 +3247,31 @@ var Bool = proptools.Bool var BoolDefault = proptools.BoolDefault var String = proptools.String var inList = android.InList + +// Add class loader context of a given dependency to the given class loader context, provided that +// all the necessary conditions are met. +func maybeAddCLCFromDep(depModule android.Module, depTag blueprint.DependencyTag, + depName string, clcMap dexpreopt.ClassLoaderContextMap) { + + if dep, ok := depModule.(Dependency); ok { + if depTag == libTag { + // Ok, propagate <uses-library> through non-static library dependencies. + } else if depTag == staticLibTag { + // Propagate <uses-library> through static library dependencies, unless it is a + // component library (such as stubs). Component libraries have a dependency on their + // SDK library, which should not be pulled just because of a static component library. + if comp, isComp := depModule.(SdkLibraryComponentDependency); isComp { + if compName := comp.OptionalImplicitSdkLibrary(); compName != nil { + dep = nil + } + } + } else { + // Don't propagate <uses-library> for other dependency tags. + dep = nil + } + + if dep != nil { + clcMap.AddContextMap(dep.ClassLoaderContexts(), depName) + } + } +} diff --git a/java/java_test.go b/java/java_test.go index 4594b8111..f7cf03f4d 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -15,6 +15,7 @@ package java import ( + "fmt" "io/ioutil" "os" "path/filepath" @@ -29,7 +30,6 @@ import ( "android/soong/android" "android/soong/cc" "android/soong/dexpreopt" - "android/soong/genrule" "android/soong/python" ) @@ -80,7 +80,6 @@ func testContext(config android.Config) *android.TestContext { RegisterSystemModulesBuildComponents(ctx) ctx.RegisterModuleType("java_plugin", PluginFactory) ctx.RegisterModuleType("filegroup", android.FileGroupFactory) - ctx.RegisterModuleType("genrule", genrule.GenRuleFactory) ctx.RegisterModuleType("python_binary_host", python.PythonBinaryHostFactory) RegisterDocsBuildComponents(ctx) RegisterStubsBuildComponents(ctx) @@ -92,8 +91,8 @@ func testContext(config android.Config) *android.TestContext { ctx.PreDepsMutators(python.RegisterPythonPreDepsMutators) ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators) - ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(OverlaySingletonFactory)) - ctx.RegisterPreSingletonType("sdk_versions", android.SingletonFactoryAdaptor(sdkPreSingletonFactory)) + ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(ctx.Context, OverlaySingletonFactory)) + ctx.RegisterPreSingletonType("sdk_versions", android.SingletonFactoryAdaptor(ctx.Context, sdkPreSingletonFactory)) android.RegisterPrebuiltMutators(ctx) @@ -202,7 +201,7 @@ func TestJavaLinkType(t *testing.T) { } `) - testJavaError(t, "Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source.", ` + testJavaError(t, "consider adjusting sdk_version: OR platform_apis:", ` java_library { name: "foo", srcs: ["a.java"], @@ -246,7 +245,7 @@ func TestJavaLinkType(t *testing.T) { } `) - testJavaError(t, "Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source.", ` + testJavaError(t, "consider adjusting sdk_version: OR platform_apis:", ` java_library { name: "foo", srcs: ["a.java"], @@ -314,8 +313,9 @@ func TestSimple(t *testing.T) { func TestExportedPlugins(t *testing.T) { type Result struct { - library string - processors string + library string + processors string + disableTurbine bool } var tests = []struct { name string @@ -374,6 +374,18 @@ func TestExportedPlugins(t *testing.T) { {library: "foo", processors: "-processor com.android.TestPlugin,com.android.TestPlugin2"}, }, }, + { + name: "Exports plugin to with generates_api to dependee", + extra: ` + java_library{name: "exports", exported_plugins: ["plugin_generates_api"]} + java_library{name: "foo", srcs: ["a.java"], libs: ["exports"]} + java_library{name: "bar", srcs: ["a.java"], static_libs: ["exports"]} + `, + results: []Result{ + {library: "foo", processors: "-processor com.android.TestPlugin", disableTurbine: true}, + {library: "bar", processors: "-processor com.android.TestPlugin", disableTurbine: true}, + }, + }, } for _, test := range tests { @@ -383,6 +395,11 @@ func TestExportedPlugins(t *testing.T) { name: "plugin", processor_class: "com.android.TestPlugin", } + java_plugin { + name: "plugin_generates_api", + generates_api: true, + processor_class: "com.android.TestPlugin", + } `+test.extra) for _, want := range test.results { @@ -390,6 +407,11 @@ func TestExportedPlugins(t *testing.T) { if javac.Args["processor"] != want.processors { t.Errorf("For library %v, expected %v, found %v", want.library, want.processors, javac.Args["processor"]) } + turbine := ctx.ModuleForTests(want.library, "android_common").MaybeRule("turbine") + disableTurbine := turbine.BuildParams.Rule == nil + if disableTurbine != want.disableTurbine { + t.Errorf("For library %v, expected disableTurbine %v, found %v", want.library, want.disableTurbine, disableTurbine) + } } }) } @@ -622,6 +644,35 @@ func assertDeepEquals(t *testing.T, message string, expected interface{}, actual } } +func TestPrebuiltStubsSources(t *testing.T) { + test := func(t *testing.T, sourcesPath string, expectedInputs []string) { + ctx, _ := testJavaWithFS(t, fmt.Sprintf(` +prebuilt_stubs_sources { + name: "stubs-source", + srcs: ["%s"], +}`, sourcesPath), map[string][]byte{ + "stubs/sources/pkg/A.java": nil, + "stubs/sources/pkg/B.java": nil, + }) + + zipSrc := ctx.ModuleForTests("stubs-source", "android_common").Rule("zip_src") + if expected, actual := expectedInputs, zipSrc.Inputs.Strings(); !reflect.DeepEqual(expected, actual) { + t.Errorf("mismatch of inputs to soong_zip: expected %q, actual %q", expected, actual) + } + } + + t.Run("empty/missing directory", func(t *testing.T) { + test(t, "empty-directory", []string{}) + }) + + t.Run("non-empty set of sources", func(t *testing.T) { + test(t, "stubs/sources", []string{ + "stubs/sources/pkg/A.java", + "stubs/sources/pkg/B.java", + }) + }) +} + func TestJavaSdkLibraryImport(t *testing.T) { ctx, _ := testJava(t, ` java_library { @@ -754,6 +805,165 @@ func TestJavaSdkLibraryImport_Preferred(t *testing.T) { }) } +func TestJavaSdkLibraryEnforce(t *testing.T) { + partitionToBpOption := func(partition string) string { + switch partition { + case "system": + return "" + case "vendor": + return "soc_specific: true," + case "product": + return "product_specific: true," + default: + panic("Invalid partition group name: " + partition) + } + } + + type testConfigInfo struct { + libraryType string + fromPartition string + toPartition string + enforceVendorInterface bool + enforceProductInterface bool + enforceJavaSdkLibraryCheck bool + allowList []string + } + + createTestConfig := func(info testConfigInfo) android.Config { + bpFileTemplate := ` + java_library { + name: "foo", + srcs: ["foo.java"], + libs: ["bar"], + sdk_version: "current", + %s + } + + %s { + name: "bar", + srcs: ["bar.java"], + sdk_version: "current", + %s + } + ` + + bpFile := fmt.Sprintf(bpFileTemplate, + partitionToBpOption(info.fromPartition), + info.libraryType, + partitionToBpOption(info.toPartition)) + + config := testConfig(nil, bpFile, nil) + configVariables := config.TestProductVariables + + configVariables.EnforceProductPartitionInterface = proptools.BoolPtr(info.enforceProductInterface) + if info.enforceVendorInterface { + configVariables.DeviceVndkVersion = proptools.StringPtr("current") + } + configVariables.EnforceInterPartitionJavaSdkLibrary = proptools.BoolPtr(info.enforceJavaSdkLibraryCheck) + configVariables.InterPartitionJavaLibraryAllowList = info.allowList + + return config + } + + isValidDependency := func(configInfo testConfigInfo) bool { + if configInfo.enforceVendorInterface == false { + return true + } + + if configInfo.enforceJavaSdkLibraryCheck == false { + return true + } + + if inList("bar", configInfo.allowList) { + return true + } + + if configInfo.libraryType == "java_library" { + if configInfo.fromPartition != configInfo.toPartition { + if !configInfo.enforceProductInterface && + ((configInfo.fromPartition == "system" && configInfo.toPartition == "product") || + (configInfo.fromPartition == "product" && configInfo.toPartition == "system")) { + return true + } + return false + } + } + + return true + } + + errorMessage := "is not allowed across the partitions" + + allPartitionCombinations := func() [][2]string { + var result [][2]string + partitions := []string{"system", "vendor", "product"} + + for _, fromPartition := range partitions { + for _, toPartition := range partitions { + result = append(result, [2]string{fromPartition, toPartition}) + } + } + + return result + } + + allFlagCombinations := func() [][3]bool { + var result [][3]bool + flagValues := [2]bool{false, true} + + for _, vendorInterface := range flagValues { + for _, productInterface := range flagValues { + for _, enableEnforce := range flagValues { + result = append(result, [3]bool{vendorInterface, productInterface, enableEnforce}) + } + } + } + + return result + } + + for _, libraryType := range []string{"java_library", "java_sdk_library"} { + for _, partitionValues := range allPartitionCombinations() { + for _, flagValues := range allFlagCombinations() { + testInfo := testConfigInfo{ + libraryType: libraryType, + fromPartition: partitionValues[0], + toPartition: partitionValues[1], + enforceVendorInterface: flagValues[0], + enforceProductInterface: flagValues[1], + enforceJavaSdkLibraryCheck: flagValues[2], + } + + if isValidDependency(testInfo) { + testJavaWithConfig(t, createTestConfig(testInfo)) + } else { + testJavaErrorWithConfig(t, errorMessage, createTestConfig(testInfo)) + } + } + } + } + + testJavaWithConfig(t, createTestConfig(testConfigInfo{ + libraryType: "java_library", + fromPartition: "vendor", + toPartition: "system", + enforceVendorInterface: true, + enforceProductInterface: true, + enforceJavaSdkLibraryCheck: true, + allowList: []string{"bar"}, + })) + + testJavaErrorWithConfig(t, errorMessage, createTestConfig(testConfigInfo{ + libraryType: "java_library", + fromPartition: "vendor", + toPartition: "system", + enforceVendorInterface: true, + enforceProductInterface: true, + enforceJavaSdkLibraryCheck: true, + allowList: []string{"foo"}, + })) +} + func TestDefaults(t *testing.T) { ctx, _ := testJava(t, ` java_defaults { @@ -1379,8 +1589,8 @@ func TestJarGenrules(t *testing.T) { baz := ctx.ModuleForTests("baz", "android_common").Output("javac/baz.jar") barCombined := ctx.ModuleForTests("bar", "android_common").Output("combined/bar.jar") - if len(jargen.Inputs) != 1 || jargen.Inputs[0].String() != foo.Output.String() { - t.Errorf("expected jargen inputs [%q], got %q", foo.Output.String(), jargen.Inputs.Strings()) + if g, w := jargen.Implicits.Strings(), foo.Output.String(); !android.InList(w, g) { + t.Errorf("expected jargen inputs [%q], got %q", w, g) } if !strings.Contains(bar.Args["classpath"], jargen.Output.String()) { @@ -1593,7 +1803,7 @@ func TestJavaSdkLibrary(t *testing.T) { // test if baz has exported SDK lib names foo and bar to qux qux := ctx.ModuleForTests("qux", "android_common") if quxLib, ok := qux.Module().(*Library); ok { - sdkLibs := quxLib.ExportedSdkLibs().UsesLibs() + sdkLibs := quxLib.ClassLoaderContexts().UsesLibs() if w := []string{"foo", "bar", "fred", "quuz"}; !reflect.DeepEqual(w, sdkLibs) { t.Errorf("qux should export %q but exports %q", w, sdkLibs) } diff --git a/java/kotlin.go b/java/kotlin.go index e8c030aa7..8067ad521 100644 --- a/java/kotlin.go +++ b/java/kotlin.go @@ -63,9 +63,9 @@ func kotlinCommonSrcsList(ctx android.ModuleContext, commonSrcFiles android.Path // we can't use the rsp file because it is already being used for srcs. // Insert a second rule to write out the list of resources to a file. commonSrcsList := android.PathForModuleOut(ctx, "kotlinc_common_srcs.list") - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) rule.Command().Text("cp").FlagWithRspFileInputList("", commonSrcFiles).Output(commonSrcsList) - rule.Build(pctx, ctx, "kotlin_common_srcs_list", "kotlin common_srcs list") + rule.Build("kotlin_common_srcs_list", "kotlin common_srcs list") return android.OptionalPathForPath(commonSrcsList) } return android.OptionalPath{} diff --git a/java/kotlin_test.go b/java/kotlin_test.go index 60ca1c476..77ef29456 100644 --- a/java/kotlin_test.go +++ b/java/kotlin_test.go @@ -84,11 +84,14 @@ func TestKotlin(t *testing.T) { } func TestKapt(t *testing.T) { - ctx, _ := testJava(t, ` + bp := ` java_library { name: "foo", srcs: ["a.java", "b.kt"], plugins: ["bar", "baz"], + errorprone: { + extra_check_modules: ["my_check"], + }, } java_plugin { @@ -102,64 +105,119 @@ func TestKapt(t *testing.T) { processor_class: "com.baz", srcs: ["b.java"], } - `) - buildOS := android.BuildOs.String() + java_plugin { + name: "my_check", + srcs: ["b.java"], + } + ` + t.Run("", func(t *testing.T) { + ctx, _ := testJava(t, bp) - kapt := ctx.ModuleForTests("foo", "android_common").Rule("kapt") - kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc") - javac := ctx.ModuleForTests("foo", "android_common").Rule("javac") + buildOS := android.BuildOs.String() - bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String() - baz := ctx.ModuleForTests("baz", buildOS+"_common").Rule("javac").Output.String() + kapt := ctx.ModuleForTests("foo", "android_common").Rule("kapt") + kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc") + javac := ctx.ModuleForTests("foo", "android_common").Rule("javac") - // Test that the kotlin and java sources are passed to kapt and kotlinc - if len(kapt.Inputs) != 2 || kapt.Inputs[0].String() != "a.java" || kapt.Inputs[1].String() != "b.kt" { - t.Errorf(`foo kapt inputs %v != ["a.java", "b.kt"]`, kapt.Inputs) - } - if len(kotlinc.Inputs) != 2 || kotlinc.Inputs[0].String() != "a.java" || kotlinc.Inputs[1].String() != "b.kt" { - t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, kotlinc.Inputs) - } + bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String() + baz := ctx.ModuleForTests("baz", buildOS+"_common").Rule("javac").Output.String() - // Test that only the java sources are passed to javac - if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" { - t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs) - } + // Test that the kotlin and java sources are passed to kapt and kotlinc + if len(kapt.Inputs) != 2 || kapt.Inputs[0].String() != "a.java" || kapt.Inputs[1].String() != "b.kt" { + t.Errorf(`foo kapt inputs %v != ["a.java", "b.kt"]`, kapt.Inputs) + } + if len(kotlinc.Inputs) != 2 || kotlinc.Inputs[0].String() != "a.java" || kotlinc.Inputs[1].String() != "b.kt" { + t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, kotlinc.Inputs) + } - // Test that the kapt srcjar is a dependency of kotlinc and javac rules - if !inList(kapt.Output.String(), kotlinc.Implicits.Strings()) { - t.Errorf("expected %q in kotlinc implicits %v", kapt.Output.String(), kotlinc.Implicits.Strings()) - } - if !inList(kapt.Output.String(), javac.Implicits.Strings()) { - t.Errorf("expected %q in javac implicits %v", kapt.Output.String(), javac.Implicits.Strings()) - } + // Test that only the java sources are passed to javac + if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" { + t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs) + } - // Test that the kapt srcjar is extracted by the kotlinc and javac rules - if kotlinc.Args["srcJars"] != kapt.Output.String() { - t.Errorf("expected %q in kotlinc srcjars %v", kapt.Output.String(), kotlinc.Args["srcJars"]) - } - if javac.Args["srcJars"] != kapt.Output.String() { - t.Errorf("expected %q in javac srcjars %v", kapt.Output.String(), kotlinc.Args["srcJars"]) - } + // Test that the kapt srcjar is a dependency of kotlinc and javac rules + if !inList(kapt.Output.String(), kotlinc.Implicits.Strings()) { + t.Errorf("expected %q in kotlinc implicits %v", kapt.Output.String(), kotlinc.Implicits.Strings()) + } + if !inList(kapt.Output.String(), javac.Implicits.Strings()) { + t.Errorf("expected %q in javac implicits %v", kapt.Output.String(), javac.Implicits.Strings()) + } - // Test that the processors are passed to kapt - expectedProcessorPath := "-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + bar + - " -P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + baz - if kapt.Args["kaptProcessorPath"] != expectedProcessorPath { - t.Errorf("expected kaptProcessorPath %q, got %q", expectedProcessorPath, kapt.Args["kaptProcessorPath"]) - } - expectedProcessor := "-P plugin:org.jetbrains.kotlin.kapt3:processors=com.bar -P plugin:org.jetbrains.kotlin.kapt3:processors=com.baz" - if kapt.Args["kaptProcessor"] != expectedProcessor { - t.Errorf("expected kaptProcessor %q, got %q", expectedProcessor, kapt.Args["kaptProcessor"]) - } + // Test that the kapt srcjar is extracted by the kotlinc and javac rules + if kotlinc.Args["srcJars"] != kapt.Output.String() { + t.Errorf("expected %q in kotlinc srcjars %v", kapt.Output.String(), kotlinc.Args["srcJars"]) + } + if javac.Args["srcJars"] != kapt.Output.String() { + t.Errorf("expected %q in javac srcjars %v", kapt.Output.String(), kotlinc.Args["srcJars"]) + } - // Test that the processors are not passed to javac - if javac.Args["processorPath"] != "" { - t.Errorf("expected processorPath '', got %q", javac.Args["processorPath"]) - } - if javac.Args["processor"] != "-proc:none" { - t.Errorf("expected processor '-proc:none', got %q", javac.Args["processor"]) - } + // Test that the processors are passed to kapt + expectedProcessorPath := "-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + bar + + " -P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + baz + if kapt.Args["kaptProcessorPath"] != expectedProcessorPath { + t.Errorf("expected kaptProcessorPath %q, got %q", expectedProcessorPath, kapt.Args["kaptProcessorPath"]) + } + expectedProcessor := "-P plugin:org.jetbrains.kotlin.kapt3:processors=com.bar -P plugin:org.jetbrains.kotlin.kapt3:processors=com.baz" + if kapt.Args["kaptProcessor"] != expectedProcessor { + t.Errorf("expected kaptProcessor %q, got %q", expectedProcessor, kapt.Args["kaptProcessor"]) + } + + // Test that the processors are not passed to javac + if javac.Args["processorpath"] != "" { + t.Errorf("expected processorPath '', got %q", javac.Args["processorpath"]) + } + if javac.Args["processor"] != "-proc:none" { + t.Errorf("expected processor '-proc:none', got %q", javac.Args["processor"]) + } + }) + + t.Run("errorprone", func(t *testing.T) { + env := map[string]string{ + "RUN_ERROR_PRONE": "true", + } + config := testConfig(env, bp, nil) + ctx, _ := testJavaWithConfig(t, config) + + buildOS := android.BuildOs.String() + + kapt := ctx.ModuleForTests("foo", "android_common").Rule("kapt") + //kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc") + javac := ctx.ModuleForTests("foo", "android_common").Description("javac") + errorprone := ctx.ModuleForTests("foo", "android_common").Description("errorprone") + + bar := ctx.ModuleForTests("bar", buildOS+"_common").Description("javac").Output.String() + baz := ctx.ModuleForTests("baz", buildOS+"_common").Description("javac").Output.String() + myCheck := ctx.ModuleForTests("my_check", buildOS+"_common").Description("javac").Output.String() + + // Test that the errorprone plugins are not passed to kapt + expectedProcessorPath := "-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + bar + + " -P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + baz + if kapt.Args["kaptProcessorPath"] != expectedProcessorPath { + t.Errorf("expected kaptProcessorPath %q, got %q", expectedProcessorPath, kapt.Args["kaptProcessorPath"]) + } + expectedProcessor := "-P plugin:org.jetbrains.kotlin.kapt3:processors=com.bar -P plugin:org.jetbrains.kotlin.kapt3:processors=com.baz" + if kapt.Args["kaptProcessor"] != expectedProcessor { + t.Errorf("expected kaptProcessor %q, got %q", expectedProcessor, kapt.Args["kaptProcessor"]) + } + + // Test that the errorprone plugins are not passed to javac + if javac.Args["processorpath"] != "" { + t.Errorf("expected processorPath '', got %q", javac.Args["processorpath"]) + } + if javac.Args["processor"] != "-proc:none" { + t.Errorf("expected processor '-proc:none', got %q", javac.Args["processor"]) + } + + // Test that the errorprone plugins are passed to errorprone + expectedProcessorPath = "-processorpath " + myCheck + if errorprone.Args["processorpath"] != expectedProcessorPath { + t.Errorf("expected processorpath %q, got %q", expectedProcessorPath, errorprone.Args["processorpath"]) + } + if errorprone.Args["processor"] != "-proc:none" { + t.Errorf("expected processor '-proc:none', got %q", errorprone.Args["processor"]) + } + }) } func TestKaptEncodeFlags(t *testing.T) { diff --git a/java/lint.go b/java/lint.go index 3df582f6e..cd2a904d6 100644 --- a/java/lint.go +++ b/java/lint.go @@ -176,9 +176,9 @@ func (l *linter) writeLintProjectXML(ctx android.ModuleContext, // we can't use the rsp file because it is already being used for srcs. // Insert a second rule to write out the list of resources to a file. resourcesList = android.PathForModuleOut(ctx, "lint", "resources.list") - resListRule := android.NewRuleBuilder() + resListRule := android.NewRuleBuilder(pctx, ctx) resListRule.Command().Text("cp").FlagWithRspFileInputList("", l.resources).Output(resourcesList) - resListRule.Build(pctx, ctx, "lint_resources_list", "lint resources list") + resListRule.Build("lint_resources_list", "lint resources list") deps = append(deps, l.resources...) } @@ -192,7 +192,7 @@ func (l *linter) writeLintProjectXML(ctx android.ModuleContext, srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars) cmd := rule.Command(). - BuiltTool(ctx, "lint-project-xml"). + BuiltTool("lint-project-xml"). FlagWithOutput("--project_out ", projectXMLPath). FlagWithOutput("--config_out ", configXMLPath). FlagWithArg("--name ", ctx.ModuleName()) @@ -284,7 +284,7 @@ func (l *linter) lint(ctx android.ModuleContext) { } } - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) if l.manifest == nil { manifest := l.generateManifest(ctx, rule) @@ -347,7 +347,7 @@ func (l *linter) lint(ctx android.ModuleContext) { rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String()) - rule.Build(pctx, ctx, "lint", "lint") + rule.Build("lint", "lint") l.outputs = lintOutputs{ html: html, @@ -447,7 +447,7 @@ func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) { var outputs []*lintOutputs var dirs []string ctx.VisitAllModules(func(m android.Module) { - if ctx.Config().EmbeddedInMake() && !m.ExportedToMake() { + if ctx.Config().KatiEnabled() && !m.ExportedToMake() { return } @@ -511,12 +511,12 @@ func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android return paths[i].String() < paths[j].String() }) - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) - rule.Command().BuiltTool(ctx, "soong_zip"). + rule.Command().BuiltTool("soong_zip"). FlagWithOutput("-o ", outputPath). FlagWithArg("-C ", android.PathForIntermediates(ctx).String()). FlagWithRspFileInputList("-r ", paths) - rule.Build(pctx, ctx, outputPath.Base(), outputPath.Base()) + rule.Build(outputPath.Base(), outputPath.Base()) } diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go index cb8e6841a..9bc821dfe 100644 --- a/java/platform_compat_config.go +++ b/java/platform_compat_config.go @@ -86,15 +86,15 @@ func (p *platformCompatConfigSingleton) GenerateBuildActions(ctx android.Singlet return } - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) outputPath := platformCompatConfigPath(ctx) rule.Command(). - BuiltTool(ctx, "process-compat-config"). + BuiltTool("process-compat-config"). FlagForEachInput("--xml ", compatConfigMetadata). FlagWithOutput("--merged-config ", outputPath) - rule.Build(pctx, ctx, "merged-compat-config", "Merge compat config") + rule.Build("merged-compat-config", "Merge compat config") p.metadata = outputPath } @@ -106,7 +106,7 @@ func (p *platformCompatConfigSingleton) MakeVars(ctx android.MakeVarsContext) { } func (p *platformCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) { - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) configFileName := p.Name() + ".xml" metadataFileName := p.Name() + "_meta.xml" @@ -115,13 +115,13 @@ func (p *platformCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleCon path := android.PathForModuleSrc(ctx, String(p.properties.Src)) rule.Command(). - BuiltTool(ctx, "process-compat-config"). + BuiltTool("process-compat-config"). FlagWithInput("--jar ", path). FlagWithOutput("--device-config ", p.configFile). FlagWithOutput("--merged-config ", p.metadataFile) p.installDirPath = android.PathForModuleInstall(ctx, "etc", "compatconfig") - rule.Build(pctx, ctx, configFileName, "Extract compat/compat_config.xml and install it") + rule.Build(configFileName, "Extract compat/compat_config.xml and install it") } diff --git a/java/proto.go b/java/proto.go index 4d735ebbe..cc9abbeee 100644 --- a/java/proto.go +++ b/java/proto.go @@ -34,7 +34,7 @@ func genProto(ctx android.ModuleContext, protoFiles android.Paths, flags android outDir := srcJarFile.ReplaceExtension(ctx, "tmp") - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) rule.Command().Text("rm -rf").Flag(outDir.String()) rule.Command().Text("mkdir -p").Flag(outDir.String()) @@ -42,13 +42,13 @@ func genProto(ctx android.ModuleContext, protoFiles android.Paths, flags android for _, protoFile := range shard { depFile := srcJarFile.InSameDir(ctx, protoFile.String()+".d") rule.Command().Text("mkdir -p").Flag(filepath.Dir(depFile.String())) - android.ProtoRule(ctx, rule, protoFile, flags, flags.Deps, outDir, depFile, nil) + android.ProtoRule(rule, protoFile, flags, flags.Deps, outDir, depFile, nil) } // Proto generated java files have an unknown package name in the path, so package the entire output directory // into a srcjar. rule.Command(). - BuiltTool(ctx, "soong_zip"). + BuiltTool("soong_zip"). Flag("-jar"). Flag("-write_if_changed"). FlagWithOutput("-o ", srcJarFile). @@ -66,7 +66,7 @@ func genProto(ctx android.ModuleContext, protoFiles android.Paths, flags android ruleDesc += " " + strconv.Itoa(i) } - rule.Build(pctx, ctx, ruleName, ruleDesc) + rule.Build(ruleName, ruleDesc) } return srcJarFiles diff --git a/java/robolectric.go b/java/robolectric.go index 04fc11722..419efda88 100644 --- a/java/robolectric.go +++ b/java/robolectric.go @@ -199,7 +199,7 @@ func (r *robolectricTest) GenerateAndroidBuildActions(ctx android.ModuleContext) func generateRoboTestConfig(ctx android.ModuleContext, outputFile android.WritablePath, instrumentedApp *AndroidApp) { - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) manifest := instrumentedApp.mergedManifestFile resourceApk := instrumentedApp.outputFile @@ -213,11 +213,11 @@ func generateRoboTestConfig(ctx android.ModuleContext, outputFile android.Writab Implicit(manifest). Implicit(resourceApk) - rule.Build(pctx, ctx, "generate_test_config", "generate test_config.properties") + rule.Build("generate_test_config", "generate test_config.properties") } func generateSameDirRoboTestConfigJar(ctx android.ModuleContext, outputFile android.ModuleOutPath) { - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) outputDir := outputFile.InSameDir(ctx) configFile := outputDir.Join(ctx, "com/android/tools/test_config.properties") @@ -230,12 +230,12 @@ func generateSameDirRoboTestConfigJar(ctx android.ModuleContext, outputFile andr Textf(`echo "android_resource_apk=%s.apk"`, ctx.ModuleName()). Text(") >>").Output(configFile) rule.Command(). - BuiltTool(ctx, "soong_zip"). + BuiltTool("soong_zip"). FlagWithArg("-C ", outputDir.String()). FlagWithInput("-f ", configFile). FlagWithOutput("-o ", outputFile) - rule.Build(pctx, ctx, "generate_test_config_samedir", "generate test_config.properties") + rule.Build("generate_test_config_samedir", "generate test_config.properties") } func (r *robolectricTest) generateRoboSrcJar(ctx android.ModuleContext, outputFile android.WritablePath, @@ -389,8 +389,10 @@ func (r *robolectricRuntimes) GenerateAndroidBuildActions(ctx android.ModuleCont } runtimeFromSourceJar := android.OutputFileForModule(ctx, runtimeFromSourceModule, "") + // TODO(murj) Update this to ctx.Config().PlatformSdkCodename() once the platform + // classes like android.os.Build are updated to S. runtimeName := fmt.Sprintf("android-all-%s-robolectric-r0.jar", - ctx.Config().PlatformSdkCodename()) + "R") installedRuntime := ctx.InstallFile(androidAllDir, runtimeName, runtimeFromSourceJar) r.runtimes = append(r.runtimes, installedRuntime) } diff --git a/java/sdk.go b/java/sdk.go index 971791f4a..32a4b5a88 100644 --- a/java/sdk.go +++ b/java/sdk.go @@ -544,7 +544,7 @@ func createSdkFrameworkAidl(ctx android.SingletonContext) { commitChangeForRestat(rule, tempPath, combinedAidl) - rule.Build(pctx, ctx, "framework_aidl", "generate framework.aidl") + rule.Build("framework_aidl", "generate framework.aidl") } // Creates a version of framework.aidl for the non-updatable part of the platform. @@ -558,7 +558,7 @@ func createNonUpdatableFrameworkAidl(ctx android.SingletonContext) { commitChangeForRestat(rule, tempPath, combinedAidl) - rule.Build(pctx, ctx, "framework_non_updatable_aidl", "generate framework_non_updatable.aidl") + rule.Build("framework_non_updatable_aidl", "generate framework_non_updatable.aidl") } func createFrameworkAidl(stubsModules []string, path android.OutputPath, ctx android.SingletonContext) *android.RuleBuilder { @@ -586,7 +586,7 @@ func createFrameworkAidl(stubsModules []string, path android.OutputPath, ctx and } } - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) rule.MissingDeps(missingDeps) var aidls android.Paths @@ -597,7 +597,7 @@ func createFrameworkAidl(stubsModules []string, path android.OutputPath, ctx and rule.Command(). Text("rm -f").Output(aidl) rule.Command(). - BuiltTool(ctx, "sdkparcelables"). + BuiltTool("sdkparcelables"). Input(jar). Output(aidl) @@ -632,7 +632,7 @@ func nonUpdatableFrameworkAidlPath(ctx android.PathContext) android.OutputPath { func createAPIFingerprint(ctx android.SingletonContext) { out := ApiFingerprintPath(ctx) - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) rule.Command(). Text("rm -f").Output(out) @@ -659,7 +659,7 @@ func createAPIFingerprint(ctx android.SingletonContext) { Output(out) } - rule.Build(pctx, ctx, "api_fingerprint", "generate api_fingerprint.txt") + rule.Build("api_fingerprint", "generate api_fingerprint.txt") } func ApiFingerprintPath(ctx android.PathContext) android.OutputPath { diff --git a/java/sdk_library.go b/java/sdk_library.go index 21c03cd39..4e33d747b 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -435,8 +435,23 @@ type sdkLibraryProperties struct { // If set to true, the path of dist files is apistubs/core. Defaults to false. Core_lib *bool - // don't create dist rules. - No_dist *bool `blueprint:"mutated"` + // If set to true then don't create dist rules. + No_dist *bool + + // The stem for the artifacts that are copied to the dist, if not specified + // then defaults to the base module name. + // + // For each scope the following artifacts are copied to the apistubs/<scope> + // directory in the dist. + // * stubs impl jar -> <dist-stem>.jar + // * API specification file -> api/<dist-stem>.txt + // * Removed API specification file -> api/<dist-stem>-removed.txt + // + // Also used to construct the name of the filegroup (created by prebuilt_apis) + // that references the latest released API and remove API specification files. + // * API specification filegroup -> <dist-stem>.api.<scope>.latest + // * Removed API specification filegroup -> <dist-stem>-removed.api.<scope>.latest + Dist_stem *string // indicates whether system and test apis should be generated. Generate_system_and_test_apis bool `blueprint:"mutated"` @@ -1109,12 +1124,16 @@ func (module *SdkLibrary) sdkVersionForStubsLibrary(mctx android.EarlyModuleCont } } +func (module *SdkLibrary) distStem() string { + return proptools.StringDefault(module.sdkLibraryProperties.Dist_stem, module.BaseModuleName()) +} + func (module *SdkLibrary) latestApiFilegroupName(apiScope *apiScope) string { - return ":" + module.BaseModuleName() + ".api." + apiScope.name + ".latest" + return ":" + module.distStem() + ".api." + apiScope.name + ".latest" } func (module *SdkLibrary) latestRemovedApiFilegroupName(apiScope *apiScope) string { - return ":" + module.BaseModuleName() + "-removed.api." + apiScope.name + ".latest" + return ":" + module.distStem() + "-removed.api." + apiScope.name + ".latest" } func childModuleVisibility(childVisibility []string) []string { @@ -1220,7 +1239,7 @@ func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext // Dist the class jar artifact for sdk builds. if !Bool(module.sdkLibraryProperties.No_dist) { props.Dist.Targets = []string{"sdk", "win_sdk"} - props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.BaseModuleName())) + props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.distStem())) props.Dist.Dir = proptools.StringPtr(module.apiDistPath(apiScope)) props.Dist.Tag = proptools.StringPtr(".jar") } @@ -1262,11 +1281,7 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC Include_dirs []string Local_include_dirs []string } - Dist struct { - Targets []string - Dest *string - Dir *string - } + Dists []android.Dist }{} // The stubs source processing uses the same compile time classpath when extracting the @@ -1366,11 +1381,23 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC } } - // Dist the api txt artifact for sdk builds. if !Bool(module.sdkLibraryProperties.No_dist) { - props.Dist.Targets = []string{"sdk", "win_sdk"} - props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.txt", module.BaseModuleName())) - props.Dist.Dir = proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api")) + // Dist the api txt and removed api txt artifacts for sdk builds. + distDir := proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api")) + for _, p := range []struct { + tag string + pattern string + }{ + {tag: ".api.txt", pattern: "%s.txt"}, + {tag: ".removed-api.txt", pattern: "%s-removed.txt"}, + } { + props.Dists = append(props.Dists, android.Dist{ + Targets: []string{"sdk", "win_sdk"}, + Dir: distDir, + Dest: proptools.StringPtr(fmt.Sprintf(p.pattern, module.distStem())), + Tag: proptools.StringPtr(p.tag), + }) + } } mctx.CreateModule(DroidstubsFactory, &props) @@ -1474,10 +1501,6 @@ func (module *SdkLibrary) SdkImplementationJars(ctx android.BaseModuleContext, s return module.sdkJars(ctx, sdkVersion, false /*headerJars*/) } -func (module *SdkLibrary) SetNoDist() { - module.sdkLibraryProperties.No_dist = proptools.BoolPtr(true) -} - var javaSdkLibrariesKey = android.NewOnceKey("javaSdkLibraries") func javaSdkLibraries(config android.Config) *[]string { @@ -1505,12 +1528,10 @@ func (module *SdkLibrary) CreateInternalModules(mctx android.DefaultableHookCont } // If this builds against standard libraries (i.e. is not part of the core libraries) - // then assume it provides both system and test apis. Otherwise, assume it does not and - // also assume it does not contribute to the dist build. + // then assume it provides both system and test apis. sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library)) hasSystemAndTestApis := sdkDep.hasStandardLibs() module.sdkLibraryProperties.Generate_system_and_test_apis = hasSystemAndTestApis - module.sdkLibraryProperties.No_dist = proptools.BoolPtr(!hasSystemAndTestApis) missing_current_api := false @@ -2155,12 +2176,12 @@ func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleConte xmlContent := fmt.Sprintf(permissionsTemplate, libName, module.implPath(ctx)) module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath - rule := android.NewRuleBuilder() + rule := android.NewRuleBuilder(pctx, ctx) rule.Command(). Text("/bin/bash -c \"echo -e '" + xmlContent + "'\" > "). Output(module.outputFilePath) - rule.Build(pctx, ctx, "java_sdk_xml", "Permission XML") + rule.Build("java_sdk_xml", "Permission XML") module.installDirPath = android.PathForModuleInstall(ctx, "etc", module.SubDir()) } @@ -2292,11 +2313,10 @@ func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberCo scopeSet.AddProperty("jars", jars) // Merge the stubs source jar into the snapshot zip so that when it is unpacked - // the source files are also unpacked. Use a glob so that if the directory is missing - // (because there are no stubs sources for this scope) it will not fail. + // the source files are also unpacked. snapshotRelativeDir := filepath.Join(scopeDir, ctx.Name()+"_stub_sources") ctx.SnapshotBuilder().UnzipToSnapshot(properties.StubsSrcJar, snapshotRelativeDir) - scopeSet.AddProperty("stub_srcs", []string{snapshotRelativeDir + "/**/*.java"}) + scopeSet.AddProperty("stub_srcs", []string{snapshotRelativeDir}) if properties.CurrentApiFile != nil { currentApiSnapshotPath := filepath.Join(scopeDir, ctx.Name()+".txt") diff --git a/java/sdk_library_external.go b/java/sdk_library_external.go new file mode 100644 index 000000000..293493685 --- /dev/null +++ b/java/sdk_library_external.go @@ -0,0 +1,109 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package java + +import ( + "android/soong/android" +) + +type partitionGroup int + +// Representation of partition group for checking inter-partition library dependencies. +// Between system and system_ext, there are no restrictions of dependencies, +// so we can treat these partitions as the same in terms of inter-partition dependency. +// Same policy is applied between vendor and odm partiton. +const ( + partitionGroupNone partitionGroup = iota + // group for system, and system_ext partition + partitionGroupSystem + // group for vendor and odm partition + partitionGroupVendor + // product partition + partitionGroupProduct +) + +func (g partitionGroup) String() string { + switch g { + case partitionGroupSystem: + return "system" + case partitionGroupVendor: + return "vendor" + case partitionGroupProduct: + return "product" + } + + return "" +} + +// Get partition group of java module that can be used at inter-partition dependency check. +// We currently have three groups +// (system, system_ext) => system partition group +// (vendor, odm) => vendor partition group +// (product) => product partition group +func (j *Module) partitionGroup(ctx android.EarlyModuleContext) partitionGroup { + // system and system_ext partition can be treated as the same in terms of inter-partition dependency. + if j.Platform() || j.SystemExtSpecific() { + return partitionGroupSystem + } + + // vendor and odm partition can be treated as the same in terms of inter-partition dependency. + if j.SocSpecific() || j.DeviceSpecific() { + return partitionGroupVendor + } + + // product partition is independent. + if j.ProductSpecific() { + return partitionGroupProduct + } + + panic("Cannot determine partition type") +} + +func (j *Module) allowListedInterPartitionJavaLibrary(ctx android.EarlyModuleContext) bool { + return inList(j.Name(), ctx.Config().InterPartitionJavaLibraryAllowList()) +} + +type javaSdkLibraryEnforceContext interface { + Name() string + allowListedInterPartitionJavaLibrary(ctx android.EarlyModuleContext) bool + partitionGroup(ctx android.EarlyModuleContext) partitionGroup +} + +var _ javaSdkLibraryEnforceContext = (*Module)(nil) + +func (j *Module) checkPartitionsForJavaDependency(ctx android.EarlyModuleContext, propName string, dep javaSdkLibraryEnforceContext) { + if dep.allowListedInterPartitionJavaLibrary(ctx) { + return + } + + // If product interface is not enforced, skip check between system and product partition. + // But still need to check between product and vendor partition because product interface flag + // just represents enforcement between product and system, and vendor interface enforcement + // that is enforced here by precondition is representing enforcement between vendor and other partitions. + if !ctx.Config().EnforceProductPartitionInterface() { + productToSystem := j.partitionGroup(ctx) == partitionGroupProduct && dep.partitionGroup(ctx) == partitionGroupSystem + systemToProduct := j.partitionGroup(ctx) == partitionGroupSystem && dep.partitionGroup(ctx) == partitionGroupProduct + + if productToSystem || systemToProduct { + return + } + } + + // If module and dependency library is inter-partition + if j.partitionGroup(ctx) != dep.partitionGroup(ctx) { + errorFormat := "dependency on java_library (%q) is not allowed across the partitions (%s -> %s), use java_sdk_library instead" + ctx.PropertyErrorf(propName, errorFormat, dep.Name(), j.partitionGroup(ctx), dep.partitionGroup(ctx)) + } +} diff --git a/java/sysprop.go b/java/sysprop.go index 1a70499b8..e41aef68a 100644 --- a/java/sysprop.go +++ b/java/sysprop.go @@ -14,6 +14,10 @@ package java +// This file contains a map to redirect dependencies towards sysprop_library. If a sysprop_library +// is owned by Platform, and the client module links against system API, the public stub of the +// sysprop_library should be used. The map will contain public stub names of sysprop_libraries. + import ( "sync" |