diff options
Diffstat (limited to 'java')
-rw-r--r-- | java/android_manifest.go | 10 | ||||
-rw-r--r-- | java/androidmk.go | 10 | ||||
-rwxr-xr-x | java/app.go | 29 | ||||
-rw-r--r-- | java/app_test.go | 12 | ||||
-rw-r--r-- | java/base.go | 27 | ||||
-rw-r--r-- | java/config/config.go | 20 | ||||
-rw-r--r-- | java/config/makevars.go | 9 | ||||
-rw-r--r-- | java/dex.go | 4 | ||||
-rw-r--r-- | java/dexpreopt.go | 4 | ||||
-rw-r--r-- | java/dexpreopt.go_v1 | 404 | ||||
-rw-r--r-- | java/dexpreopt_bootjars.go | 13 | ||||
-rw-r--r-- | java/dexpreopt_bootjars.go_v1 | 952 | ||||
-rw-r--r-- | java/dexpreopt_config.go_v1 | 215 | ||||
-rw-r--r-- | java/fuzz.go | 113 | ||||
-rw-r--r-- | java/fuzz_test.go | 43 | ||||
-rw-r--r-- | java/hiddenapi.go | 15 | ||||
-rw-r--r-- | java/hiddenapi_modular.go | 6 | ||||
-rw-r--r-- | java/java.go | 33 | ||||
-rw-r--r-- | java/kotlin.go | 1 | ||||
-rw-r--r-- | java/kotlin_test.go | 8 | ||||
-rw-r--r-- | java/lint.go | 12 | ||||
-rw-r--r-- | java/lint_defaults.txt | 6 | ||||
-rw-r--r-- | java/sdk_library.go | 39 |
23 files changed, 1868 insertions, 117 deletions
diff --git a/java/android_manifest.go b/java/android_manifest.go index 7772b7090..a297b2c10 100644 --- a/java/android_manifest.go +++ b/java/android_manifest.go @@ -82,8 +82,8 @@ func ManifestFixer(ctx android.ModuleContext, manifest android.Path, if minSdkVersion.FinalOrFutureInt() >= 23 { args = append(args, fmt.Sprintf("--extract-native-libs=%v", !params.UseEmbeddedNativeLibs)) } else if params.UseEmbeddedNativeLibs { - ctx.ModuleErrorf("module attempted to store uncompressed native libraries, but minSdkVersion=%d doesn't support it", - minSdkVersion) + ctx.ModuleErrorf("module attempted to store uncompressed native libraries, but minSdkVersion=%s doesn't support it", + minSdkVersion.String()) } } @@ -96,9 +96,9 @@ func ManifestFixer(ctx android.ModuleContext, manifest android.Path, } if params.ClassLoaderContexts != nil { - // manifest_fixer should add only the implicit SDK libraries inferred by Soong, not those added - // explicitly via `uses_libs`/`optional_uses_libs`. - requiredUsesLibs, optionalUsesLibs := params.ClassLoaderContexts.ImplicitUsesLibs() + // Libraries propagated via `uses_libs`/`optional_uses_libs` are also added (they may be + // propagated from dependencies). + requiredUsesLibs, optionalUsesLibs := params.ClassLoaderContexts.UsesLibs() for _, usesLib := range requiredUsesLibs { args = append(args, "--uses-library", usesLib) diff --git a/java/androidmk.go b/java/androidmk.go index 80b828d45..f6ea6a930 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -132,6 +132,16 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { return entriesList } +func (j *JavaFuzzLibrary) AndroidMkEntries() []android.AndroidMkEntries { + entriesList := j.Library.AndroidMkEntries() + entries := &entriesList[0] + entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { + entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", "null-suite") + androidMkWriteTestData(j.jniFilePaths, entries) + }) + return entriesList +} + // Called for modules that are a component of a test suite. func testSuiteComponent(entries *android.AndroidMkEntries, test_suites []string, perTestcaseDirectory bool) { entries.SetString("LOCAL_MODULE_TAGS", "tests") diff --git a/java/app.go b/java/app.go index 2b52eab15..768d9e923 100755 --- a/java/app.go +++ b/java/app.go @@ -900,6 +900,7 @@ func AndroidAppFactory() android.Module { module.Module.dexProperties.Optimize.Shrink = proptools.BoolPtr(true) module.Module.properties.Instrument = true + module.Module.properties.Supports_static_instrumentation = true module.Module.properties.Installable = proptools.BoolPtr(true) module.addHostAndDeviceProperties() @@ -1019,6 +1020,7 @@ func AndroidTestFactory() android.Module { module.Module.dexProperties.Optimize.EnabledByDefault = true module.Module.properties.Instrument = true + module.Module.properties.Supports_static_instrumentation = true module.Module.properties.Installable = proptools.BoolPtr(true) module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true) module.appProperties.AlwaysPackageNativeLibs = true @@ -1225,28 +1227,17 @@ func (u *usesLibrary) addLib(lib string, optional bool) { func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, hasFrameworkLibs bool) { if !ctx.Config().UnbundledBuild() || ctx.Config().UnbundledBuildImage() { - reqTag := makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, false, false) - ctx.AddVariationDependencies(nil, reqTag, u.usesLibraryProperties.Uses_libs...) - - optTag := makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, true, false) - ctx.AddVariationDependencies(nil, optTag, u.presentOptionalUsesLibs(ctx)...) - + ctx.AddVariationDependencies(nil, usesLibReqTag, u.usesLibraryProperties.Uses_libs...) + ctx.AddVariationDependencies(nil, usesLibOptTag, u.presentOptionalUsesLibs(ctx)...) // Only add these extra dependencies if the module depends on framework libs. This avoids // creating a cyclic dependency: // e.g. framework-res -> org.apache.http.legacy -> ... -> framework-res. if hasFrameworkLibs { - // Add implicit <uses-library> dependencies on compatibility libraries. Some of them are - // optional, and some required --- this depends on the most common usage of the library - // and may be wrong for some apps (they need explicit `uses_libs`/`optional_uses_libs`). - - compat28OptTag := makeUsesLibraryDependencyTag(28, true, true) - ctx.AddVariationDependencies(nil, compat28OptTag, dexpreopt.OptionalCompatUsesLibs28...) - - compat29ReqTag := makeUsesLibraryDependencyTag(29, false, true) - ctx.AddVariationDependencies(nil, compat29ReqTag, dexpreopt.CompatUsesLibs29...) - - compat30OptTag := makeUsesLibraryDependencyTag(30, true, true) - ctx.AddVariationDependencies(nil, compat30OptTag, dexpreopt.OptionalCompatUsesLibs30...) + // Dexpreopt needs paths to the dex jars of these libraries in order to construct + // class loader context for dex2oat. Add them as a dependency with a special tag. + ctx.AddVariationDependencies(nil, usesLibCompat29ReqTag, dexpreopt.CompatUsesLibs29...) + ctx.AddVariationDependencies(nil, usesLibCompat28OptTag, dexpreopt.OptionalCompatUsesLibs28...) + ctx.AddVariationDependencies(nil, usesLibCompat30OptTag, dexpreopt.OptionalCompatUsesLibs30...) } } } @@ -1305,7 +1296,7 @@ func (u *usesLibrary) classLoaderContextForUsesLibDeps(ctx android.ModuleContext replaceInList(u.usesLibraryProperties.Uses_libs, dep, libName) replaceInList(u.usesLibraryProperties.Optional_uses_libs, dep, libName) } - clcMap.AddContext(ctx, tag.sdkVersion, libName, tag.optional, tag.implicit, + clcMap.AddContext(ctx, tag.sdkVersion, libName, tag.optional, lib.DexJarBuildPath().PathOrNil(), lib.DexJarInstallPath(), lib.ClassLoaderContexts()) } else if ctx.Config().AllowMissingDependencies() { diff --git a/java/app_test.go b/java/app_test.go index 6a4508cd6..8324dff6c 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -2505,12 +2505,20 @@ func TestUsesLibraries(t *testing.T) { prebuilt := result.ModuleForTests("prebuilt", "android_common") // Test that implicit dependencies on java_sdk_library instances are passed to the manifest. - // This should not include explicit `uses_libs`/`optional_uses_libs` entries. + // These also include explicit `uses_libs`/`optional_uses_libs` entries, as they may be + // propagated from dependencies. actualManifestFixerArgs := app.Output("manifest_fixer/AndroidManifest.xml").Args["args"] expectManifestFixerArgs := `--extract-native-libs=true ` + `--uses-library qux ` + `--uses-library quuz ` + - `--uses-library runtime-library` + `--uses-library foo ` + + `--uses-library com.non.sdk.lib ` + + `--uses-library runtime-library ` + + `--uses-library runtime-required-x ` + + `--uses-library runtime-required-y ` + + `--optional-uses-library bar ` + + `--optional-uses-library runtime-optional-x ` + + `--optional-uses-library runtime-optional-y` android.AssertStringDoesContain(t, "manifest_fixer args", actualManifestFixerArgs, expectManifestFixerArgs) // Test that all libraries are verified (library order matters). diff --git a/java/base.go b/java/base.go index b925350a0..e60e54ec1 100644 --- a/java/base.go +++ b/java/base.go @@ -170,6 +170,9 @@ type CommonProperties struct { } Instrument bool `blueprint:"mutated"` + // If true, then the module supports statically including the jacocoagent + // into the library. + Supports_static_instrumentation bool `blueprint:"mutated"` // List of files to include in the META-INF/services folder of the resulting jar. Services []string `android:"path,arch_variant"` @@ -442,9 +445,6 @@ type Module struct { // manifest file to use instead of properties.Manifest overrideManifest android.OptionalPath - // map of SDK version to class loader context - classLoaderContexts dexpreopt.ClassLoaderContextMap - // list of plugins that this java module is exporting exportedPluginJars android.Paths @@ -605,7 +605,8 @@ func (j *Module) shouldInstrument(ctx android.BaseModuleContext) bool { } func (j *Module) shouldInstrumentStatic(ctx android.BaseModuleContext) bool { - return j.shouldInstrument(ctx) && + return j.properties.Supports_static_instrumentation && + j.shouldInstrument(ctx) && (ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_STATIC") || ctx.Config().UnbundledBuild()) } @@ -723,8 +724,10 @@ func (j *Module) deps(ctx android.BottomUpMutatorContext) { if component, ok := dep.(SdkLibraryComponentDependency); ok { if lib := component.OptionalSdkLibraryImplementation(); lib != nil { // Add library as optional if it's one of the optional compatibility libs. - optional := android.InList(*lib, dexpreopt.OptionalCompatUsesLibs) - tag := makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, optional, true) + tag := usesLibReqTag + if android.InList(*lib, dexpreopt.OptionalCompatUsesLibs) { + tag = usesLibOptTag + } ctx.AddVariationDependencies(nil, tag, *lib) } } @@ -1481,11 +1484,11 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { } if ctx.Device() { - lintSDKVersionString := func(sdkSpec android.SdkSpec) string { + lintSDKVersion := func(sdkSpec android.SdkSpec) android.ApiLevel { if v := sdkSpec.ApiLevel; !v.IsPreview() { - return v.String() + return v } else { - return ctx.Config().DefaultAppTargetSdk(ctx).String() + return ctx.Config().DefaultAppTargetSdk(ctx) } } @@ -1494,9 +1497,9 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { j.linter.srcJars = srcJars j.linter.classpath = append(append(android.Paths(nil), flags.bootClasspath...), flags.classpath...) j.linter.classes = j.implementationJarFile - j.linter.minSdkVersion = lintSDKVersionString(j.MinSdkVersion(ctx)) - j.linter.targetSdkVersion = lintSDKVersionString(j.TargetSdkVersion(ctx)) - j.linter.compileSdkVersion = lintSDKVersionString(j.SdkVersion(ctx)) + j.linter.minSdkVersion = lintSDKVersion(j.MinSdkVersion(ctx)) + j.linter.targetSdkVersion = lintSDKVersion(j.TargetSdkVersion(ctx)) + j.linter.compileSdkVersion = lintSDKVersion(j.SdkVersion(ctx)) j.linter.compileSdkKind = j.SdkVersion(ctx).Kind j.linter.javaLanguageLevel = flags.javaVersion.String() j.linter.kotlinLanguageLevel = "1.3" diff --git a/java/config/config.go b/java/config/config.go index 95b841fa7..d7440022f 100644 --- a/java/config/config.go +++ b/java/config/config.go @@ -68,6 +68,11 @@ var ( "-J-XX:+TieredCompilation", "-J-XX:TieredStopAtLevel=1", } + dexerJavaVmFlagsList = []string{ + `-JXX:OnError="cat hs_err_pid%p.log"`, + "-JXX:CICompilerCount=6", + "-JXX:+UseDynamicNumberOfGCThreads", + } ) func init() { @@ -81,11 +86,16 @@ func init() { exportedVars.ExportStringStaticVariable("ErrorProneHeapSize", "4096M") exportedVars.ExportStringStaticVariable("ErrorProneHeapFlags", "-J-Xmx${ErrorProneHeapSize}") - exportedVars.ExportStringListStaticVariable("DexFlags", []string{ - `-JXX:OnError="cat hs_err_pid%p.log"`, - "-JXX:CICompilerCount=6", - "-JXX:+UseDynamicNumberOfGCThreads", - }) + // D8 invocations are shorter lived, so we restrict their JIT tiering relative to R8. + // Note that the `-JXX` prefix syntax is specific to the R8/D8 invocation wrappers. + exportedVars.ExportStringListStaticVariable("D8Flags", append([]string{ + "-JXmx2048M", + "-JXX:+TieredCompilation", + "-JXX:TieredStopAtLevel=1", + }, dexerJavaVmFlagsList...)) + exportedVars.ExportStringListStaticVariable("R8Flags", append([]string{ + "-JXmx2048M", + }, dexerJavaVmFlagsList...)) exportedVars.ExportStringListStaticVariable("CommonJdkFlags", []string{ `-Xmaxerrs 9999999`, diff --git a/java/config/makevars.go b/java/config/makevars.go index df447a129..273aca0b4 100644 --- a/java/config/makevars.go +++ b/java/config/makevars.go @@ -43,9 +43,10 @@ func makeVarsProvider(ctx android.MakeVarsContext) { ctx.Strict("JAVADOC", "${JavadocCmd}") ctx.Strict("COMMON_JDK_FLAGS", "${CommonJdkFlags}") - ctx.Strict("DX", "${D8Cmd}") - ctx.Strict("DX_COMMAND", "${D8Cmd} -JXms16M -JXmx2048M") - ctx.Strict("R8_COMPAT_PROGUARD", "${R8Cmd}") + ctx.Strict("D8", "${D8Cmd}") + ctx.Strict("R8", "${R8Cmd}") + ctx.Strict("D8_COMMAND", "${D8Cmd} ${D8Flags}") + ctx.Strict("R8_COMMAND", "${R8Cmd} ${R8Flags}") ctx.Strict("TURBINE", "${TurbineJar}") @@ -78,8 +79,6 @@ func makeVarsProvider(ctx android.MakeVarsContext) { ctx.Strict("CLASS2NONSDKLIST", "${Class2NonSdkList}") ctx.Strict("HIDDENAPI", "${HiddenAPI}") - ctx.Strict("DEX_FLAGS", "${DexFlags}") - ctx.Strict("AIDL", "${AidlCmd}") ctx.Strict("AAPT2", "${Aapt2Cmd}") ctx.Strict("ZIPALIGN", "${ZipAlign}") diff --git a/java/dex.go b/java/dex.go index 84665e7b5..13d6e4a01 100644 --- a/java/dex.go +++ b/java/dex.go @@ -95,7 +95,7 @@ var d8, d8RE = pctx.MultiCommandRemoteStaticRules("d8", Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` + `mkdir -p $$(dirname $tmpJar) && ` + `${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` + - `$d8Template${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $tmpJar && ` + + `$d8Template${config.D8Cmd} ${config.D8Flags} --output $outDir $d8Flags $tmpJar && ` + `$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` + `${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in`, CommandDeps: []string{ @@ -128,7 +128,7 @@ var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8", `mkdir -p $$(dirname ${outUsage}) && ` + `mkdir -p $$(dirname $tmpJar) && ` + `${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` + - `$r8Template${config.R8Cmd} ${config.DexFlags} -injars $tmpJar --output $outDir ` + + `$r8Template${config.R8Cmd} ${config.R8Flags} -injars $tmpJar --output $outDir ` + `--no-data-resources ` + `-printmapping ${outDict} ` + `-printusage ${outUsage} ` + diff --git a/java/dexpreopt.go b/java/dexpreopt.go index 7c5f055e6..0adaf9917 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -259,10 +259,6 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(moduleName(ctx)) bootImage := defaultBootImageConfig(ctx) - if global.UseArtImage { - bootImage = artBootImageConfig(ctx) - } - dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp) targets := ctx.MultiTargets() diff --git a/java/dexpreopt.go_v1 b/java/dexpreopt.go_v1 new file mode 100644 index 000000000..0adaf9917 --- /dev/null +++ b/java/dexpreopt.go_v1 @@ -0,0 +1,404 @@ +// Copyright 2018 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 ( + "path/filepath" + "strings" + + "android/soong/android" + "android/soong/dexpreopt" +) + +type DexpreopterInterface interface { + IsInstallable() bool // Structs that embed dexpreopter must implement this. + dexpreoptDisabled(ctx android.BaseModuleContext) bool + DexpreoptBuiltInstalledForApex() []dexpreopterInstall + AndroidMkEntriesForApex() []android.AndroidMkEntries +} + +type dexpreopterInstall struct { + // A unique name to distinguish an output from others for the same java library module. Usually in + // the form of `<arch>-<encoded-path>.odex/vdex/art`. + name string + + // The name of the input java module. + moduleName string + + // The path to the dexpreopt output on host. + outputPathOnHost android.Path + + // The directory on the device for the output to install to. + installDirOnDevice android.InstallPath + + // The basename (the last segment of the path) for the output to install as. + installFileOnDevice string +} + +// The full module name of the output in the makefile. +func (install *dexpreopterInstall) FullModuleName() string { + return install.moduleName + install.SubModuleName() +} + +// The sub-module name of the output in the makefile (the name excluding the java module name). +func (install *dexpreopterInstall) SubModuleName() string { + return "-dexpreopt-" + install.name +} + +// Returns Make entries for installing the file. +// +// This function uses a value receiver rather than a pointer receiver to ensure that the object is +// safe to use in `android.AndroidMkExtraEntriesFunc`. +func (install dexpreopterInstall) ToMakeEntries() android.AndroidMkEntries { + return android.AndroidMkEntries{ + Class: "ETC", + SubName: install.SubModuleName(), + OutputFile: android.OptionalPathForPath(install.outputPathOnHost), + ExtraEntries: []android.AndroidMkExtraEntriesFunc{ + func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { + entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String()) + entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice) + entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false") + }, + }, + } +} + +type dexpreopter struct { + dexpreoptProperties DexpreoptProperties + + installPath android.InstallPath + uncompressedDex bool + isSDKLibrary bool + isApp bool + isTest bool + isPresignedPrebuilt bool + preventInstall bool + + manifestFile android.Path + statusFile android.WritablePath + enforceUsesLibs bool + classLoaderContexts dexpreopt.ClassLoaderContextMap + + // See the `dexpreopt` function for details. + builtInstalled string + builtInstalledForApex []dexpreopterInstall + + // The config is used for two purposes: + // - Passing dexpreopt information about libraries from Soong to Make. This is needed when + // a <uses-library> is defined in Android.bp, but used in Android.mk (see dex_preopt_config_merger.py). + // Note that dexpreopt.config might be needed even if dexpreopt is disabled for the library itself. + // - Dexpreopt post-processing (using dexpreopt artifacts from a prebuilt system image to incrementally + // dexpreopt another partition). + configPath android.WritablePath +} + +type DexpreoptProperties struct { + Dex_preopt struct { + // If false, prevent dexpreopting. Defaults to true. + Enabled *bool + + // If true, generate an app image (.art file) for this module. + App_image *bool + + // If true, use a checked-in profile to guide optimization. Defaults to false unless + // a matching profile is set or a profile is found in PRODUCT_DEX_PREOPT_PROFILE_DIR + // that matches the name of this module, in which case it is defaulted to true. + Profile_guided *bool + + // If set, provides the path to profile relative to the Android.bp file. If not set, + // defaults to searching for a file that matches the name of this module in the default + // profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found. + Profile *string `android:"path"` + } +} + +func init() { + dexpreopt.DexpreoptRunningInSoong = true +} + +func isApexVariant(ctx android.BaseModuleContext) bool { + apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + return !apexInfo.IsForPlatform() +} + +func forPrebuiltApex(ctx android.BaseModuleContext) bool { + apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + return apexInfo.ForPrebuiltApex +} + +func moduleName(ctx android.BaseModuleContext) string { + // Remove the "prebuilt_" prefix if the module is from a prebuilt because the prefix is not + // expected by dexpreopter. + return android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()) +} + +func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool { + if !ctx.Device() { + return true + } + + if d.isTest { + return true + } + + if !BoolDefault(d.dexpreoptProperties.Dex_preopt.Enabled, true) { + return true + } + + // If the module is from a prebuilt APEX, it shouldn't be installable, but it can still be + // dexpreopted. + if !ctx.Module().(DexpreopterInterface).IsInstallable() && !forPrebuiltApex(ctx) { + return true + } + + if !android.IsModulePreferred(ctx.Module()) { + return true + } + + global := dexpreopt.GetGlobalConfig(ctx) + + if global.DisablePreopt { + return true + } + + if inList(moduleName(ctx), global.DisablePreoptModules) { + return true + } + + isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) + if isApexVariant(ctx) { + // Don't preopt APEX variant module unless the module is an APEX system server jar and we are + // building the entire system image. + if !isApexSystemServerJar || ctx.Config().UnbundledBuild() { + return true + } + } else { + // Don't preopt the platform variant of an APEX system server jar to avoid conflicts. + if isApexSystemServerJar { + return true + } + } + + // TODO: contains no java code + + return false +} + +func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) { + if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) { + return + } + dexpreopt.RegisterToolDeps(ctx) +} + +func (d *dexpreopter) odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool { + return dexpreopt.OdexOnSystemOtherByName(moduleName(ctx), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx)) +} + +// Returns the install path of the dex jar of a module. +// +// Do not rely on `ApexInfo.ApexVariationName` because it can be something like "apex1000", rather +// than the `name` in the path `/apex/<name>` as suggested in its comment. +// +// This function is on a best-effort basis. It cannot handle the case where an APEX jar is not a +// system server jar, which is fine because we currently only preopt system server jars for APEXes. +func (d *dexpreopter) getInstallPath( + ctx android.ModuleContext, defaultInstallPath android.InstallPath) android.InstallPath { + global := dexpreopt.GetGlobalConfig(ctx) + if global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) { + dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, moduleName(ctx)) + return android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexLocation, "/")) + } + if !d.dexpreoptDisabled(ctx) && isApexVariant(ctx) && + filepath.Base(defaultInstallPath.PartitionDir()) != "apex" { + ctx.ModuleErrorf("unable to get the install path of the dex jar for dexpreopt") + } + return defaultInstallPath +} + +func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.WritablePath) { + global := dexpreopt.GetGlobalConfig(ctx) + + // TODO(b/148690468): The check on d.installPath is to bail out in cases where + // the dexpreopter struct hasn't been fully initialized before we're called, + // e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively + // disabled, even if installable is true. + if d.installPath.Base() == "." { + return + } + + dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath) + + providesUsesLib := moduleName(ctx) + if ulib, ok := ctx.Module().(ProvidesUsesLib); ok { + name := ulib.ProvidesUsesLib() + if name != nil { + providesUsesLib = *name + } + } + + // If it is test, make config files regardless of its dexpreopt setting. + // The config files are required for apps defined in make which depend on the lib. + if d.isTest && d.dexpreoptDisabled(ctx) { + return + } + + isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(moduleName(ctx)) + + bootImage := defaultBootImageConfig(ctx) + dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp) + + targets := ctx.MultiTargets() + if len(targets) == 0 { + // assume this is a java library, dexpreopt for all arches for now + for _, target := range ctx.Config().Targets[android.Android] { + if target.NativeBridge == android.NativeBridgeDisabled { + targets = append(targets, target) + } + } + if isSystemServerJar && !d.isSDKLibrary { + // If the module is not an SDK library and it's a system server jar, only preopt the primary arch. + targets = targets[:1] + } + } + + var archs []android.ArchType + var images android.Paths + var imagesDeps []android.OutputPaths + for _, target := range targets { + archs = append(archs, target.Arch.ArchType) + variant := bootImage.getVariant(target) + images = append(images, variant.imagePathOnHost) + imagesDeps = append(imagesDeps, variant.imagesDeps) + } + // The image locations for all Android variants are identical. + hostImageLocations, deviceImageLocations := bootImage.getAnyAndroidVariant().imageLocations() + + var profileClassListing android.OptionalPath + var profileBootListing android.OptionalPath + profileIsTextListing := false + if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) { + // If dex_preopt.profile_guided is not set, default it based on the existence of the + // dexprepot.profile option or the profile class listing. + if String(d.dexpreoptProperties.Dex_preopt.Profile) != "" { + profileClassListing = android.OptionalPathForPath( + android.PathForModuleSrc(ctx, String(d.dexpreoptProperties.Dex_preopt.Profile))) + profileBootListing = android.ExistentPathForSource(ctx, + ctx.ModuleDir(), String(d.dexpreoptProperties.Dex_preopt.Profile)+"-boot") + profileIsTextListing = true + } else if global.ProfileDir != "" { + profileClassListing = android.ExistentPathForSource(ctx, + global.ProfileDir, moduleName(ctx)+".prof") + } + } + + // Full dexpreopt config, used to create dexpreopt build rules. + dexpreoptConfig := &dexpreopt.ModuleConfig{ + Name: moduleName(ctx), + DexLocation: dexLocation, + BuildPath: android.PathForModuleOut(ctx, "dexpreopt", moduleName(ctx)+".jar").OutputPath, + DexPath: dexJarFile, + ManifestPath: android.OptionalPathForPath(d.manifestFile), + UncompressedDex: d.uncompressedDex, + HasApkLibraries: false, + PreoptFlags: nil, + + ProfileClassListing: profileClassListing, + ProfileIsTextListing: profileIsTextListing, + ProfileBootListing: profileBootListing, + + EnforceUsesLibrariesStatusFile: dexpreopt.UsesLibrariesStatusFile(ctx), + EnforceUsesLibraries: d.enforceUsesLibs, + ProvidesUsesLibrary: providesUsesLib, + ClassLoaderContexts: d.classLoaderContexts, + + Archs: archs, + DexPreoptImagesDeps: imagesDeps, + DexPreoptImageLocationsOnHost: hostImageLocations, + DexPreoptImageLocationsOnDevice: deviceImageLocations, + + PreoptBootClassPathDexFiles: dexFiles.Paths(), + PreoptBootClassPathDexLocations: dexLocations, + + PreoptExtractedApk: false, + + NoCreateAppImage: !BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, true), + ForceCreateAppImage: BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, false), + + PresignedPrebuilt: d.isPresignedPrebuilt, + } + + d.configPath = android.PathForModuleOut(ctx, "dexpreopt", "dexpreopt.config") + dexpreopt.WriteModuleConfig(ctx, dexpreoptConfig, d.configPath) + + if d.dexpreoptDisabled(ctx) { + return + } + + globalSoong := dexpreopt.GetGlobalSoongConfig(ctx) + + dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, dexpreoptConfig) + if err != nil { + ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error()) + return + } + + dexpreoptRule.Build("dexpreopt", "dexpreopt") + + isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) + + for _, install := range dexpreoptRule.Installs() { + // Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT. + installDir := strings.TrimPrefix(filepath.Dir(install.To), "/") + installBase := filepath.Base(install.To) + arch := filepath.Base(installDir) + installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir) + + if isApexSystemServerJar { + // APEX variants of java libraries are hidden from Make, so their dexpreopt + // outputs need special handling. Currently, for APEX variants of java + // libraries, only those in the system server classpath are handled here. + // Preopting of boot classpath jars in the ART APEX are handled in + // java/dexpreopt_bootjars.go, and other APEX jars are not preopted. + // The installs will be handled by Make as sub-modules of the java library. + d.builtInstalledForApex = append(d.builtInstalledForApex, dexpreopterInstall{ + name: arch + "-" + installBase, + moduleName: moduleName(ctx), + outputPathOnHost: install.From, + installDirOnDevice: installPath, + installFileOnDevice: installBase, + }) + } else if !d.preventInstall { + ctx.InstallFile(installPath, installBase, install.From) + } + } + + if !isApexSystemServerJar { + d.builtInstalled = dexpreoptRule.Installs().String() + } +} + +func (d *dexpreopter) DexpreoptBuiltInstalledForApex() []dexpreopterInstall { + return d.builtInstalledForApex +} + +func (d *dexpreopter) AndroidMkEntriesForApex() []android.AndroidMkEntries { + var entries []android.AndroidMkEntries + for _, install := range d.builtInstalledForApex { + entries = append(entries, install.ToMakeEntries()) + } + return entries +} diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index 3d91aec91..7c4da3ef6 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -213,12 +213,6 @@ import ( // 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). -// var artApexNames = []string{ "com.android.art", @@ -938,11 +932,8 @@ func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) { ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(dexLocations, " ")) 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. + // The primary ART boot image is exposed to Make for testing (gtests) and benchmarking + // (golem) purposes. for _, current := range append(d.otherImages, image) { imageNames = append(imageNames, current.name) for _, variant := range current.variants { diff --git a/java/dexpreopt_bootjars.go_v1 b/java/dexpreopt_bootjars.go_v1 new file mode 100644 index 000000000..07a357bb5 --- /dev/null +++ b/java/dexpreopt_bootjars.go_v1 @@ -0,0 +1,952 @@ +// Copyright 2019 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 ( + "path/filepath" + "strings" + + "android/soong/android" + "android/soong/dexpreopt" + + "github.com/google/blueprint/proptools" +) + +// ================================================================================================= +// WIP - see http://b/177892522 for details +// +// The build support for boot images is currently being migrated away from singleton to modules so +// the documentation may not be strictly accurate. Rather than update the documentation at every +// step which will create a lot of churn the changes that have been made will be listed here and the +// documentation will be updated once it is closer to the final result. +// +// Changes: +// 1) dex_bootjars is now a singleton module and not a plain singleton. +// 2) Boot images are now represented by the boot_image module type. +// 3) The art boot image is called "art-boot-image", the framework boot image is called +// "framework-boot-image". +// 4) They are defined in art/build/boot/Android.bp and frameworks/base/boot/Android.bp +// respectively. +// 5) Each boot_image retrieves the appropriate boot image configuration from the map returned by +// genBootImageConfigs() using the image_name specified in the boot_image module. +// ================================================================================================= + +// 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 as they are already part of the primary ART boot image. +// +// 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 a +// colon-separated pair <apex>:<library>, where <apex> is the variant name of a non-updatable APEX, +// "platform" if the library is a part of the platform in the system partition, or "system_ext" if +// it's in the system_ext partition. +// +// In these variables APEXes are identified by their "variant names", i.e. the names they get +// mounted as in /apex on device. In Soong modules that is the name set in the "apex_name" +// properties, which default to the "name" values. For example, many APEXes have both +// com.android.xxx and com.google.android.xxx modules in Soong, but take the same place +// /apex/com.android.xxx at runtime. In these cases the variant name is always com.android.xxx, +// regardless which APEX goes into the product. See also android.ApexInfo.ApexVariationName and +// apex.apexBundleProperties.Apex_name. +// +// A related variable PRODUCT_APEX_BOOT_JARS contains bootclasspath libraries that are in APEXes. +// They are not included in the boot image. The only exception here are ART jars and core-icu4j.jar +// that have been historically part of the boot image and are now in apexes; they are in boot images +// and core-icu4j.jar is generally treated as being part of PRODUCT_BOOT_JARS. +// +// 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. +// + +var artApexNames = []string{ + "com.android.art", + "com.android.art.debug", + "com.android.art.testing", + "com.google.android.art", + "com.google.android.art.debug", + "com.google.android.art.testing", +} + +func init() { + RegisterDexpreoptBootJarsComponents(android.InitRegistrationContext) +} + +// Target-independent description of a boot image. +type bootImageConfig struct { + // If this image is an extension, the image that it extends. + extends *bootImageConfig + + // Image name (used in directory names and ninja rule names). + name string + + // Basename of the image: the resulting filenames are <stem>[-<jar>].{art,oat,vdex}. + stem string + + // Output directory for the image files. + dir android.OutputPath + + // Output directory for the image files with debug symbols. + symbolsDir android.OutputPath + + // Subdirectory where the image files are installed. + installDirOnHost string + + // Subdirectory where the image files on device are installed. + installDirOnDevice string + + // Install path of the boot image profile if it needs to be installed in the APEX, or empty if not + // needed. + profileInstallPathInApex string + + // A list of (location, jar) pairs for the Java modules in this image. + modules android.ConfiguredJarList + + // File paths to jars. + dexPaths android.WritablePaths // for this image + dexPathsDeps android.WritablePaths // for the dependency images and in this image + + // Map from module name (without prebuilt_ prefix) to the predefined build path. + dexPathsByModule map[string]android.WritablePath + + // File path to a zip archive with all image files (or nil, if not needed). + zip android.WritablePath + + // Rules which should be used in make to install the outputs. + profileInstalls android.RuleBuilderInstalls + + // Path to the license metadata file for the module that built the profile. + profileLicenseMetadataFile android.OptionalPath + + // Path to the image profile file on host (or empty, if profile is not generated). + profilePathOnHost android.Path + + // Target-dependent fields. + variants []*bootImageVariant + + // Path of the preloaded classes file. + preloadedClassesFile string +} + +// Target-dependent description of a boot image. +type bootImageVariant struct { + *bootImageConfig + + // Target for which the image is generated. + target android.Target + + // The "locations" of jars. + dexLocations []string // for this image + dexLocationsDeps []string // for the dependency images and in this image + + // Paths to image files. + imagePathOnHost android.OutputPath // first image file path on host + imagePathOnDevice string // first image file path on device + + // All the files that constitute this image variant, i.e. .art, .oat and .vdex files. + imagesDeps android.OutputPaths + + // The path to the primary image variant's imagePathOnHost field, where primary image variant + // means the image variant that this extends. + // + // This is only set for a variant of an image that extends another image. + primaryImages android.OutputPath + + // The paths to the primary image variant's imagesDeps field, where primary image variant + // means the image variant that this extends. + // + // This is only set for a variant of an image that extends another image. + primaryImagesDeps android.Paths + + // Rules which should be used in make to install the outputs on host. + installs android.RuleBuilderInstalls + vdexInstalls android.RuleBuilderInstalls + unstrippedInstalls android.RuleBuilderInstalls + + // Rules which should be used in make to install the outputs on device. + deviceInstalls android.RuleBuilderInstalls + + // Path to the license metadata file for the module that built the image. + licenseMetadataFile android.OptionalPath +} + +// 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 { + return variant + } + } + return nil +} + +// 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 { + return variant + } + } + 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 { + // 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 { + name += "-" + android.ModuleStem(m) + } + 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) + } else { + return image.stem + } +} + +// 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++ { + name := image.moduleName(ctx, i) + for _, ext := range exts { + ret = append(ret, dir.Join(ctx, name+ext)) + } + } + return ret +} + +// apexVariants returns a list of all *bootImageVariant that could be included in an apex. +func (image *bootImageConfig) apexVariants() []*bootImageVariant { + variants := []*bootImageVariant{} + for _, variant := range image.variants { + // We also generate boot images for host (for testing), but we don't need those in the apex. + // TODO(b/177892522) - consider changing this to check Os.OsClass = android.Device + if variant.target.Os == android.Android { + variants = append(variants, variant) + } + } + return variants +} + +// Returns true if the boot image should be installed in the APEX. +func (image *bootImageConfig) shouldInstallInApex() bool { + return strings.HasPrefix(image.installDirOnDevice, "apex/") +} + +// 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 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() (imageLocationsOnHost []string, imageLocationsOnDevice []string) { + if image.extends != nil { + imageLocationsOnHost, imageLocationsOnDevice = image.extends.getVariant(image.target).imageLocations() + } + return append(imageLocationsOnHost, dexpreopt.PathToLocation(image.imagePathOnHost, image.target.Arch.ArchType)), + append(imageLocationsOnDevice, dexpreopt.PathStringToLocation(image.imagePathOnDevice, image.target.Arch.ArchType)) +} + +func dexpreoptBootJarsFactory() android.SingletonModule { + m := &dexpreoptBootJars{} + android.InitAndroidModule(m) + return m +} + +func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) { + ctx.RegisterSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory) +} + +func SkipDexpreoptBootJars(ctx android.PathContext) bool { + return dexpreopt.GetGlobalConfig(ctx).DisablePreoptBootImages +} + +// Singleton module for generating boot image build rules. +type dexpreoptBootJars struct { + android.SingletonModuleBase + + // 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 + + // 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 +} + +// Provide paths to boot images for use by modules that depend upon them. +// +// The build rules are created in GenerateSingletonBuildActions(). +func (d *dexpreoptBootJars) GenerateAndroidBuildActions(ctx android.ModuleContext) { + // Placeholder for now. +} + +// Generate build rules for boot images. +func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonContext) { + if SkipDexpreoptBootJars(ctx) { + return + } + if dexpreopt.GetCachedGlobalSoongConfig(ctx) == nil { + // No module has enabled dexpreopting, so we assume there will be no boot image to make. + return + } + + d.dexpreoptConfigForMake = android.PathForOutput(ctx, ctx.Config().DeviceName(), "dexpreopt.config") + writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake) + + global := dexpreopt.GetGlobalConfig(ctx) + if !shouldBuildBootImages(ctx.Config(), global) { + return + } + + defaultImageConfig := defaultBootImageConfig(ctx) + d.defaultBootImage = defaultImageConfig +} + +// shouldBuildBootImages determines whether boot images should be built. +func shouldBuildBootImages(config android.Config, global *dexpreopt.GlobalConfig) bool { + // Skip recompiling the boot image for the second sanitization phase. We'll get separate paths + // and invalidate first-stage artifacts which are crucial to SANITIZE_LITE builds. + // Note: this is technically incorrect. Compiled code contains stack checks which may depend + // on ASAN settings. + if len(config.SanitizeDevice()) == 1 && config.SanitizeDevice()[0] == "address" && global.SanitizeLite { + return false + } + return true +} + +// copyBootJarsToPredefinedLocations generates commands that will copy boot jars to predefined +// paths in the global config. +func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, srcBootDexJarsByModule bootDexJarByModule, dstBootJarsByModule map[string]android.WritablePath) { + // Create the super set of module names. + names := []string{} + names = append(names, android.SortedStringKeys(srcBootDexJarsByModule)...) + names = append(names, android.SortedStringKeys(dstBootJarsByModule)...) + names = android.SortedUniqueStrings(names) + for _, name := range names { + src := srcBootDexJarsByModule[name] + dst := dstBootJarsByModule[name] + + if src == nil { + // A dex boot jar should be provided by the source java module. It needs to be installable or + // have compile_dex=true - cf. assignments to java.Module.dexJarFile. + // + // However, the source java module may be either replaced or overridden (using prefer:true) by + // a prebuilt java module with the same name. In that case the dex boot jar needs to be + // provided by the corresponding prebuilt APEX module. That APEX is the one that refers + // through a exported_(boot|systemserver)classpath_fragments property to a + // prebuilt_(boot|systemserver)classpath_fragment module, which in turn lists the prebuilt + // java module in the contents property. If that chain is broken then this dependency will + // fail. + if !ctx.Config().AllowMissingDependencies() { + ctx.ModuleErrorf("module %s does not provide a dex boot jar (see comment next to this message in Soong for details)", name) + } else { + ctx.AddMissingDependencies([]string{name}) + } + } else if dst == nil { + ctx.ModuleErrorf("module %s is not part of the boot configuration", name) + } else { + ctx.Build(pctx, android.BuildParams{ + Rule: android.Cp, + Input: src, + Output: dst, + }) + } + } +} + +// buildBootImageVariantsForAndroidOs generates rules to build the boot image variants for the +// android.Android OsType and returns a map from the architectures to the paths of the generated +// boot image files. +// +// The paths are returned because they are needed elsewhere in Soong, e.g. for populating an APEX. +func buildBootImageVariantsForAndroidOs(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) bootImageFilesByArch { + return buildBootImageForOsType(ctx, image, profile, android.Android) +} + +// buildBootImageVariantsForBuildOs generates rules to build the boot image variants for the +// config.BuildOS OsType, i.e. the type of OS on which the build is being running. +// +// The files need to be generated into their predefined location because they are used from there +// both within Soong and outside, e.g. for ART based host side testing and also for use by some +// cloud based tools. However, they are not needed by callers of this function and so the paths do +// not need to be returned from this func, unlike the buildBootImageVariantsForAndroidOs func. +func buildBootImageVariantsForBuildOs(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) { + buildBootImageForOsType(ctx, image, profile, ctx.Config().BuildOS) +} + +// buildBootImageForOsType takes a bootImageConfig, a profile file and an android.OsType +// boot image files are required for and it creates rules to build the boot image +// files for all the required architectures for them. +// +// It returns a map from android.ArchType to the predefined paths of the boot image files. +func buildBootImageForOsType(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath, requiredOsType android.OsType) bootImageFilesByArch { + filesByArch := bootImageFilesByArch{} + for _, variant := range image.variants { + if variant.target.Os == requiredOsType { + buildBootImageVariant(ctx, variant, profile) + filesByArch[variant.target.Arch.ArchType] = variant.imagesDeps.Paths() + } + } + + return filesByArch +} + +// buildBootImageZipInPredefinedLocation generates a zip file containing all the boot image files. +// +// The supplied filesByArch is nil when the boot image files have not been generated. Otherwise, it +// is a map from android.ArchType to the predefined locations. +func buildBootImageZipInPredefinedLocation(ctx android.ModuleContext, image *bootImageConfig, filesByArch bootImageFilesByArch) { + if filesByArch == nil { + return + } + + // Compute the list of files from all the architectures. + zipFiles := android.Paths{} + for _, archType := range android.ArchTypeList() { + zipFiles = append(zipFiles, filesByArch[archType]...) + } + + rule := android.NewRuleBuilder(pctx, ctx) + rule.Command(). + BuiltTool("soong_zip"). + FlagWithOutput("-o ", image.zip). + FlagWithArg("-C ", image.dir.Join(ctx, android.Android.String()).String()). + FlagWithInputList("-f ", zipFiles, " -f ") + + rule.Build("zip_"+image.name, "zip "+image.name+" image") +} + +// Generate boot image build rules for a specific target. +func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) { + + globalSoong := dexpreopt.GetGlobalSoongConfig(ctx) + global := dexpreopt.GetGlobalConfig(ctx) + + arch := image.target.Arch.ArchType + os := image.target.Os.String() // We need to distinguish host-x86 and device-x86. + symbolsDir := image.symbolsDir.Join(ctx, os, image.installDirOnHost, arch.String()) + symbolsFile := symbolsDir.Join(ctx, image.stem+".oat") + outputDir := image.dir.Join(ctx, os, image.installDirOnHost, arch.String()) + outputPath := outputDir.Join(ctx, image.stem+".oat") + oatLocation := dexpreopt.PathToLocation(outputPath, arch) + imagePath := outputPath.ReplaceExtension(ctx, "art") + + rule := android.NewRuleBuilder(pctx, ctx) + + rule.Command().Text("mkdir").Flag("-p").Flag(symbolsDir.String()) + rule.Command().Text("rm").Flag("-f"). + Flag(symbolsDir.Join(ctx, "*.art").String()). + Flag(symbolsDir.Join(ctx, "*.oat").String()). + Flag(symbolsDir.Join(ctx, "*.invocation").String()) + rule.Command().Text("rm").Flag("-f"). + Flag(outputDir.Join(ctx, "*.art").String()). + Flag(outputDir.Join(ctx, "*.oat").String()). + Flag(outputDir.Join(ctx, "*.invocation").String()) + + cmd := rule.Command() + + extraFlags := ctx.Config().Getenv("ART_BOOT_IMAGE_EXTRA_ARGS") + if extraFlags == "" { + // Use ANDROID_LOG_TAGS to suppress most logging by default... + cmd.Text(`ANDROID_LOG_TAGS="*:e"`) + } else { + // ...unless the boot image is generated specifically for testing, then allow all logging. + cmd.Text(`ANDROID_LOG_TAGS="*:v"`) + } + + invocationPath := outputPath.ReplaceExtension(ctx, "invocation") + + cmd.Tool(globalSoong.Dex2oat). + Flag("--avoid-storing-invocation"). + FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath). + Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatImageXms). + Flag("--runtime-arg").FlagWithArg("-Xmx", global.Dex2oatImageXmx) + + if profile != nil { + cmd.FlagWithInput("--profile-file=", profile) + } + + dirtyImageFile := "frameworks/base/config/dirty-image-objects" + dirtyImagePath := android.ExistentPathForSource(ctx, dirtyImageFile) + if dirtyImagePath.Valid() { + cmd.FlagWithInput("--dirty-image-objects=", dirtyImagePath.Path()) + } + + 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, ":"). + // Add the path to the first file in the boot image with the arch specific directory removed, + // dex2oat will reconstruct the path to the actual file when it needs it. As the actual path + // to the file cannot be passed to the command make sure to add the actual path as an Implicit + // dependency to ensure that it is built before the command runs. + FlagWithArg("--boot-image=", dexpreopt.PathToLocation(artImage, arch)).Implicit(artImage). + // Similarly, the dex2oat tool will automatically find the paths to other files in the base + // boot image so make sure to add them as implicit dependencies to ensure that they are built + // before this command is run. + Implicits(image.primaryImagesDeps) + } else { + // It is a primary image, so it needs a base address. + cmd.FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress()) + } + + // We always expect a preloaded classes file to be available. However, if we cannot find it, it's + // OK to not pass the flag to dex2oat. + preloadedClassesPath := android.ExistentPathForSource(ctx, image.preloadedClassesFile) + if preloadedClassesPath.Valid() { + cmd.FlagWithInput("--preloaded-classes=", preloadedClassesPath.Path()) + } + + cmd. + FlagForEachInput("--dex-file=", image.dexPaths.Paths()). + FlagForEachArg("--dex-location=", image.dexLocations). + Flag("--generate-debug-info"). + Flag("--generate-build-id"). + Flag("--image-format=lz4hc"). + FlagWithArg("--oat-symbols=", symbolsFile.String()). + Flag("--strip"). + FlagWithArg("--oat-file=", outputPath.String()). + FlagWithArg("--oat-location=", oatLocation). + FlagWithArg("--image=", imagePath.String()). + FlagWithArg("--instruction-set=", arch.String()). + FlagWithArg("--android-root=", global.EmptyDirectory). + FlagWithArg("--no-inline-from=", "core-oj.jar"). + Flag("--force-determinism"). + Flag("--abort-on-hard-verifier-error") + + // Use the default variant/features for host builds. + // The map below contains only device CPU info (which might be x86 on some devices). + if image.target.Os == android.Android { + cmd.FlagWithArg("--instruction-set-variant=", global.CpuVariant[arch]) + cmd.FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch]) + } + + if global.BootFlags != "" { + cmd.Flag(global.BootFlags) + } + + if extraFlags != "" { + cmd.Flag(extraFlags) + } + + cmd.Textf(`|| ( echo %s ; false )`, proptools.ShellEscape(failureMessage)) + + installDir := filepath.Join("/", image.installDirOnHost, arch.String()) + + var vdexInstalls android.RuleBuilderInstalls + var unstrippedInstalls android.RuleBuilderInstalls + var deviceInstalls android.RuleBuilderInstalls + + for _, artOrOat := range image.moduleFiles(ctx, outputDir, ".art", ".oat") { + cmd.ImplicitOutput(artOrOat) + + // Install the .oat and .art files + rule.Install(artOrOat, filepath.Join(installDir, artOrOat.Base())) + } + + for _, vdex := range image.moduleFiles(ctx, outputDir, ".vdex") { + cmd.ImplicitOutput(vdex) + + // Note that the vdex files are identical between architectures. + // Make rules will create symlinks to share them between architectures. + vdexInstalls = append(vdexInstalls, + android.RuleBuilderInstall{vdex, filepath.Join(installDir, vdex.Base())}) + } + + for _, unstrippedOat := range image.moduleFiles(ctx, symbolsDir, ".oat") { + cmd.ImplicitOutput(unstrippedOat) + + // Install the unstripped oat files. The Make rules will put these in $(TARGET_OUT_UNSTRIPPED) + unstrippedInstalls = append(unstrippedInstalls, + android.RuleBuilderInstall{unstrippedOat, filepath.Join(installDir, unstrippedOat.Base())}) + } + + if image.installDirOnHost != image.installDirOnDevice && !image.shouldInstallInApex() && !ctx.Config().UnbundledBuild() { + installDirOnDevice := filepath.Join("/", image.installDirOnDevice, arch.String()) + for _, file := range image.moduleFiles(ctx, outputDir, ".art", ".oat", ".vdex") { + deviceInstalls = append(deviceInstalls, + android.RuleBuilderInstall{file, filepath.Join(installDirOnDevice, file.Base())}) + } + } + + 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() + image.vdexInstalls = vdexInstalls + image.unstrippedInstalls = unstrippedInstalls + image.deviceInstalls = deviceInstalls + image.licenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile()) +} + +const failureMessage = `ERROR: Dex2oat failed to compile a boot image. +It is likely that the boot classpath is inconsistent. +Rebuild with ART_BOOT_IMAGE_EXTRA_ARGS="--runtime-arg -verbose:verifier" to see verification errors.` + +func bootImageProfileRule(ctx android.ModuleContext, image *bootImageConfig) android.WritablePath { + globalSoong := dexpreopt.GetGlobalSoongConfig(ctx) + global := dexpreopt.GetGlobalConfig(ctx) + + if global.DisableGenerateProfile { + return nil + } + + defaultProfile := "frameworks/base/config/boot-image-profile.txt" + + rule := android.NewRuleBuilder(pctx, ctx) + + var bootImageProfile android.Path + if len(global.BootImageProfiles) > 1 { + combinedBootImageProfile := image.dir.Join(ctx, "boot-image-profile.txt") + rule.Command().Text("cat").Inputs(global.BootImageProfiles).Text(">").Output(combinedBootImageProfile) + bootImageProfile = combinedBootImageProfile + } else if len(global.BootImageProfiles) == 1 { + bootImageProfile = global.BootImageProfiles[0] + } else if path := android.ExistentPathForSource(ctx, defaultProfile); path.Valid() { + bootImageProfile = path.Path() + } else { + // No profile (not even a default one, which is the case on some branches + // like master-art-host that don't have frameworks/base). + // Return nil and continue without profile. + return nil + } + + profile := image.dir.Join(ctx, "boot.prof") + + rule.Command(). + Text(`ANDROID_LOG_TAGS="*:e"`). + Tool(globalSoong.Profman). + Flag("--output-profile-type=boot"). + FlagWithInput("--create-profile-from=", bootImageProfile). + FlagForEachInput("--apk=", image.dexPathsDeps.Paths()). + FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps). + FlagWithOutput("--reference-profile-file=", profile) + + if image == defaultBootImageConfig(ctx) { + rule.Install(profile, "/system/etc/boot-image.prof") + image.profileInstalls = append(image.profileInstalls, rule.Installs()...) + image.profileLicenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile()) + } + + rule.Build("bootJarsProfile", "profile boot jars") + + image.profilePathOnHost = profile + + return profile +} + +// bootFrameworkProfileRule generates the rule to create the boot framework profile and +// returns a path to the generated file. +func bootFrameworkProfileRule(ctx android.ModuleContext, image *bootImageConfig) android.WritablePath { + globalSoong := dexpreopt.GetGlobalSoongConfig(ctx) + global := dexpreopt.GetGlobalConfig(ctx) + + if global.DisableGenerateProfile || ctx.Config().UnbundledBuild() { + return nil + } + + defaultProfile := "frameworks/base/config/boot-profile.txt" + bootFrameworkProfile := android.PathForSource(ctx, defaultProfile) + + profile := image.dir.Join(ctx, "boot.bprof") + + rule := android.NewRuleBuilder(pctx, ctx) + rule.Command(). + Text(`ANDROID_LOG_TAGS="*:e"`). + Tool(globalSoong.Profman). + Flag("--output-profile-type=bprof"). + FlagWithInput("--create-profile-from=", bootFrameworkProfile). + FlagForEachInput("--apk=", image.dexPathsDeps.Paths()). + FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps). + FlagWithOutput("--reference-profile-file=", profile) + + rule.Install(profile, "/system/etc/boot-image.bprof") + rule.Build("bootFrameworkProfile", "profile boot framework jars") + image.profileInstalls = append(image.profileInstalls, rule.Installs()...) + image.profileLicenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile()) + + return profile +} + +func dumpOatRules(ctx android.ModuleContext, image *bootImageConfig) { + var allPhonies android.Paths + for _, image := range image.variants { + arch := image.target.Arch.ArchType + suffix := arch.String() + // Host and target might both use x86 arch. We need to ensure the names are unique. + if image.target.Os.Class == android.Host { + suffix = "host-" + suffix + } + // Create a rule to call oatdump. + output := android.PathForOutput(ctx, "boot."+suffix+".oatdump.txt") + rule := android.NewRuleBuilder(pctx, ctx) + imageLocationsOnHost, _ := image.imageLocations() + rule.Command(). + BuiltTool("oatdump"). + FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPathsDeps.Paths(), ":"). + FlagWithList("--runtime-arg -Xbootclasspath-locations:", image.dexLocationsDeps, ":"). + FlagWithArg("--image=", strings.Join(imageLocationsOnHost, ":")).Implicits(image.imagesDeps.Paths()). + FlagWithOutput("--output=", output). + FlagWithArg("--instruction-set=", 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(pctx, ctx) + rule.Command(). + Implicit(output). + ImplicitOutput(phony). + Text("echo").FlagWithArg("Output in ", output.String()) + rule.Build("phony-dump-oat-boot-"+suffix, "dump oat boot "+arch.String()) + + allPhonies = append(allPhonies, phony) + } + + phony := android.PathForPhony(ctx, "dump-oat-boot") + ctx.Build(pctx, android.BuildParams{ + Rule: android.Phony, + Output: phony, + Inputs: allPhonies, + Description: "dump-oat-boot", + }) +} + +func writeGlobalConfigForMake(ctx android.SingletonContext, path android.WritablePath) { + data := dexpreopt.GetGlobalConfigRawData(ctx) + + android.WriteFileRule(ctx, path, string(data)) +} + +// 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()) + ctx.Strict("DEX_PREOPT_SOONG_CONFIG_FOR_MAKE", android.PathForOutput(ctx, "dexpreopt_soong.config").String()) + } + + image := d.defaultBootImage + if image == nil { + return + } + + ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", image.profileInstalls.String()) + if image.profileLicenseMetadataFile.Valid() { + ctx.Strict("DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA", image.profileLicenseMetadataFile.String()) + } + + global := dexpreopt.GetGlobalConfig(ctx) + dexPaths, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp) + ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(dexPaths.Strings(), " ")) + ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(dexLocations, " ")) + + for _, variant := range image.variants { + suffix := "" + if variant.target.Os.Class == android.Host { + suffix = "_host" + } + sfx := suffix + "_" + variant.target.Arch.ArchType.String() + ctx.Strict("DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_"+sfx, variant.vdexInstalls.String()) + ctx.Strict("DEXPREOPT_IMAGE_"+sfx, variant.imagePathOnHost.String()) + ctx.Strict("DEXPREOPT_IMAGE_DEPS_"+sfx, strings.Join(variant.imagesDeps.Strings(), " ")) + ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+sfx, variant.installs.String()) + ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+sfx, variant.unstrippedInstalls.String()) + if variant.licenseMetadataFile.Valid() { + ctx.Strict("DEXPREOPT_IMAGE_LICENSE_METADATA_"+sfx, variant.licenseMetadataFile.String()) + } + } + imageLocationsOnHost, imageLocationsOnDevice := image.getAnyAndroidVariant().imageLocations() + ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_HOST", strings.Join(imageLocationsOnHost, ":")) + ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICE", strings.Join(imageLocationsOnDevice, ":")) + ctx.Strict("DEXPREOPT_IMAGE_ZIP", image.zip.String()) + + // There used to be multiple images for JIT-Zygote mode, not there's only one. + ctx.Strict("DEXPREOPT_IMAGE_NAMES", image.name) +} diff --git a/java/dexpreopt_config.go_v1 b/java/dexpreopt_config.go_v1 new file mode 100644 index 000000000..d71e2bbfd --- /dev/null +++ b/java/dexpreopt_config.go_v1 @@ -0,0 +1,215 @@ +// Copyright 2019 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 ( + "path/filepath" + "strings" + + "android/soong/android" + "android/soong/dexpreopt" +) + +// dexpreoptTargets returns the list of targets that are relevant to dexpreopting, which excludes architectures +// supported through native bridge. +func dexpreoptTargets(ctx android.PathContext) []android.Target { + var targets []android.Target + for _, target := range ctx.Config().Targets[android.Android] { + if target.NativeBridge == android.NativeBridgeDisabled { + targets = append(targets, target) + } + } + // We may also need the images on host in order to run host-based tests. + for _, target := range ctx.Config().Targets[ctx.Config().BuildOS] { + targets = append(targets, target) + } + + return targets +} + +var ( + bootImageConfigKey = android.NewOnceKey("bootImageConfig") + bootImageConfigRawKey = android.NewOnceKey("bootImageConfigRaw") + artBootImageName = "art" + frameworkBootImageName = "boot" +) + +func genBootImageConfigRaw(ctx android.PathContext) map[string]*bootImageConfig { + return ctx.Config().Once(bootImageConfigRawKey, func() interface{} { + global := dexpreopt.GetGlobalConfig(ctx) + + artModules := global.ArtApexJars + frameworkModules := global.BootJars.RemoveList(artModules) + + // ART config for the primary boot image in the ART apex. + // It includes the Core Libraries. + artCfg := bootImageConfig{ + name: artBootImageName, + stem: "boot", + installDirOnHost: "apex/art_boot_images/javalib", + installDirOnDevice: "system/framework", + profileInstallPathInApex: "etc/boot-image.prof", + modules: artModules, + preloadedClassesFile: "art/build/boot/preloaded-classes", + } + + // Framework config for the boot image extension. + // It includes framework libraries and depends on the ART config. + frameworkSubdir := "system/framework" + frameworkCfg := bootImageConfig{ + extends: &artCfg, + name: frameworkBootImageName, + stem: "boot", + installDirOnHost: frameworkSubdir, + installDirOnDevice: frameworkSubdir, + modules: frameworkModules, + preloadedClassesFile: "frameworks/base/config/preloaded-classes", + } + + return map[string]*bootImageConfig{ + artBootImageName: &artCfg, + frameworkBootImageName: &frameworkCfg, + } + }).(map[string]*bootImageConfig) +} + +// Construct the global boot image configs. +func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig { + return ctx.Config().Once(bootImageConfigKey, func() interface{} { + targets := dexpreoptTargets(ctx) + deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName()) + + configs := genBootImageConfigRaw(ctx) + artCfg := configs[artBootImageName] + frameworkCfg := configs[frameworkBootImageName] + + // common to all configs + for _, c := range configs { + c.dir = deviceDir.Join(ctx, "dex_"+c.name+"jars") + c.symbolsDir = deviceDir.Join(ctx, "dex_"+c.name+"jars_unstripped") + + // expands to <stem>.art for primary image and <stem>-<1st module>.art for extension + imageName := c.firstModuleNameOrStem(ctx) + ".art" + + // The path to bootclasspath dex files needs to be known at module + // GenerateAndroidBuildAction time, before the bootclasspath modules have been compiled. + // Set up known paths for them, the singleton rules will copy them there. + // TODO(b/143682396): use module dependencies instead + inputDir := deviceDir.Join(ctx, "dex_"+c.name+"jars_input") + c.dexPaths = c.modules.BuildPaths(ctx, inputDir) + c.dexPathsByModule = c.modules.BuildPathsByModule(ctx, inputDir) + c.dexPathsDeps = c.dexPaths + + // Create target-specific variants. + for _, target := range targets { + arch := target.Arch.ArchType + imageDir := c.dir.Join(ctx, target.Os.String(), c.installDirOnHost, arch.String()) + variant := &bootImageVariant{ + bootImageConfig: c, + target: target, + imagePathOnHost: imageDir.Join(ctx, imageName), + imagePathOnDevice: filepath.Join("/", c.installDirOnDevice, arch.String(), imageName), + imagesDeps: c.moduleFiles(ctx, imageDir, ".art", ".oat", ".vdex"), + dexLocations: c.modules.DevicePaths(ctx.Config(), target.Os), + } + variant.dexLocationsDeps = variant.dexLocations + c.variants = append(c.variants, variant) + } + + c.zip = c.dir.Join(ctx, c.name+".zip") + } + + // specific to the framework config + frameworkCfg.dexPathsDeps = append(artCfg.dexPathsDeps, frameworkCfg.dexPathsDeps...) + for i := range targets { + frameworkCfg.variants[i].primaryImages = artCfg.variants[i].imagePathOnHost + frameworkCfg.variants[i].primaryImagesDeps = artCfg.variants[i].imagesDeps.Paths() + frameworkCfg.variants[i].dexLocationsDeps = append(artCfg.variants[i].dexLocations, frameworkCfg.variants[i].dexLocationsDeps...) + } + + return configs + }).(map[string]*bootImageConfig) +} + +func defaultBootImageConfig(ctx android.PathContext) *bootImageConfig { + return genBootImageConfigs(ctx)[frameworkBootImageName] +} + +// Apex boot config allows to access build/install paths of apex boot jars without going +// through the usual trouble of registering dependencies on those modules and extracting build paths +// from those dependencies. +type apexBootConfig struct { + // A list of apex boot jars. + modules android.ConfiguredJarList + + // A list of predefined build paths to apex boot jars. They are configured very early, + // before the modules for these jars are processed and the actual paths are generated, and + // later on a singleton adds commands to copy actual jars to the predefined paths. + dexPaths android.WritablePaths + + // Map from module name (without prebuilt_ prefix) to the predefined build path. + dexPathsByModule map[string]android.WritablePath + + // A list of dex locations (a.k.a. on-device paths) to the boot jars. + dexLocations []string +} + +var updatableBootConfigKey = android.NewOnceKey("apexBootConfig") + +// Returns apex boot config. +func GetApexBootConfig(ctx android.PathContext) apexBootConfig { + return ctx.Config().Once(updatableBootConfigKey, func() interface{} { + apexBootJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars + + dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "apex_bootjars") + dexPaths := apexBootJars.BuildPaths(ctx, dir) + dexPathsByModuleName := apexBootJars.BuildPathsByModule(ctx, dir) + + dexLocations := apexBootJars.DevicePaths(ctx.Config(), android.Android) + + return apexBootConfig{apexBootJars, dexPaths, dexPathsByModuleName, dexLocations} + }).(apexBootConfig) +} + +// Returns a list of paths and a list of locations for the boot jars used in dexpreopt (to be +// passed in -Xbootclasspath and -Xbootclasspath-locations arguments for dex2oat). +func bcpForDexpreopt(ctx android.PathContext, withUpdatable bool) (android.WritablePaths, []string) { + // Non-updatable boot jars (they are used both in the boot image and in dexpreopt). + bootImage := defaultBootImageConfig(ctx) + dexPaths := bootImage.dexPathsDeps + // The dex locations for all Android variants are identical. + dexLocations := bootImage.getAnyAndroidVariant().dexLocationsDeps + + if withUpdatable { + // Apex boot jars (they are used only in dexpreopt, but not in the boot image). + apexBootConfig := GetApexBootConfig(ctx) + dexPaths = append(dexPaths, apexBootConfig.dexPaths...) + dexLocations = append(dexLocations, apexBootConfig.dexLocations...) + } + + return dexPaths, dexLocations +} + +var defaultBootclasspathKey = android.NewOnceKey("defaultBootclasspath") + +var copyOf = android.CopyOf + +func init() { + android.RegisterMakeVarsProvider(pctx, dexpreoptConfigMakevars) +} + +func dexpreoptConfigMakevars(ctx android.MakeVarsContext) { + ctx.Strict("DEXPREOPT_BOOT_JARS_MODULES", strings.Join(defaultBootImageConfig(ctx).modules.CopyOfApexJarPairs(), ":")) +} diff --git a/java/fuzz.go b/java/fuzz.go index 257f34356..584c80b0c 100644 --- a/java/fuzz.go +++ b/java/fuzz.go @@ -15,14 +15,25 @@ package java import ( - "github.com/google/blueprint/proptools" "sort" "strings" + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" + "android/soong/android" + "android/soong/cc" "android/soong/fuzz" ) +type jniProperties struct { + // list of jni libs + Jni_libs []string + + // sanitization + Sanitizers []string +} + func init() { RegisterJavaFuzzBuildComponents(android.InitRegistrationContext) } @@ -35,11 +46,60 @@ func RegisterJavaFuzzBuildComponents(ctx android.RegistrationContext) { type JavaFuzzLibrary struct { Library fuzzPackagedModule fuzz.FuzzPackagedModule + jniProperties jniProperties + jniFilePaths android.Paths } -func (j *JavaFuzzLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { - j.Library.GenerateAndroidBuildActions(ctx) +// IsSanitizerEnabled implemented to make JavaFuzzLibrary implement +// cc.Sanitizeable +func (j *JavaFuzzLibrary) IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizerName string) bool { + for _, s := range j.jniProperties.Sanitizers { + if sanitizerName == s { + return true + } + } + return false +} + +// IsSanitizerEnabledForJni implemented to make JavaFuzzLibrary implement +// cc.JniSanitizeable. It returns a bool for whether a cc dependency should be +// sanitized for the given sanitizer or not. +func (j *JavaFuzzLibrary) IsSanitizerEnabledForJni(ctx android.BaseModuleContext, sanitizerName string) bool { + return j.IsSanitizerEnabled(ctx, sanitizerName) +} + +// EnableSanitizer implemented to make JavaFuzzLibrary implement +// cc.Sanitizeable +func (j *JavaFuzzLibrary) EnableSanitizer(sanitizerName string) { +} + +// AddSanitizerDependencies implemented to make JavaFuzzLibrary implement +// cc.Sanitizeable +func (j *JavaFuzzLibrary) AddSanitizerDependencies(mctx android.BottomUpMutatorContext, sanitizerName string) { +} + +// To verify that JavaFuzzLibrary implements cc.Sanitizeable +var _ cc.Sanitizeable = (*JavaFuzzLibrary)(nil) + +func (j *JavaFuzzLibrary) DepsMutator(mctx android.BottomUpMutatorContext) { + if len(j.jniProperties.Jni_libs) > 0 { + if j.fuzzPackagedModule.FuzzProperties.Fuzz_config == nil { + config := &fuzz.FuzzConfig{} + j.fuzzPackagedModule.FuzzProperties.Fuzz_config = config + } + // this will be used by the ingestion pipeline to determine the version + // of jazzer to add to the fuzzer package + j.fuzzPackagedModule.FuzzProperties.Fuzz_config.IsJni = proptools.BoolPtr(true) + + for _, target := range mctx.MultiTargets() { + sharedLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"}) + mctx.AddFarVariationDependencies(sharedLibVariations, cc.JniFuzzLibTag, j.jniProperties.Jni_libs...) + } + } + j.Library.DepsMutator(mctx) +} +func (j *JavaFuzzLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { if j.fuzzPackagedModule.FuzzProperties.Corpus != nil { j.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Corpus) } @@ -55,6 +115,23 @@ func (j *JavaFuzzLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) android.WriteFileRule(ctx, configPath, j.fuzzPackagedModule.FuzzProperties.Fuzz_config.String()) j.fuzzPackagedModule.Config = configPath } + + ctx.VisitDirectDepsWithTag(cc.JniFuzzLibTag, func(dep android.Module) { + sharedLibInfo := ctx.OtherModuleProvider(dep, cc.SharedLibraryInfoProvider).(cc.SharedLibraryInfo) + if sharedLibInfo.SharedLibrary != nil { + libPath := android.PathForModuleOut(ctx, sharedLibInfo.SharedLibrary.Base()) + ctx.Build(pctx, android.BuildParams{ + Rule: android.Cp, + Input: sharedLibInfo.SharedLibrary, + Output: libPath, + }) + j.jniFilePaths = append(j.jniFilePaths, libPath) + } else { + ctx.PropertyErrorf("jni_libs", "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep)) + } + }) + + j.Library.GenerateAndroidBuildActions(ctx) } // java_fuzz builds and links sources into a `.jar` file for the host. @@ -65,7 +142,8 @@ func FuzzFactory() android.Module { module := &JavaFuzzLibrary{} module.addHostProperties() - module.Module.properties.Installable = proptools.BoolPtr(false) + module.AddProperties(&module.jniProperties) + module.Module.properties.Installable = proptools.BoolPtr(true) module.AddProperties(&module.fuzzPackagedModule.FuzzProperties) // java_fuzz packaging rules collide when both linux_glibc and linux_bionic are enabled, disable the linux_bionic variants. @@ -83,7 +161,7 @@ func FuzzFactory() android.Module { module.initModuleAndImport(module) android.InitSdkAwareModule(module) - InitJavaModule(module, android.HostSupported) + InitJavaModuleMultiTargets(module, android.HostSupported) return module } @@ -106,26 +184,26 @@ func (s *javaFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) { ctx.VisitAllModules(func(module android.Module) { // Discard non-fuzz targets. - javaModule, ok := module.(*JavaFuzzLibrary) + javaFuzzModule, ok := module.(*JavaFuzzLibrary) if !ok { return } fuzzModuleValidator := fuzz.FuzzModule{ - javaModule.ModuleBase, - javaModule.DefaultableModuleBase, - javaModule.ApexModuleBase, + javaFuzzModule.ModuleBase, + javaFuzzModule.DefaultableModuleBase, + javaFuzzModule.ApexModuleBase, } - if ok := fuzz.IsValid(fuzzModuleValidator); !ok || *javaModule.Module.properties.Installable { + if ok := fuzz.IsValid(fuzzModuleValidator); !ok { return } hostOrTargetString := "target" - if javaModule.Host() { + if javaFuzzModule.Host() { hostOrTargetString = "host" } - archString := javaModule.Arch().ArchType.String() + archString := javaFuzzModule.Arch().ArchType.String() archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString) archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()} @@ -134,12 +212,17 @@ func (s *javaFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) { builder := android.NewRuleBuilder(pctx, ctx) // Package the artifacts (data, corpus, config and dictionary into a zipfile. - files = s.PackageArtifacts(ctx, module, javaModule.fuzzPackagedModule, archDir, builder) + files = s.PackageArtifacts(ctx, module, javaFuzzModule.fuzzPackagedModule, archDir, builder) // Add .jar - files = append(files, fuzz.FileToZip{javaModule.outputFile, ""}) + files = append(files, fuzz.FileToZip{javaFuzzModule.outputFile, ""}) + + // Add jni .so files + for _, fPath := range javaFuzzModule.jniFilePaths { + files = append(files, fuzz.FileToZip{fPath, ""}) + } - archDirs[archOs], ok = s.BuildZipFile(ctx, module, javaModule.fuzzPackagedModule, files, builder, archDir, archString, "host", archOs, archDirs) + archDirs[archOs], ok = s.BuildZipFile(ctx, module, javaFuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs) if !ok { return } diff --git a/java/fuzz_test.go b/java/fuzz_test.go index cf063ebe2..0a2c94517 100644 --- a/java/fuzz_test.go +++ b/java/fuzz_test.go @@ -15,13 +15,17 @@ package java import ( - "android/soong/android" "path/filepath" + "runtime" "testing" + + "android/soong/android" + "android/soong/cc" ) var prepForJavaFuzzTest = android.GroupFixturePreparers( PrepareForTestWithJavaDefaultModules, + cc.PrepareForTestWithCcBuildComponents, android.FixtureRegisterWithContext(RegisterJavaFuzzBuildComponents), ) @@ -32,6 +36,13 @@ func TestJavaFuzz(t *testing.T) { srcs: ["a.java"], libs: ["bar"], static_libs: ["baz"], + jni_libs: [ + "libjni", + ], + sanitizers: [ + "address", + "fuzzer", + ], } java_library_host { @@ -42,11 +53,21 @@ func TestJavaFuzz(t *testing.T) { java_library_host { name: "baz", srcs: ["c.java"], - }`) + } + + cc_library_shared { + name: "libjni", + host_supported: true, + device_supported: false, + stl: "none", + } + `) osCommonTarget := result.Config.BuildOSCommonTarget.String() - javac := result.ModuleForTests("foo", osCommonTarget).Rule("javac") - combineJar := result.ModuleForTests("foo", osCommonTarget).Description("for javac") + + osCommonTargetWithSan := osCommonTarget + "_asan" + "_fuzzer" + javac := result.ModuleForTests("foo", osCommonTargetWithSan).Rule("javac") + combineJar := result.ModuleForTests("foo", osCommonTargetWithSan).Description("for javac") if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" { t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs) @@ -62,4 +83,18 @@ func TestJavaFuzz(t *testing.T) { if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != baz { t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, baz) } + + ctx := result.TestContext + foo := ctx.ModuleForTests("foo", osCommonTargetWithSan).Module().(*JavaFuzzLibrary) + + expected := "libjni.so" + if runtime.GOOS == "darwin" { + expected = "libjni.dylib" + } + + fooJniFilePaths := foo.jniFilePaths + if len(fooJniFilePaths) != 1 || fooJniFilePaths[0].Rel() != expected { + t.Errorf(`expected foo test data relative path [%q], got %q`, + expected, fooJniFilePaths.Strings()) + } } diff --git a/java/hiddenapi.go b/java/hiddenapi.go index 3af5f1c7b..cf9c7ad7a 100644 --- a/java/hiddenapi.go +++ b/java/hiddenapi.go @@ -65,6 +65,8 @@ func (h *hiddenAPI) uncompressDex() *bool { type hiddenAPIModule interface { android.Module hiddenAPIIntf + + MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec } type hiddenAPIIntf interface { @@ -148,7 +150,7 @@ func (h *hiddenAPI) hiddenAPIEncodeDex(ctx android.ModuleContext, dexJar android // Create a copy of the dex jar which has been encoded with hiddenapi flags. flagsCSV := hiddenAPISingletonPaths(ctx).flags outputDir := android.PathForModuleOut(ctx, "hiddenapi").OutputPath - encodedDex := hiddenAPIEncodeDex(ctx, dexJar, flagsCSV, uncompressDex, outputDir) + encodedDex := hiddenAPIEncodeDex(ctx, dexJar, flagsCSV, uncompressDex, android.SdkSpecNone, outputDir) // Use the encoded dex jar from here onwards. return encodedDex @@ -246,7 +248,7 @@ var hiddenAPIEncodeDexRule = pctx.AndroidStaticRule("hiddenAPIEncodeDex", bluepr // The encode dex rule requires unzipping, encoding and rezipping the classes.dex files along with // all the resources from the input jar. It also ensures that if it was uncompressed in the input // it stays uncompressed in the output. -func hiddenAPIEncodeDex(ctx android.ModuleContext, dexInput, flagsCSV android.Path, uncompressDex bool, outputDir android.OutputPath) android.OutputPath { +func hiddenAPIEncodeDex(ctx android.ModuleContext, dexInput, flagsCSV android.Path, uncompressDex bool, minSdkVersion android.SdkSpec, outputDir android.OutputPath) android.OutputPath { // The output file has the same name as the input file and is in the output directory. output := outputDir.Join(ctx, dexInput.Base()) @@ -274,6 +276,15 @@ func hiddenAPIEncodeDex(ctx android.ModuleContext, dexInput, flagsCSV android.Pa hiddenapiFlags = "--no-force-assign-all" } + // If the library is targeted for Q and/or R then make sure that they do not + // have any S+ flags encoded as that will break the runtime. + minApiLevel := minSdkVersion.ApiLevel + if !minApiLevel.IsNone() { + if minApiLevel.LessThanOrEqualTo(android.ApiLevelOrPanic(ctx, "R")) { + hiddenapiFlags = hiddenapiFlags + " --max-hiddenapi-level=max-target-r" + } + } + ctx.Build(pctx, android.BuildParams{ Rule: hiddenAPIEncodeDexRule, Description: "hiddenapi encode dex", diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go index 534a8145f..c90b2ff97 100644 --- a/java/hiddenapi_modular.go +++ b/java/hiddenapi_modular.go @@ -1104,7 +1104,7 @@ func hiddenAPIRulesForBootclasspathFragment(ctx android.ModuleContext, contents for _, name := range android.SortedStringKeys(bootDexInfoByModule) { bootDexInfo := bootDexInfoByModule[name] unencodedDex := bootDexInfo.path - encodedDex := hiddenAPIEncodeDex(ctx, unencodedDex, allFlagsCSV, bootDexInfo.uncompressDex, outputDir) + encodedDex := hiddenAPIEncodeDex(ctx, unencodedDex, allFlagsCSV, bootDexInfo.uncompressDex, bootDexInfo.minSdkVersion, outputDir) encodedBootDexJarsByModule[name] = encodedDex } @@ -1188,6 +1188,9 @@ type bootDexInfo struct { // Indicates whether the dex jar needs uncompressing before encoding. uncompressDex bool + + // The minimum sdk version that the dex jar will be used on. + minSdkVersion android.SdkSpec } // bootDexInfoByModule is a map from module name (as returned by module.Name()) to the boot dex @@ -1213,6 +1216,7 @@ func extractBootDexInfoFromModules(ctx android.ModuleContext, contents []android bootDexJarsByModule[module.Name()] = bootDexInfo{ path: bootDexJar, uncompressDex: *hiddenAPIModule.uncompressDex(), + minSdkVersion: hiddenAPIModule.MinSdkVersion(ctx), } } diff --git a/java/java.go b/java/java.go index b34d6de8a..13f4c807e 100644 --- a/java/java.go +++ b/java/java.go @@ -300,19 +300,11 @@ var _ android.LicenseAnnotationsDependencyTag = dependencyTag{} type usesLibraryDependencyTag struct { dependencyTag - - // SDK version in which the library appared as a standalone library. - sdkVersion int - - // If the dependency is optional or required. - optional bool - - // Whether this is an implicit dependency inferred by Soong, or an explicit one added via - // `uses_libs`/`optional_uses_libs` properties. - implicit bool + sdkVersion int // SDK version in which the library appared as a standalone library. + optional bool // If the dependency is optional or required. } -func makeUsesLibraryDependencyTag(sdkVersion int, optional bool, implicit bool) usesLibraryDependencyTag { +func makeUsesLibraryDependencyTag(sdkVersion int, optional bool) usesLibraryDependencyTag { return usesLibraryDependencyTag{ dependencyTag: dependencyTag{ name: fmt.Sprintf("uses-library-%d", sdkVersion), @@ -320,7 +312,6 @@ func makeUsesLibraryDependencyTag(sdkVersion int, optional bool, implicit bool) }, sdkVersion: sdkVersion, optional: optional, - implicit: implicit, } } @@ -351,6 +342,11 @@ var ( syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"} jniInstallTag = installDependencyTag{name: "jni install"} binaryInstallTag = installDependencyTag{name: "binary install"} + usesLibReqTag = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, false) + usesLibOptTag = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, true) + usesLibCompat28OptTag = makeUsesLibraryDependencyTag(28, true) + usesLibCompat29ReqTag = makeUsesLibraryDependencyTag(29, false) + usesLibCompat30OptTag = makeUsesLibraryDependencyTag(30, true) ) func IsLibDepTag(depTag blueprint.DependencyTag) bool { @@ -1996,10 +1992,8 @@ func addCLCFromDep(ctx android.ModuleContext, depModule android.Module, depTag := ctx.OtherModuleDependencyTag(depModule) if depTag == libTag { // Ok, propagate <uses-library> through non-static library dependencies. - } else if tag, ok := depTag.(usesLibraryDependencyTag); ok && - tag.sdkVersion == dexpreopt.AnySdkVersion && tag.implicit { - // Ok, propagate <uses-library> through non-compatibility implicit <uses-library> - // dependencies. + } else if tag, ok := depTag.(usesLibraryDependencyTag); ok && tag.sdkVersion == dexpreopt.AnySdkVersion { + // Ok, propagate <uses-library> through non-compatibility <uses-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, @@ -2017,7 +2011,7 @@ func addCLCFromDep(ctx android.ModuleContext, depModule android.Module, // <uses_library> and should not be added to CLC, but the transitive <uses-library> dependencies // from its CLC should be added to the current CLC. if sdkLib != nil { - clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, false, true, + clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, false, dep.DexJarBuildPath().PathOrNil(), dep.DexJarInstallPath(), dep.ClassLoaderContexts()) } else { clcMap.AddContextMap(dep.ClassLoaderContexts(), depName) @@ -2089,6 +2083,11 @@ func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) if m.properties.Javacflags != nil { javacopts = append(javacopts, m.properties.Javacflags...) } + if m.properties.Java_version != nil { + javaVersion := normalizeJavaVersion(ctx, *m.properties.Java_version).String() + javacopts = append(javacopts, fmt.Sprintf("-source %s -target %s", javaVersion, javaVersion)) + } + epEnabled := m.properties.Errorprone.Enabled //TODO(b/227504307) add configuration that depends on RUN_ERROR_PRONE environment variable if Bool(epEnabled) { diff --git a/java/kotlin.go b/java/kotlin.go index eff5bb53f..903c6249b 100644 --- a/java/kotlin.go +++ b/java/kotlin.go @@ -175,6 +175,7 @@ func kotlinKapt(ctx android.ModuleContext, srcJarOutputFile, resJarOutputFile an var deps android.Paths deps = append(deps, flags.kotlincClasspath...) + deps = append(deps, flags.kotlincDeps...) deps = append(deps, srcJars...) deps = append(deps, flags.processorPath...) deps = append(deps, commonSrcFiles...) diff --git a/java/kotlin_test.go b/java/kotlin_test.go index f9ff98229..435d78294 100644 --- a/java/kotlin_test.go +++ b/java/kotlin_test.go @@ -325,6 +325,7 @@ func TestKotlinCompose(t *testing.T) { java_library { name: "withcompose", srcs: ["a.kt"], + plugins: ["plugin"], static_libs: ["androidx.compose.runtime_runtime"], } @@ -332,6 +333,10 @@ func TestKotlinCompose(t *testing.T) { name: "nocompose", srcs: ["a.kt"], } + + java_plugin { + name: "plugin", + } `) buildOS := result.Config.BuildOS.String() @@ -346,6 +351,9 @@ func TestKotlinCompose(t *testing.T) { android.AssertStringDoesContain(t, "missing compose compiler plugin", withCompose.VariablesForTestsRelativeToTop()["kotlincFlags"], "-Xplugin="+composeCompiler.String()) + android.AssertStringListContains(t, "missing kapt compose compiler dependency", + withCompose.Rule("kapt").Implicits.Strings(), composeCompiler.String()) + android.AssertStringListDoesNotContain(t, "unexpected compose compiler dependency", noCompose.Rule("kotlinc").Implicits.Strings(), composeCompiler.String()) diff --git a/java/lint.go b/java/lint.go index e97c9c225..426a2af25 100644 --- a/java/lint.go +++ b/java/lint.go @@ -75,9 +75,9 @@ type linter struct { extraLintCheckJars android.Paths test bool library bool - minSdkVersion string - targetSdkVersion string - compileSdkVersion string + minSdkVersion android.ApiLevel + targetSdkVersion android.ApiLevel + compileSdkVersion android.ApiLevel compileSdkKind android.SdkKind javaLanguageLevel string kotlinLanguageLevel string @@ -300,7 +300,7 @@ func (l *linter) generateManifest(ctx android.ModuleContext, rule *android.RuleB Text(`echo "<manifest xmlns:android='http://schemas.android.com/apk/res/android'" &&`). Text(`echo " android:versionCode='1' android:versionName='1' >" &&`). Textf(`echo " <uses-sdk android:minSdkVersion='%s' android:targetSdkVersion='%s'/>" &&`, - l.minSdkVersion, l.targetSdkVersion). + l.minSdkVersion.String(), l.targetSdkVersion.String()). Text(`echo "</manifest>"`). Text(") >").Output(manifestPath) @@ -325,7 +325,7 @@ func (l *linter) lint(ctx android.ModuleContext) { return } - if l.minSdkVersion != l.compileSdkVersion { + if l.minSdkVersion.CompareTo(l.compileSdkVersion) == -1 { l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, updatabilityChecks...) _, filtered := android.FilterList(l.properties.Lint.Warning_checks, updatabilityChecks) if len(filtered) != 0 { @@ -427,7 +427,7 @@ func (l *linter) lint(ctx android.ModuleContext) { FlagWithOutput("--html ", html). FlagWithOutput("--text ", text). FlagWithOutput("--xml ", xml). - FlagWithArg("--compile-sdk-version ", l.compileSdkVersion). + FlagWithArg("--compile-sdk-version ", l.compileSdkVersion.String()). FlagWithArg("--java-language-level ", l.javaLanguageLevel). FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel). FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())). diff --git a/java/lint_defaults.txt b/java/lint_defaults.txt index 4bc0c5fd4..1eee354cd 100644 --- a/java/lint_defaults.txt +++ b/java/lint_defaults.txt @@ -28,6 +28,11 @@ --disable_check SuspiciousImport --disable_check UnusedResources --disable_check ViewConstructor +# Disable NewApi checks for the platform since platform is the one that implements +# the API. This prevents noisy lint warnings like b/228956345#1 +# NewApi checks will continue to be enforced for apex deps since +# lint.strict_updatability_linting will be true for those Soong modules +--disable_check NewApi # Downgrade existing errors to warnings --warning_check AppCompatResource # 55 occurences in 10 modules @@ -66,7 +71,6 @@ --warning_check MissingTvBanner # 3 occurences in 3 modules --warning_check NamespaceTypo # 3 occurences in 3 modules --warning_check NetworkSecurityConfig # 46 occurences in 12 modules ---warning_check NewApi # 1996 occurences in 122 modules --warning_check NotSibling # 15 occurences in 10 modules --warning_check ObjectAnimatorBinding # 14 occurences in 5 modules --warning_check OnClick # 49 occurences in 21 modules diff --git a/java/sdk_library.go b/java/sdk_library.go index c37ed1a27..cd8e8755e 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -377,6 +377,9 @@ type sdkLibraryProperties struct { // List of Java libraries that will be in the classpath when building the implementation lib Impl_only_libs []string `android:"arch_variant"` + // List of Java libraries that will included in the implementation lib. + Impl_only_static_libs []string `android:"arch_variant"` + // List of Java libraries that will be in the classpath when building stubs Stub_only_libs []string `android:"arch_variant"` @@ -1346,10 +1349,12 @@ func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) visibility := childModuleVisibility(module.sdkLibraryProperties.Impl_library_visibility) props := struct { - Name *string - Visibility []string - Instrument bool - Libs []string + Name *string + Visibility []string + Instrument bool + Libs []string + Static_libs []string + Apex_available []string }{ Name: proptools.StringPtr(module.implLibraryModuleName()), Visibility: visibility, @@ -1358,6 +1363,12 @@ func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) // Set the impl_only libs. Note that the module's "Libs" get appended as well, via the // addition of &module.properties below. Libs: module.sdkLibraryProperties.Impl_only_libs, + // Set the impl_only static libs. Note that the module's "static_libs" get appended as well, via the + // addition of &module.properties below. + Static_libs: module.sdkLibraryProperties.Impl_only_static_libs, + // Pass the apex_available settings down so that the impl library can be statically + // embedded within a library that is added to an APEX. Needed for updatable-media. + Apex_available: module.ApexAvailable(), } properties := []interface{}{ @@ -1814,8 +1825,9 @@ func (module *SdkLibrary) CreateInternalModules(mctx android.DefaultableHookCont *javaSdkLibraries = append(*javaSdkLibraries, module.BaseModuleName()) } - // Add the impl_only_libs *after* we're done using the Libs prop in submodules. + // Add the impl_only_libs and impl_only_static_libs *after* we're done using them in submodules. module.properties.Libs = append(module.properties.Libs, module.sdkLibraryProperties.Impl_only_libs...) + module.properties.Static_libs = append(module.properties.Static_libs, module.sdkLibraryProperties.Impl_only_static_libs...) } func (module *SdkLibrary) InitSdkLibraryProperties() { @@ -2211,8 +2223,23 @@ func (module *SdkLibraryImport) UniqueApexVariations() bool { return module.uniqueApexVariations() } +// MinSdkVersion - Implements hiddenAPIModule +func (module *SdkLibraryImport) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec { + return android.SdkSpecNone +} + +var _ hiddenAPIModule = (*SdkLibraryImport)(nil) + func (module *SdkLibraryImport) OutputFiles(tag string) (android.Paths, error) { - return module.commonOutputFiles(tag) + paths, err := module.commonOutputFiles(tag) + if paths != nil || err != nil { + return paths, err + } + if module.implLibraryModule != nil { + return module.implLibraryModule.OutputFiles(tag) + } else { + return nil, nil + } } func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { |