diff options
Diffstat (limited to 'java')
75 files changed, 7294 insertions, 1861 deletions
diff --git a/java/Android.bp b/java/Android.bp index 59526024a..df0d1eb3d 100644 --- a/java/Android.bp +++ b/java/Android.bp @@ -10,10 +10,12 @@ bootstrap_go_package { "blueprint-pathtools", "soong", "soong-android", + "soong-bazel", "soong-cc", "soong-dexpreopt", "soong-genrule", "soong-java-config", + "soong-provenance", "soong-python", "soong-remoteexec", "soong-tradefed", @@ -39,9 +41,11 @@ bootstrap_go_package { "dex.go", "dexpreopt.go", "dexpreopt_bootjars.go", + "dexpreopt_check.go", "dexpreopt_config.go", "droiddoc.go", "droidstubs.go", + "fuzz.go", "gen.go", "genrule.go", "hiddenapi.go", @@ -78,6 +82,7 @@ bootstrap_go_package { "app_test.go", "bootclasspath_fragment_test.go", "device_host_converter_test.go", + "dex_test.go", "dexpreopt_test.go", "dexpreopt_bootjars_test.go", "droiddoc_test.go", @@ -91,8 +96,10 @@ bootstrap_go_package { "platform_bootclasspath_test.go", "platform_compat_config_test.go", "plugin_test.go", + "prebuilt_apis_test.go", "rro_test.go", "sdk_test.go", + "sdk_library_test.go", "system_modules_test.go", "systemserver_classpath_fragment_test.go", ], diff --git a/java/OWNERS b/java/OWNERS index 16ef4d812..5b71b1e35 100644 --- a/java/OWNERS +++ b/java/OWNERS @@ -1 +1,4 @@ -per-file dexpreopt*.go = ngeoffray@google.com,calin@google.com,mathieuc@google.com +per-file dexpreopt*.go = ngeoffray@google.com,calin@google.com,skvadrik@google.com + +# For metalava team to disable lint checks in platform +per-file droidstubs.go = aurimas@google.com,emberrose@google.com,sjgilbert@google.com
\ No newline at end of file diff --git a/java/aar.go b/java/aar.go index a5cf0fd70..00ff7e774 100644 --- a/java/aar.go +++ b/java/aar.go @@ -267,18 +267,29 @@ var extractAssetsRule = pctx.AndroidStaticRule("extractAssets", }) func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkContext, - classLoaderContexts dexpreopt.ClassLoaderContextMap, extraLinkFlags ...string) { + classLoaderContexts dexpreopt.ClassLoaderContextMap, excludedLibs []string, + extraLinkFlags ...string) { transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assetPackages, libDeps, libFlags := aaptLibs(ctx, sdkContext, classLoaderContexts) + // Exclude any libraries from the supplied list. + classLoaderContexts = classLoaderContexts.ExcludeLibs(excludedLibs) + // App manifest file manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml") manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile) - manifestPath := manifestFixer(ctx, manifestSrcPath, sdkContext, classLoaderContexts, - a.isLibrary, a.useEmbeddedNativeLibs, a.usesNonSdkApis, a.useEmbeddedDex, a.hasNoCode, - a.LoggingParent) + manifestPath := ManifestFixer(ctx, manifestSrcPath, ManifestFixerParams{ + SdkContext: sdkContext, + ClassLoaderContexts: classLoaderContexts, + IsLibrary: a.isLibrary, + UseEmbeddedNativeLibs: a.useEmbeddedNativeLibs, + UsesNonSdkApis: a.usesNonSdkApis, + UseEmbeddedDex: a.useEmbeddedDex, + HasNoCode: a.hasNoCode, + LoggingParent: a.LoggingParent, + }) // Add additional manifest files to transitive manifests. additionalManifests := android.PathsForModuleSrc(ctx, a.aaptProperties.Additional_manifests) @@ -514,12 +525,13 @@ func (a *AndroidLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { if sdkDep.hasFrameworkLibs() { a.aapt.deps(ctx, sdkDep) } + a.usesLibrary.deps(ctx, sdkDep.hasFrameworkLibs()) } func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.aapt.isLibrary = true - a.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap) - a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts) + a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx) + a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts, nil) a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform() @@ -586,16 +598,26 @@ func AndroidLibraryFactory() android.Module { // AAR (android library) prebuilts // +// Properties for android_library_import type AARImportProperties struct { + // ARR (android library prebuilt) filepath. Exactly one ARR is required. Aars []string `android:"path"` - - Sdk_version *string + // If not blank, set to the version of the sdk to compile against. + // Defaults to private. + // Values are of one of the following forms: + // 1) numerical API level, "current", "none", or "core_platform" + // 2) An SDK kind with an API level: "<sdk kind>_<API level>" + // See build/soong/android/sdk_version.go for the complete and up to date list of SDK kinds. + // If the SDK kind is empty, it will be set to public + Sdk_version *string + // If not blank, set the minimum version of the sdk that the compiled artifacts will run against. + // Defaults to sdk_version if not set. See sdk_version for possible values. Min_sdk_version *string - + // List of java static libraries that the included ARR (android library prebuilts) has dependencies to. Static_libs []string - Libs []string - - // if set to true, run Jetifier against .aar file. Defaults to false. + // List of java libraries that the included ARR (android library prebuilts) has dependencies to. + Libs []string + // If set to true, run Jetifier against .aar file. Defaults to false. Jetifier *bool } @@ -615,6 +637,7 @@ type AARImport struct { exportPackage android.WritablePath extraAaptPackagesFile android.WritablePath manifest android.WritablePath + assetsPackage android.WritablePath exportedStaticPackages android.Paths @@ -685,9 +708,8 @@ func (a *AARImport) ExportedManifests() android.Paths { return android.Paths{a.manifest} } -// TODO(jungjw): Decide whether we want to implement this. func (a *AARImport) ExportedAssets() android.OptionalPath { - return android.OptionalPath{} + return android.OptionalPathForPath(a.assetsPackage) } // RRO enforcement is not available on aar_import since its RRO dirs are not @@ -731,10 +753,11 @@ var unzipAAR = pctx.AndroidStaticRule("unzipAAR", blueprint.RuleParams{ Command: `rm -rf $outDir && mkdir -p $outDir && ` + `unzip -qoDD -d $outDir $in && rm -rf $outDir/res && touch $out && ` + + `${config.Zip2ZipCmd} -i $in -o $assetsPackage 'assets/**/*' && ` + `${config.MergeZipsCmd} $combinedClassesJar $$(ls $outDir/classes.jar 2> /dev/null) $$(ls $outDir/libs/*.jar 2> /dev/null)`, - CommandDeps: []string{"${config.MergeZipsCmd}"}, + CommandDeps: []string{"${config.MergeZipsCmd}", "${config.Zip2ZipCmd}"}, }, - "outDir", "combinedClassesJar") + "outDir", "combinedClassesJar", "assetsPackage") func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { if len(a.properties.Aars) != 1 { @@ -760,15 +783,17 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.classpathFile = extractedAARDir.Join(ctx, "classes-combined.jar") a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt") a.manifest = extractedAARDir.Join(ctx, "AndroidManifest.xml") + a.assetsPackage = android.PathForModuleOut(ctx, "assets.zip") ctx.Build(pctx, android.BuildParams{ Rule: unzipAAR, Input: a.aarPath, - Outputs: android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest}, + Outputs: android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest, a.assetsPackage}, Description: "unzip AAR", Args: map[string]string{ "outDir": extractedAARDir.String(), "combinedClassesJar": a.classpathFile.String(), + "assetsPackage": a.assetsPackage.String(), }, }) @@ -811,6 +836,19 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { aapt2Link(ctx, a.exportPackage, srcJar, proguardOptionsFile, rTxt, a.extraAaptPackagesFile, linkFlags, linkDeps, nil, overlayRes, transitiveAssets, nil) + // Merge this import's assets with its dependencies' assets (if there are any). + if len(transitiveAssets) > 0 { + mergedAssets := android.PathForModuleOut(ctx, "merged-assets.zip") + inputZips := append(android.Paths{a.assetsPackage}, transitiveAssets...) + ctx.Build(pctx, android.BuildParams{ + Rule: mergeAssetsRule, + Inputs: inputZips, + Output: mergedAssets, + Description: "merge assets from dependencies and self", + }) + a.assetsPackage = mergedAssets + } + ctx.SetProvider(JavaInfoProvider, JavaInfo{ HeaderJars: android.PathsIfNonNil(a.classpathFile), ImplementationAndResourcesJars: android.PathsIfNonNil(a.classpathFile), diff --git a/java/android_manifest.go b/java/android_manifest.go index 331f941ec..7772b7090 100644 --- a/java/android_manifest.go +++ b/java/android_manifest.go @@ -16,6 +16,7 @@ package java import ( "fmt" + "strconv" "strings" "github.com/google/blueprint" @@ -27,13 +28,10 @@ import ( var manifestFixerRule = pctx.AndroidStaticRule("manifestFixer", blueprint.RuleParams{ Command: `${config.ManifestFixerCmd} ` + - `--minSdkVersion ${minSdkVersion} ` + - `--targetSdkVersion ${targetSdkVersion} ` + - `--raise-min-sdk-version ` + `$args $in $out`, CommandDeps: []string{"${config.ManifestFixerCmd}"}, }, - "minSdkVersion", "targetSdkVersion", "args") + "args") var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger", blueprint.RuleParams{ @@ -42,84 +40,124 @@ var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger", }, "args", "libs") -// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml -func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext android.SdkContext, - classLoaderContexts dexpreopt.ClassLoaderContextMap, isLibrary, useEmbeddedNativeLibs, usesNonSdkApis, - useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path { +// targetSdkVersion for manifest_fixer +// When TARGET_BUILD_APPS is not empty, this method returns 10000 for modules targeting an unreleased SDK +// This enables release builds (that run with TARGET_BUILD_APPS=[val...]) to target APIs that have not yet been finalized as part of an SDK +func targetSdkVersionForManifestFixer(ctx android.ModuleContext, sdkContext android.SdkContext) string { + targetSdkVersionSpec := sdkContext.TargetSdkVersion(ctx) + if ctx.Config().UnbundledBuildApps() && targetSdkVersionSpec.ApiLevel.IsPreview() { + return strconv.Itoa(android.FutureApiLevel.FinalOrFutureInt()) + } + targetSdkVersion, err := targetSdkVersionSpec.EffectiveVersionString(ctx) + if err != nil { + ctx.ModuleErrorf("invalid targetSdkVersion: %s", err) + } + return targetSdkVersion +} + +type ManifestFixerParams struct { + SdkContext android.SdkContext + ClassLoaderContexts dexpreopt.ClassLoaderContextMap + IsLibrary bool + UseEmbeddedNativeLibs bool + UsesNonSdkApis bool + UseEmbeddedDex bool + HasNoCode bool + TestOnly bool + LoggingParent string +} +// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml +func ManifestFixer(ctx android.ModuleContext, manifest android.Path, + params ManifestFixerParams) android.Path { var args []string - if isLibrary { + + if params.IsLibrary { args = append(args, "--library") - } else { - minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersion(ctx) + } else if params.SdkContext != nil { + minSdkVersion, err := params.SdkContext.MinSdkVersion(ctx).EffectiveVersion(ctx) if err != nil { ctx.ModuleErrorf("invalid minSdkVersion: %s", err) } if minSdkVersion.FinalOrFutureInt() >= 23 { - args = append(args, fmt.Sprintf("--extract-native-libs=%v", !useEmbeddedNativeLibs)) - } else if useEmbeddedNativeLibs { + 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) } } - if usesNonSdkApis { + if params.UsesNonSdkApis { args = append(args, "--uses-non-sdk-api") } - if useEmbeddedDex { + if params.UseEmbeddedDex { args = append(args, "--use-embedded-dex") } - for _, usesLib := range classLoaderContexts.UsesLibs() { - if inList(usesLib, dexpreopt.OptionalCompatUsesLibs) { - args = append(args, "--optional-uses-library", usesLib) - } else { + 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() + + for _, usesLib := range requiredUsesLibs { args = append(args, "--uses-library", usesLib) } + for _, usesLib := range optionalUsesLibs { + args = append(args, "--optional-uses-library", usesLib) + } } - if hasNoCode { + if params.HasNoCode { args = append(args, "--has-no-code") } - if loggingParent != "" { - args = append(args, "--logging-parent", loggingParent) - } - var deps android.Paths - targetSdkVersion, err := sdkContext.TargetSdkVersion(ctx).EffectiveVersionString(ctx) - if err != nil { - ctx.ModuleErrorf("invalid targetSdkVersion: %s", err) - } - if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" { - targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String()) - deps = append(deps, ApiFingerprintPath(ctx)) + if params.TestOnly { + args = append(args, "--test-only") } - minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx) - if err != nil { - ctx.ModuleErrorf("invalid minSdkVersion: %s", err) + if params.LoggingParent != "" { + args = append(args, "--logging-parent", params.LoggingParent) } - if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" { - minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String()) - deps = append(deps, ApiFingerprintPath(ctx)) + var deps android.Paths + var argsMapper = make(map[string]string) + + if params.SdkContext != nil { + targetSdkVersion := targetSdkVersionForManifestFixer(ctx, params.SdkContext) + args = append(args, "--targetSdkVersion ", targetSdkVersion) + + if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" { + targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String()) + deps = append(deps, ApiFingerprintPath(ctx)) + } + + minSdkVersion, err := params.SdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx) + if err != nil { + ctx.ModuleErrorf("invalid minSdkVersion: %s", err) + } + + if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" { + minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String()) + deps = append(deps, ApiFingerprintPath(ctx)) + } + + if err != nil { + ctx.ModuleErrorf("invalid minSdkVersion: %s", err) + } + args = append(args, "--minSdkVersion ", minSdkVersion) + args = append(args, "--raise-min-sdk-version") } fixedManifest := android.PathForModuleOut(ctx, "manifest_fixer", "AndroidManifest.xml") - if err != nil { - ctx.ModuleErrorf("invalid minSdkVersion: %s", err) - } + argsMapper["args"] = strings.Join(args, " ") + ctx.Build(pctx, android.BuildParams{ Rule: manifestFixerRule, Description: "fix manifest", Input: manifest, Implicits: deps, Output: fixedManifest, - Args: map[string]string{ - "minSdkVersion": minSdkVersion, - "targetSdkVersion": targetSdkVersion, - "args": strings.Join(args, " "), - }, + Args: argsMapper, }) return fixedManifest.WithoutRel() diff --git a/java/android_resources.go b/java/android_resources.go index 6864ebb90..8c5908f69 100644 --- a/java/android_resources.go +++ b/java/android_resources.go @@ -43,7 +43,7 @@ var androidResourceIgnoreFilenames = []string{ // androidResourceGlob returns the list of files in the given directory, using the standard // exclusion patterns for Android resources. -func androidResourceGlob(ctx android.ModuleContext, dir android.Path) android.Paths { +func androidResourceGlob(ctx android.EarlyModuleContext, dir android.Path) android.Paths { return ctx.GlobFiles(filepath.Join(dir.String(), "**/*"), androidResourceIgnoreFilenames) } diff --git a/java/androidmk.go b/java/androidmk.go index 04357e066..7322637a7 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -29,8 +29,8 @@ func (library *Library) AndroidMkEntriesHostDex() android.AndroidMkEntries { if hostDexNeeded { var output android.Path - if library.dexJarFile != nil { - output = library.dexJarFile + if library.dexJarFile.IsSet() { + output = library.dexJarFile.Path() } else { output = library.implementationAndResourcesJar } @@ -44,9 +44,10 @@ func (library *Library) AndroidMkEntriesHostDex() android.AndroidMkEntries { func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetBool("LOCAL_IS_HOST_MODULE", true) entries.SetPath("LOCAL_PREBUILT_MODULE_FILE", output) - if library.dexJarFile != nil { - entries.SetPath("LOCAL_SOONG_DEX_JAR", library.dexJarFile) + if library.dexJarFile.IsSet() { + entries.SetPath("LOCAL_SOONG_DEX_JAR", library.dexJarFile.Path()) } + entries.SetPath("LOCAL_SOONG_INSTALLED_MODULE", library.hostdexInstallFile) entries.SetPath("LOCAL_SOONG_HEADER_JAR", library.headerJarFile) entries.SetPath("LOCAL_SOONG_CLASSES_JAR", library.implementationAndResourcesJar) entries.SetString("LOCAL_MODULE_STEM", library.Stem()+"-hostdex") @@ -60,28 +61,23 @@ func (library *Library) AndroidMkEntriesHostDex() android.AndroidMkEntries { func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { var entriesList []android.AndroidMkEntries + if library.Os() == android.Windows { + // Make does not support Windows Java modules + return nil + } + if library.hideApexVariantFromMake { - // For a java library built for an APEX we don't need Make module + // For a java library built for an APEX, we don't need a Make module for itself. Otherwise, it + // will conflict with the platform variant because they have the same module name in the + // makefile. However, we need to add its dexpreopt outputs as sub-modules, if it is preopted. + dexpreoptEntries := library.dexpreopter.AndroidMkEntriesForApex() + if len(dexpreoptEntries) > 0 { + entriesList = append(entriesList, dexpreoptEntries...) + } entriesList = append(entriesList, android.AndroidMkEntries{Disabled: true}) } else if !library.ApexModuleBase.AvailableFor(android.AvailableToPlatform) { // Platform variant. If not available for the platform, we don't need Make module. - // May still need to add some additional dependencies. - checkedModulePaths := library.additionalCheckedModules - if len(checkedModulePaths) != 0 { - entriesList = append(entriesList, android.AndroidMkEntries{ - Class: "FAKE", - // Need at least one output file in order for this to take effect. - OutputFile: android.OptionalPathForPath(checkedModulePaths[0]), - Include: "$(BUILD_PHONY_PACKAGE)", - ExtraEntries: []android.AndroidMkExtraEntriesFunc{ - func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", checkedModulePaths.Strings()...) - }, - }, - }) - } else { - entriesList = append(entriesList, android.AndroidMkEntries{Disabled: true}) - } + entriesList = append(entriesList, android.AndroidMkEntries{Disabled: true}) } else { entriesList = append(entriesList, android.AndroidMkEntries{ Class: "JAVA_LIBRARIES", @@ -100,8 +96,8 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { if library.installFile == nil { entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", true) } - if library.dexJarFile != nil { - entries.SetPath("LOCAL_SOONG_DEX_JAR", library.dexJarFile) + if library.dexJarFile.IsSet() { + entries.SetPath("LOCAL_SOONG_DEX_JAR", library.dexJarFile.Path()) } if len(library.dexpreopter.builtInstalled) > 0 { entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", library.dexpreopter.builtInstalled) @@ -114,11 +110,8 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { entries.SetPath("LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR", library.jacocoReportClassesFile) } - entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", library.classLoaderContexts.UsesLibs()...) - - if len(library.additionalCheckedModules) != 0 { - entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", library.additionalCheckedModules.Strings()...) - } + requiredUsesLibs, optionalUsesLibs := library.classLoaderContexts.UsesLibs() + entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", append(requiredUsesLibs, optionalUsesLibs...)...) entries.SetOptionalPath("LOCAL_SOONG_PROGUARD_DICT", library.dexer.proguardDictionary) entries.SetOptionalPath("LOCAL_SOONG_PROGUARD_USAGE_ZIP", library.dexer.proguardUsageZip) @@ -140,20 +133,21 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { } // Called for modules that are a component of a test suite. -func testSuiteComponent(entries *android.AndroidMkEntries, test_suites []string) { +func testSuiteComponent(entries *android.AndroidMkEntries, test_suites []string, perTestcaseDirectory bool) { entries.SetString("LOCAL_MODULE_TAGS", "tests") if len(test_suites) > 0 { entries.AddCompatibilityTestSuites(test_suites...) } else { entries.AddCompatibilityTestSuites("null-suite") } + entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", perTestcaseDirectory) } func (j *Test) AndroidMkEntries() []android.AndroidMkEntries { entriesList := j.Library.AndroidMkEntries() entries := &entriesList[0] entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - testSuiteComponent(entries, j.testProperties.Test_suites) + testSuiteComponent(entries, j.testProperties.Test_suites, Bool(j.testProperties.Per_testcase_directory)) if j.testConfig != nil { entries.SetPath("LOCAL_FULL_TEST_CONFIG", j.testConfig) } @@ -181,14 +175,21 @@ func (j *TestHelperLibrary) AndroidMkEntries() []android.AndroidMkEntries { entriesList := j.Library.AndroidMkEntries() entries := &entriesList[0] entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - testSuiteComponent(entries, j.testHelperLibraryProperties.Test_suites) + testSuiteComponent(entries, j.testHelperLibraryProperties.Test_suites, Bool(j.testHelperLibraryProperties.Per_testcase_directory)) }) return entriesList } func (prebuilt *Import) AndroidMkEntries() []android.AndroidMkEntries { - if prebuilt.hideApexVariantFromMake || !prebuilt.ContainingSdk().Unversioned() { + if prebuilt.hideApexVariantFromMake { + // For a library imported from a prebuilt APEX, we don't need a Make module for itself, as we + // don't need to install it. However, we need to add its dexpreopt outputs as sub-modules, if it + // is preopted. + dexpreoptEntries := prebuilt.dexpreopter.AndroidMkEntriesForApex() + return append(dexpreoptEntries, android.AndroidMkEntries{Disabled: true}) + } + if !prebuilt.ContainingSdk().Unversioned() { return []android.AndroidMkEntries{android.AndroidMkEntries{ Disabled: true, }} @@ -200,8 +201,8 @@ func (prebuilt *Import) AndroidMkEntries() []android.AndroidMkEntries { ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !Bool(prebuilt.properties.Installable)) - if prebuilt.dexJarFile != nil { - entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile) + if prebuilt.dexJarFile.IsSet() { + entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile.Path()) } entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.combinedClasspathFile) entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.combinedClasspathFile) @@ -220,12 +221,12 @@ func (prebuilt *DexImport) AndroidMkEntries() []android.AndroidMkEntries { } return []android.AndroidMkEntries{android.AndroidMkEntries{ Class: "JAVA_LIBRARIES", - OutputFile: android.OptionalPathForPath(prebuilt.dexJarFile), + OutputFile: android.OptionalPathForPath(prebuilt.dexJarFile.Path()), Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk", ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - if prebuilt.dexJarFile != nil { - entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile) + if prebuilt.dexJarFile.IsSet() { + entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile.Path()) } if len(prebuilt.dexpreopter.builtInstalled) > 0 { entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", prebuilt.dexpreopter.builtInstalled) @@ -262,6 +263,10 @@ func (prebuilt *AARImport) AndroidMkEntries() []android.AndroidMkEntries { } func (binary *Binary) AndroidMkEntries() []android.AndroidMkEntries { + if binary.Os() == android.Windows { + // Make does not support Windows Java modules + return nil + } if !binary.isWrapperVariant { return []android.AndroidMkEntries{android.AndroidMkEntries{ @@ -272,8 +277,8 @@ func (binary *Binary) AndroidMkEntries() []android.AndroidMkEntries { func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetPath("LOCAL_SOONG_HEADER_JAR", binary.headerJarFile) entries.SetPath("LOCAL_SOONG_CLASSES_JAR", binary.implementationAndResourcesJar) - if binary.dexJarFile != nil { - entries.SetPath("LOCAL_SOONG_DEX_JAR", binary.dexJarFile) + if binary.dexJarFile.IsSet() { + entries.SetPath("LOCAL_SOONG_DEX_JAR", binary.dexJarFile.Path()) } if len(binary.dexpreopter.builtInstalled) > 0 { entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", binary.dexpreopter.builtInstalled) @@ -288,11 +293,6 @@ func (binary *Binary) AndroidMkEntries() []android.AndroidMkEntries { }} } else { outputFile := binary.wrapperFile - // Have Make installation trigger Soong installation by using Soong's install path as - // the output file. - if binary.Host() { - outputFile = binary.binaryFile - } return []android.AndroidMkEntries{android.AndroidMkEntries{ Class: "EXECUTABLES", @@ -314,7 +314,7 @@ func (binary *Binary) AndroidMkEntries() []android.AndroidMkEntries { } func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries { - if app.hideApexVariantFromMake || app.appProperties.HideFromMake { + if app.hideApexVariantFromMake || app.IsHideFromMake() { return []android.AndroidMkEntries{android.AndroidMkEntries{ Disabled: true, }} @@ -329,8 +329,8 @@ func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries { entries.SetString("LOCAL_MODULE", app.installApkName) entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", app.appProperties.PreventInstall) entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", app.exportPackage) - if app.dexJarFile != nil { - entries.SetPath("LOCAL_SOONG_DEX_JAR", app.dexJarFile) + if app.dexJarFile.IsSet() { + entries.SetPath("LOCAL_SOONG_DEX_JAR", app.dexJarFile.Path()) } if app.implementationAndResourcesJar != nil { entries.SetPath("LOCAL_SOONG_CLASSES_JAR", app.implementationAndResourcesJar) @@ -411,30 +411,24 @@ func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries { }, ExtraFooters: []android.AndroidMkExtraFootersFunc{ func(w io.Writer, name, prefix, moduleDir string) { - if app.noticeOutputs.Merged.Valid() { - fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n", - app.installApkName, app.noticeOutputs.Merged.String(), app.installApkName+"_NOTICE") - } - if app.noticeOutputs.TxtOutput.Valid() { - fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n", - app.installApkName, app.noticeOutputs.TxtOutput.String(), app.installApkName+"_NOTICE.txt") - } - if app.noticeOutputs.HtmlOutput.Valid() { - fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n", - app.installApkName, app.noticeOutputs.HtmlOutput.String(), app.installApkName+"_NOTICE.html") + if app.javaApiUsedByOutputFile.String() != "" { + fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s/$(notdir %s))\n", + app.installApkName, app.javaApiUsedByOutputFile.String(), "java_apis_used_by_apex", app.javaApiUsedByOutputFile.String()) } }, - }, - }} + }}, + } } func (a *AndroidApp) getOverriddenPackages() []string { var overridden []string - if len(a.appProperties.Overrides) > 0 { - overridden = append(overridden, a.appProperties.Overrides...) + if len(a.overridableAppProperties.Overrides) > 0 { + overridden = append(overridden, a.overridableAppProperties.Overrides...) } - if a.Name() != a.installApkName { - overridden = append(overridden, a.Name()) + // When APK name is overridden via PRODUCT_PACKAGE_NAME_OVERRIDES + // ensure that the original name is overridden. + if a.Stem() != a.installApkName { + overridden = append(overridden, a.Stem()) } return overridden } @@ -443,7 +437,7 @@ func (a *AndroidTest) AndroidMkEntries() []android.AndroidMkEntries { entriesList := a.AndroidApp.AndroidMkEntries() entries := &entriesList[0] entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - testSuiteComponent(entries, a.testProperties.Test_suites) + testSuiteComponent(entries, a.testProperties.Test_suites, Bool(a.testProperties.Per_testcase_directory)) if a.testConfig != nil { entries.SetPath("LOCAL_FULL_TEST_CONFIG", a.testConfig) } @@ -459,7 +453,7 @@ func (a *AndroidTestHelperApp) AndroidMkEntries() []android.AndroidMkEntries { entriesList := a.AndroidApp.AndroidMkEntries() entries := &entriesList[0] entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - testSuiteComponent(entries, a.appTestHelperAppProperties.Test_suites) + testSuiteComponent(entries, a.appTestHelperAppProperties.Test_suites, Bool(a.appTestHelperAppProperties.Per_testcase_directory)) // introduce a flag variable to control the generation of the .config file entries.SetString("LOCAL_DISABLE_TEST_CONFIG", "true") }) @@ -670,7 +664,7 @@ func (a *AndroidTestImport) AndroidMkEntries() []android.AndroidMkEntries { entriesList := a.AndroidAppImport.AndroidMkEntries() entries := &entriesList[0] entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - testSuiteComponent(entries, a.testProperties.Test_suites) + testSuiteComponent(entries, a.testProperties.Test_suites, Bool(a.testProperties.Per_testcase_directory)) androidMkWriteTestData(a.data, entries) }) return entriesList @@ -692,7 +686,7 @@ func (r *RuntimeResourceOverlay) AndroidMkEntries() []android.AndroidMkEntries { ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetString("LOCAL_CERTIFICATE", r.certificate.AndroidMkString()) - entries.SetPath("LOCAL_MODULE_PATH", r.installDir.ToMakePath()) + entries.SetPath("LOCAL_MODULE_PATH", r.installDir) entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", r.properties.Overrides...) }, }, @@ -703,12 +697,12 @@ func (apkSet *AndroidAppSet) AndroidMkEntries() []android.AndroidMkEntries { return []android.AndroidMkEntries{ android.AndroidMkEntries{ Class: "APPS", - OutputFile: android.OptionalPathForPath(apkSet.packedOutput), + OutputFile: android.OptionalPathForPath(apkSet.primaryOutput), Include: "$(BUILD_SYSTEM)/soong_android_app_set.mk", ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", apkSet.Privileged()) - entries.SetString("LOCAL_APK_SET_INSTALL_FILE", apkSet.InstallFile()) + entries.SetPath("LOCAL_APK_SET_INSTALL_FILE", apkSet.PackedAdditionalOutputs()) entries.SetPath("LOCAL_APKCERTS_FILE", apkSet.apkcertsFile) entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", apkSet.properties.Overrides...) }, diff --git a/java/androidmk_test.go b/java/androidmk_test.go index 246c0eb07..197da4f38 100644 --- a/java/androidmk_test.go +++ b/java/androidmk_test.go @@ -206,3 +206,49 @@ func TestAndroidTestHelperApp_LocalDisableTestConfig(t *testing.T) { t.Errorf("Unexpected flag value - expected: %q, actual: %q", expected, actual) } } + +func TestGetOverriddenPackages(t *testing.T) { + ctx, _ := testJava( + t, ` + android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "current", + overrides: ["qux"] + } + + override_android_app { + name: "foo_override", + base: "foo", + overrides: ["bar"] + } + `) + + expectedVariants := []struct { + name string + moduleName string + variantName string + overrides []string + }{ + { + name: "foo", + moduleName: "foo", + variantName: "android_common", + overrides: []string{"qux"}, + }, + { + name: "foo", + moduleName: "foo_override", + variantName: "android_common_foo_override", + overrides: []string{"bar", "foo"}, + }, + } + + for _, expected := range expectedVariants { + mod := ctx.ModuleForTests(expected.name, expected.variantName).Module() + entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0] + actual := entries.EntryMap["LOCAL_OVERRIDES_PACKAGES"] + + android.AssertDeepEquals(t, "overrides property", expected.overrides, actual) + } +} diff --git a/java/app.go b/java/app.go index fc1ace07b..94e6fb950 100755 --- a/java/app.go +++ b/java/app.go @@ -19,13 +19,13 @@ package java import ( "path/filepath" - "sort" "strings" "github.com/google/blueprint" "github.com/google/blueprint/proptools" "android/soong/android" + "android/soong/bazel" "android/soong/cc" "android/soong/dexpreopt" "android/soong/tradefed" @@ -63,13 +63,6 @@ type appProperties struct { // list of resource labels to generate individual resource packages Package_splits []string - // Names of modules to be overridden. Listed modules can only be other binaries - // (in Make or Soong). - // This does not completely prevent installation of the overridden binaries, but if both - // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed - // from PRODUCT_PACKAGES. - Overrides []string - // list of native libraries that will be provided in or alongside the resulting jar Jni_libs []string `android:"arch_variant"` @@ -106,7 +99,6 @@ type appProperties struct { // cc.Coverage related properties PreventInstall bool `blueprint:"mutated"` - HideFromMake bool `blueprint:"mutated"` IsCoverageVariant bool `blueprint:"mutated"` // Whether this app is considered mainline updatable or not. When set to true, this will enforce @@ -125,6 +117,9 @@ type overridableAppProperties struct { // Name of the signing certificate lineage file or filegroup module. Lineage *string `android:"path"` + // For overriding the --rotation-min-sdk-version property of apksig + RotationMinSdkVersion *string + // the package name of this app. The package name in the manifest file is used if one was not given. Package_name *string @@ -133,9 +128,17 @@ type overridableAppProperties struct { // Whether to rename the package in resources to the override name rather than the base name. Defaults to true. Rename_resources_package *bool + + // Names of modules to be overridden. Listed modules can only be other binaries + // (in Make or Soong). + // This does not completely prevent installation of the overridden binaries, but if both + // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed + // from PRODUCT_PACKAGES. + Overrides []string } type AndroidApp struct { + android.BazelModuleBase Library aapt android.OverridableModuleBase @@ -162,11 +165,11 @@ type AndroidApp struct { additionalAaptFlags []string - noticeOutputs android.NoticeOutputs - overriddenManifestPackageName string android.ApexBundleDepsInfo + + javaApiUsedByOutputFile android.ModuleOutPath } func (a *AndroidApp) IsInstallable() bool { @@ -275,6 +278,7 @@ func (a *AndroidTestHelperApp) GenerateAndroidBuildActions(ctx android.ModuleCon func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.checkAppSdkVersions(ctx) a.generateAndroidBuildActions(ctx) + a.generateJavaUsedByApex(ctx) } func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) { @@ -288,7 +292,7 @@ func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) { if minSdkVersion, err := a.MinSdkVersion(ctx).EffectiveVersion(ctx); err == nil { a.checkJniLibsSdkVersion(ctx, minSdkVersion) - android.CheckMinSdkVersion(a, ctx, minSdkVersion) + android.CheckMinSdkVersion(ctx, minSdkVersion, a.WalkPayloadDeps) } else { ctx.PropertyErrorf("min_sdk_version", "%s", err.Error()) } @@ -300,10 +304,6 @@ func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) { // If an updatable APK sets min_sdk_version, min_sdk_vesion of JNI libs should match with it. // This check is enforced for "updatable" APKs (including APK-in-APEX). -// b/155209650: until min_sdk_version is properly supported, use sdk_version instead. -// because, sdk_version is overridden by min_sdk_version (if set as smaller) -// and sdkLinkType is checked with dependencies so we can be sure that the whole dependency tree -// will meet the requirements. func (a *AndroidApp) checkJniLibsSdkVersion(ctx android.ModuleContext, minSdkVersion android.ApiLevel) { // It's enough to check direct JNI deps' sdk_version because all transitive deps from JNI deps are checked in cc.checkLinkType() ctx.VisitDirectDeps(func(m android.Module) { @@ -314,10 +314,10 @@ func (a *AndroidApp) checkJniLibsSdkVersion(ctx android.ModuleContext, minSdkVer // The domain of cc.sdk_version is "current" and <number> // We can rely on android.SdkSpec to convert it to <number> so that "current" is // handled properly regardless of sdk finalization. - jniSdkVersion, err := android.SdkSpecFrom(ctx, dep.SdkVersion()).EffectiveVersion(ctx) + jniSdkVersion, err := android.SdkSpecFrom(ctx, dep.MinSdkVersion()).EffectiveVersion(ctx) if err != nil || minSdkVersion.LessThan(jniSdkVersion) { - ctx.OtherModuleErrorf(dep, "sdk_version(%v) is higher than min_sdk_version(%v) of the containing android_app(%v)", - dep.SdkVersion(), minSdkVersion, ctx.ModuleName()) + ctx.OtherModuleErrorf(dep, "min_sdk_version(%v) is higher than min_sdk_version(%v) of the containing android_app(%v)", + dep.MinSdkVersion(), minSdkVersion, ctx.ModuleName()) return } @@ -423,7 +423,8 @@ func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) { a.aapt.splitNames = a.appProperties.Package_splits a.aapt.LoggingParent = String(a.overridableAppProperties.Logging_parent) - a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts, aaptLinkFlags...) + a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts, + a.usesLibraryProperties.Exclude_uses_libs, aaptLinkFlags...) // apps manifests are handled by aapt, don't let Module see them a.properties.Manifest = nil @@ -468,12 +469,13 @@ func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path { a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries() a.dexpreopter.classLoaderContexts = a.classLoaderContexts a.dexpreopter.manifestFile = a.mergedManifestFile + a.dexpreopter.preventInstall = a.appProperties.PreventInstall if ctx.ModuleName() != "framework-res" { a.Module.compile(ctx, a.aaptSrcJar) } - return a.dexJarFile + return a.dexJarFile.PathOrNil() } func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext) android.WritablePath { @@ -482,7 +484,7 @@ func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext a.jniLibs = jniLibs if a.shouldEmbedJnis(ctx) { jniJarFile = android.PathForModuleOut(ctx, "jnilibs.zip") - a.installPathForJNISymbols = a.installPath(ctx).ToMakePath() + a.installPathForJNISymbols = a.installPath(ctx) TransformJniLibsToJar(ctx, jniJarFile, jniLibs, a.useEmbeddedNativeLibs(ctx)) for _, jni := range jniLibs { if jni.coverageFile.Valid() { @@ -519,53 +521,6 @@ func (a *AndroidApp) JNISymbolsInstalls(installPath string) android.RuleBuilderI return jniSymbols } -func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext) { - // Collect NOTICE files from all dependencies. - seenModules := make(map[android.Module]bool) - noticePathSet := make(map[android.Path]bool) - - ctx.WalkDeps(func(child android.Module, parent android.Module) bool { - // Have we already seen this? - if _, ok := seenModules[child]; ok { - return false - } - seenModules[child] = true - - // Skip host modules. - if child.Target().Os.Class == android.Host { - return false - } - - paths := child.(android.Module).NoticeFiles() - if len(paths) > 0 { - for _, path := range paths { - noticePathSet[path] = true - } - } - return true - }) - - // If the app has one, add it too. - if len(a.NoticeFiles()) > 0 { - for _, path := range a.NoticeFiles() { - noticePathSet[path] = true - } - } - - if len(noticePathSet) == 0 { - return - } - var noticePaths []android.Path - for path := range noticePathSet { - noticePaths = append(noticePaths, path) - } - sort.Slice(noticePaths, func(i, j int) bool { - return noticePaths[i].String() < noticePaths[j].String() - }) - - a.noticeOutputs = android.BuildNoticeOutput(ctx, a.installDir, a.installApkName+".apk", noticePaths) -} - // Reads and prepends a main cert from the default cert dir if it hasn't been set already, i.e. it // isn't a cert module reference. Also checks and enforces system cert restriction if applicable. func processMainCert(m android.ModuleBase, certPropValue string, certificates []Certificate, ctx android.ModuleContext) []Certificate { @@ -618,7 +573,7 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { a.aapt.useEmbeddedDex = Bool(a.appProperties.Use_embedded_dex) // Check if the install APK name needs to be overridden. - a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Name()) + a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Stem()) if ctx.ModuleName() == "framework-res" { // framework-res.apk is installed as system/framework/framework-res.apk @@ -632,11 +587,6 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { } a.onDeviceDir = android.InstallPathToOnDevicePath(ctx, a.installDir) - a.noticeBuildActions(ctx) - if Bool(a.appProperties.Embed_notices) || ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") { - a.aapt.noticeFile = a.noticeOutputs.HtmlGzOutput - } - a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx) // Process all building blocks, from AAPT to certificates. @@ -646,8 +596,12 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { a.usesLibrary.freezeEnforceUsesLibraries() // Add implicit SDK libraries to <uses-library> list. - for _, usesLib := range a.classLoaderContexts.UsesLibs() { - a.usesLibrary.addLib(usesLib, inList(usesLib, dexpreopt.OptionalCompatUsesLibs)) + requiredUsesLibs, optionalUsesLibs := a.classLoaderContexts.UsesLibs() + for _, usesLib := range requiredUsesLibs { + a.usesLibrary.addLib(usesLib, false) + } + for _, usesLib := range optionalUsesLibs { + a.usesLibrary.addLib(usesLib, true) } // Check that the <uses-library> list is coherent with the manifest. @@ -673,7 +627,21 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { } certificates := processMainCert(a.ModuleBase, a.getCertString(ctx), certificateDeps, ctx) - a.certificate = certificates[0] + + // This can be reached with an empty certificate list if AllowMissingDependencies is set + // and the certificate property for this module is a module reference to a missing module. + if len(certificates) > 0 { + a.certificate = certificates[0] + } else { + if !ctx.Config().AllowMissingDependencies() && len(ctx.GetMissingDependencies()) > 0 { + panic("Should only get here if AllowMissingDependencies set and there are missing dependencies") + } + // Set a certificate to avoid panics later when accessing it. + a.certificate = Certificate{ + Key: android.PathForModuleOut(ctx, "missing.pk8"), + Pem: android.PathForModuleOut(ctx, "missing.pem"), + } + } // Build a final signed app package. packageFile := android.PathForModuleOut(ctx, a.installApkName+".apk") @@ -686,19 +654,40 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { if lineage := String(a.overridableAppProperties.Lineage); lineage != "" { lineageFile = android.PathForModuleSrc(ctx, lineage) } - CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates, apkDeps, v4SignatureFile, lineageFile) + + rotationMinSdkVersion := String(a.overridableAppProperties.RotationMinSdkVersion) + + CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion) a.outputFile = packageFile if v4SigningRequested { a.extraOutputFiles = append(a.extraOutputFiles, v4SignatureFile) } + if Bool(a.appProperties.Embed_notices) || ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") { + noticeFile := android.PathForModuleOut(ctx, "NOTICE.html.gz") + android.BuildNoticeHtmlOutputFromLicenseMetadata( + ctx, noticeFile, "", "", + []string{ + a.installDir.String() + "/", + android.PathForModuleInstall(ctx).String() + "/", + a.outputFile.String(), + }) + noticeAssetPath := android.PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz") + builder := android.NewRuleBuilder(pctx, ctx) + builder.Command().Text("cp"). + Input(noticeFile). + Output(noticeAssetPath) + builder.Build("notice_dir", "Building notice dir") + a.aapt.noticeFile = android.OptionalPathForPath(noticeAssetPath) + } + for _, split := range a.aapt.splits { // Sign the split APKs packageFile := android.PathForModuleOut(ctx, a.installApkName+"_"+split.suffix+".apk") if v4SigningRequested { v4SignatureFile = android.PathForModuleOut(ctx, a.installApkName+"_"+split.suffix+".apk.idsig") } - CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates, apkDeps, v4SignatureFile, lineageFile) + CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion) a.extraOutputFiles = append(a.extraOutputFiles, packageFile) if v4SigningRequested { a.extraOutputFiles = append(a.extraOutputFiles, v4SignatureFile) @@ -713,11 +702,15 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) // Install the app package. - if (Bool(a.Module.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() { - ctx.InstallFile(a.installDir, a.outputFile.Base(), a.outputFile) + if (Bool(a.Module.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() && + !a.appProperties.PreventInstall { + + var extraInstalledPaths android.Paths for _, extra := range a.extraOutputFiles { - ctx.InstallFile(a.installDir, extra.Base(), extra) + installed := ctx.InstallFile(a.installDir, extra.Base(), extra) + extraInstalledPaths = append(extraInstalledPaths, installed) } + ctx.InstallFile(a.installDir, a.outputFile.Base(), a.outputFile, extraInstalledPaths...) } a.buildAppDependencyInfo(ctx) @@ -753,18 +746,18 @@ func collectAppDeps(ctx android.ModuleContext, app appDepsInterface, } lib := dep.OutputFile() - path := lib.Path() - if seenModulePaths[path.String()] { - return false - } - seenModulePaths[path.String()] = true + if lib.Valid() { + path := lib.Path() + if seenModulePaths[path.String()] { + return false + } + seenModulePaths[path.String()] = true - if checkNativeSdkVersion && dep.SdkVersion() == "" { - ctx.PropertyErrorf("jni_libs", "JNI dependency %q uses platform APIs, but this module does not", - otherName) - } + if checkNativeSdkVersion && dep.SdkVersion() == "" { + ctx.PropertyErrorf("jni_libs", "JNI dependency %q uses platform APIs, but this module does not", + otherName) + } - if lib.Valid() { jniLibs = append(jniLibs, jniLib{ name: ctx.OtherModuleName(module), path: path, @@ -859,6 +852,10 @@ func (a *AndroidApp) Updatable() bool { return Bool(a.appProperties.Updatable) } +func (a *AndroidApp) SetUpdatable(val bool) { + a.appProperties.Updatable = &val +} + func (a *AndroidApp) getCertString(ctx android.BaseModuleContext) string { certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(ctx.ModuleName()) if overridden { @@ -897,10 +894,6 @@ func (a *AndroidApp) SetPreventInstall() { a.appProperties.PreventInstall = true } -func (a *AndroidApp) HideFromMake() { - a.appProperties.HideFromMake = true -} - func (a *AndroidApp) MarkAsCoverageVariant(coverage bool) { a.appProperties.IsCoverageVariant = coverage } @@ -917,6 +910,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() @@ -929,8 +923,9 @@ func AndroidAppFactory() android.Module { android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) android.InitDefaultableModule(module) - android.InitOverridableModule(module, &module.appProperties.Overrides) + android.InitOverridableModule(module, &module.overridableAppProperties.Overrides) android.InitApexModule(module) + android.InitBazelModule(module) return module } @@ -994,6 +989,7 @@ func (a *AndroidTest) FixTestConfig(ctx android.ModuleContext, testConfig androi command := rule.Command().BuiltTool("test_config_fixer").Input(testConfig).Output(fixedConfig) fixNeeded := false + // Auto-generated test config uses `ModuleName` as the APK name. So fix it if it is not the case. if ctx.ModuleName() != a.installApkName { fixNeeded = true command.FlagWithArg("--test-file-name ", a.installApkName+".apk") @@ -1034,6 +1030,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 @@ -1050,7 +1047,7 @@ func AndroidTestFactory() android.Module { android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) android.InitDefaultableModule(module) - android.InitOverridableModule(module, &module.appProperties.Overrides) + android.InitOverridableModule(module, &module.overridableAppProperties.Overrides) return module } @@ -1063,6 +1060,9 @@ type appTestHelperAppProperties struct { // doesn't exist next to the Android.bp, this attribute doesn't need to be set to true // explicitly. Auto_gen_config *bool + + // Install the test into a folder named for the module in all test suites. + Per_testcase_directory *bool } type AndroidTestHelperApp struct { @@ -1104,6 +1104,8 @@ func AndroidTestHelperAppFactory() android.Module { type AndroidAppCertificate struct { android.ModuleBase + android.BazelModuleBase + properties AndroidAppCertificateProperties Certificate Certificate } @@ -1119,6 +1121,7 @@ func AndroidAppCertificateFactory() android.Module { module := &AndroidAppCertificate{} module.AddProperties(&module.properties) android.InitAndroidModule(module) + android.InitBazelModule(module) return module } @@ -1144,7 +1147,10 @@ func (i *OverrideAndroidApp) GenerateAndroidBuildActions(_ android.ModuleContext // some of its properties. func OverrideAndroidAppModuleFactory() android.Module { m := &OverrideAndroidApp{} - m.AddProperties(&overridableAppProperties{}) + m.AddProperties( + &OverridableDeviceProperties{}, + &overridableAppProperties{}, + ) android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon) android.InitOverrideModule(m) @@ -1189,6 +1195,23 @@ type UsesLibraryProperties struct { // libraries, because SDK ones are automatically picked up by Soong. The <uses-library> name // normally is the same as the module name, but there are exceptions. Provides_uses_lib *string + + // A list of shared library names to exclude from the classpath of the APK. Adding a library here + // will prevent it from being used when precompiling the APK and prevent it from being implicitly + // added to the APK's manifest's <uses-library> elements. + // + // Care must be taken when using this as it could result in runtime errors if the APK actually + // uses classes provided by the library and which are not provided in any other way. + // + // This is primarily intended for use by various CTS tests that check the runtime handling of the + // android.test.base shared library (and related libraries) but which depend on some common + // libraries that depend on the android.test.base library. Without this those tests will end up + // with a <uses-library android:name="android.test.base"/> in their manifest which would either + // render the tests worthless (as they would be testing the wrong behavior), or would break the + // test altogether by providing access to classes that the tests were not expecting. Those tests + // provide the android.test.base statically and use jarjar to rename them so they do not collide + // with the classes provided by the android.test.base library. + Exclude_uses_libs []string } // usesLibrary provides properties and helper functions for AndroidApp and AndroidAppImport to verify that the @@ -1213,18 +1236,29 @@ func (u *usesLibrary) addLib(lib string, optional bool) { } func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, hasFrameworkLibs bool) { - if !ctx.Config().UnbundledBuild() { - ctx.AddVariationDependencies(nil, usesLibTag, u.usesLibraryProperties.Uses_libs...) - ctx.AddVariationDependencies(nil, usesLibTag, u.presentOptionalUsesLibs(ctx)...) + 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)...) + // 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 { - // 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, usesLibCompat29Tag, dexpreopt.CompatUsesLibs29...) - ctx.AddVariationDependencies(nil, usesLibCompat28Tag, dexpreopt.OptionalCompatUsesLibs28...) - ctx.AddVariationDependencies(nil, usesLibCompat30Tag, dexpreopt.OptionalCompatUsesLibs30...) + // 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...) } } } @@ -1245,36 +1279,53 @@ func replaceInList(list []string, oldstr, newstr string) { } } -// Returns a map of module names of shared library dependencies to the paths -// to their dex jars on host and on device. +// Returns a map of module names of shared library dependencies to the paths to their dex jars on +// host and on device. func (u *usesLibrary) classLoaderContextForUsesLibDeps(ctx android.ModuleContext) dexpreopt.ClassLoaderContextMap { clcMap := make(dexpreopt.ClassLoaderContextMap) - if !ctx.Config().UnbundledBuild() { - ctx.VisitDirectDeps(func(m android.Module) { - if tag, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok { - dep := ctx.OtherModuleName(m) - if lib, ok := m.(UsesLibraryDependency); ok { - libName := dep - if ulib, ok := m.(ProvidesUsesLib); ok && ulib.ProvidesUsesLib() != nil { - libName = *ulib.ProvidesUsesLib() - // Replace module name with library name in `uses_libs`/`optional_uses_libs` - // in order to pass verify_uses_libraries check (which compares these - // properties against library names written in the manifest). - replaceInList(u.usesLibraryProperties.Uses_libs, dep, libName) - replaceInList(u.usesLibraryProperties.Optional_uses_libs, dep, libName) - } - clcMap.AddContext(ctx, tag.sdkVersion, libName, - lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ClassLoaderContexts()) - } else if ctx.Config().AllowMissingDependencies() { - ctx.AddMissingDependencies([]string{dep}) - } else { - ctx.ModuleErrorf("module %q in uses_libs or optional_uses_libs must be a java library", dep) - } - } - }) + // Skip when UnbundledBuild() is true, but UnbundledBuildImage() is false. With + // UnbundledBuildImage() it is necessary to generate dexpreopt.config for post-dexpreopting. + if ctx.Config().UnbundledBuild() && !ctx.Config().UnbundledBuildImage() { + return clcMap } + ctx.VisitDirectDeps(func(m android.Module) { + tag, isUsesLibTag := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag) + if !isUsesLibTag { + return + } + + dep := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(m)) + + // Skip stub libraries. A dependency on the implementation library has been added earlier, + // so it will be added to CLC, but the stub shouldn't be. Stub libraries can be distingushed + // from implementation libraries by their name, which is different as it has a suffix. + if comp, ok := m.(SdkLibraryComponentDependency); ok { + if impl := comp.OptionalSdkLibraryImplementation(); impl != nil && *impl != dep { + return + } + } + + if lib, ok := m.(UsesLibraryDependency); ok { + libName := dep + if ulib, ok := m.(ProvidesUsesLib); ok && ulib.ProvidesUsesLib() != nil { + libName = *ulib.ProvidesUsesLib() + // Replace module name with library name in `uses_libs`/`optional_uses_libs` in + // order to pass verify_uses_libraries check (which compares these properties + // against library names written in the manifest). + 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, + lib.DexJarBuildPath().PathOrNil(), lib.DexJarInstallPath(), + lib.ClassLoaderContexts()) + } else if ctx.Config().AllowMissingDependencies() { + ctx.AddMissingDependencies([]string{dep}) + } else { + ctx.ModuleErrorf("module %q in uses_libs or optional_uses_libs must be a java library", dep) + } + }) return clcMap } @@ -1352,3 +1403,94 @@ func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk andr outputFile := android.PathForModuleOut(ctx, "verify_uses_libraries", apk.Base()) return outputFile } + +// For Bazel / bp2build + +type bazelAndroidAppCertificateAttributes struct { + Certificate string +} + +func (m *AndroidAppCertificate) ConvertWithBp2build(ctx android.TopDownMutatorContext) { + androidAppCertificateBp2Build(ctx, m) +} + +func androidAppCertificateBp2Build(ctx android.TopDownMutatorContext, module *AndroidAppCertificate) { + var certificate string + if module.properties.Certificate != nil { + certificate = *module.properties.Certificate + } + + attrs := &bazelAndroidAppCertificateAttributes{ + Certificate: certificate, + } + + props := bazel.BazelTargetModuleProperties{ + Rule_class: "android_app_certificate", + Bzl_load_location: "//build/bazel/rules/android:android_app_certificate.bzl", + } + + ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs) +} + +type bazelAndroidAppAttributes struct { + *javaCommonAttributes + Deps bazel.LabelListAttribute + Manifest bazel.Label + Custom_package *string + Resource_files bazel.LabelListAttribute + Certificate *bazel.Label + Certificate_name *string +} + +// ConvertWithBp2build is used to convert android_app to Bazel. +func (a *AndroidApp) ConvertWithBp2build(ctx android.TopDownMutatorContext) { + commonAttrs, depLabels := a.convertLibraryAttrsBp2Build(ctx) + + deps := depLabels.Deps + if !commonAttrs.Srcs.IsEmpty() { + deps.Append(depLabels.StaticDeps) // we should only append these if there are sources to use them + } else if !deps.IsEmpty() || !depLabels.StaticDeps.IsEmpty() { + ctx.ModuleErrorf("android_app has dynamic or static dependencies but no sources." + + " Bazel does not allow direct dependencies without sources nor exported" + + " dependencies on android_binary rule.") + } + + manifest := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml") + + resourceFiles := bazel.LabelList{ + Includes: []bazel.Label{}, + } + for _, dir := range android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res") { + files := android.RootToModuleRelativePaths(ctx, androidResourceGlob(ctx, dir)) + resourceFiles.Includes = append(resourceFiles.Includes, files...) + } + + var certificate *bazel.Label + certificateNamePtr := a.overridableAppProperties.Certificate + certificateName := proptools.StringDefault(certificateNamePtr, "") + certModule := android.SrcIsModule(certificateName) + if certModule != "" { + c := android.BazelLabelForModuleDepSingle(ctx, certificateName) + certificate = &c + certificateNamePtr = nil + } + + attrs := &bazelAndroidAppAttributes{ + commonAttrs, + deps, + android.BazelLabelForModuleSrcSingle(ctx, manifest), + // TODO(b/209576404): handle package name override by product variable PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES + a.overridableAppProperties.Package_name, + bazel.MakeLabelListAttribute(resourceFiles), + certificate, + certificateNamePtr, + } + + props := bazel.BazelTargetModuleProperties{ + Rule_class: "android_binary", + Bzl_load_location: "//build/bazel/rules/android:android_binary.bzl", + } + + ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: a.Name()}, attrs) + +} diff --git a/java/app_builder.go b/java/app_builder.go index 4a18dcada..31023cb9c 100644 --- a/java/app_builder.go +++ b/java/app_builder.go @@ -52,7 +52,7 @@ var combineApk = pctx.AndroidStaticRule("combineApk", }) func CreateAndSignAppPackage(ctx android.ModuleContext, outputFile android.WritablePath, - packageFile, jniJarFile, dexJarFile android.Path, certificates []Certificate, deps android.Paths, v4SignatureFile android.WritablePath, lineageFile android.Path) { + packageFile, jniJarFile, dexJarFile android.Path, certificates []Certificate, deps android.Paths, v4SignatureFile android.WritablePath, lineageFile android.Path, rotationMinSdkVersion string) { unsignedApkName := strings.TrimSuffix(outputFile.Base(), ".apk") + "-unsigned.apk" unsignedApk := android.PathForModuleOut(ctx, unsignedApkName) @@ -73,10 +73,10 @@ func CreateAndSignAppPackage(ctx android.ModuleContext, outputFile android.Writa Implicits: deps, }) - SignAppPackage(ctx, outputFile, unsignedApk, certificates, v4SignatureFile, lineageFile) + SignAppPackage(ctx, outputFile, unsignedApk, certificates, v4SignatureFile, lineageFile, rotationMinSdkVersion) } -func SignAppPackage(ctx android.ModuleContext, signedApk android.WritablePath, unsignedApk android.Path, certificates []Certificate, v4SignatureFile android.WritablePath, lineageFile android.Path) { +func SignAppPackage(ctx android.ModuleContext, signedApk android.WritablePath, unsignedApk android.Path, certificates []Certificate, v4SignatureFile android.WritablePath, lineageFile android.Path, rotationMinSdkVersion string) { var certificateArgs []string var deps android.Paths @@ -97,6 +97,10 @@ func SignAppPackage(ctx android.ModuleContext, signedApk android.WritablePath, u deps = append(deps, lineageFile) } + if rotationMinSdkVersion != "" { + flags = append(flags, "--rotation-min-sdk-version", rotationMinSdkVersion) + } + rule := Signapk args := map[string]string{ "certificates": strings.Join(certificateArgs, " "), @@ -254,6 +258,18 @@ func TransformJniLibsToJar(ctx android.ModuleContext, outputFile android.Writabl }) } +func (a *AndroidApp) generateJavaUsedByApex(ctx android.ModuleContext) { + javaApiUsedByOutputFile := android.PathForModuleOut(ctx, a.installApkName+"_using.xml") + javaUsedByRule := android.NewRuleBuilder(pctx, ctx) + javaUsedByRule.Command(). + Tool(android.PathForSource(ctx, "build/soong/scripts/gen_java_usedby_apex.sh")). + BuiltTool("dexdeps"). + Output(javaApiUsedByOutputFile). + Input(a.Library.Module.outputFile) + javaUsedByRule.Build("java_usedby_list", "Generate Java APIs used by Apex") + a.javaApiUsedByOutputFile = javaApiUsedByOutputFile +} + func targetToJniDir(target android.Target) string { return filepath.Join("lib", target.Arch.Abi[0]) } diff --git a/java/app_import.go b/java/app_import.go index 3371e8e1f..58c01a447 100644 --- a/java/app_import.go +++ b/java/app_import.go @@ -22,6 +22,7 @@ import ( "github.com/google/blueprint/proptools" "android/soong/android" + "android/soong/provenance" ) func init() { @@ -57,11 +58,13 @@ type AndroidAppImport struct { installPath android.InstallPath hideApexVariantFromMake bool + + provenanceMetaDataFile android.OutputPath } type AndroidAppImportProperties struct { // A prebuilt apk to import - Apk *string + Apk *string `android:"path"` // The name of a certificate in the default certificate directory or an android_app_certificate // module name in the form ":module". Should be empty if presigned or default_dev_cert is set. @@ -77,6 +80,9 @@ type AndroidAppImportProperties struct { // Name of the signing certificate lineage file or filegroup module. Lineage *string `android:"path"` + // For overriding the --rotation-min-sdk-version property of apksig + RotationMinSdkVersion *string + // Sign with the default system dev certificate. Must be used judiciously. Most imported apps // need to either specify a specific certificate or be presigned. Default_dev_cert *bool @@ -99,6 +105,9 @@ type AndroidAppImportProperties struct { // If set, create package-export.apk, which other packages can // use to get PRODUCT-agnostic resource data like IDs and type definitions. Export_package_resources *bool + + // Optional. Install to a subdirectory of the default install path for the module + Relative_install_path *string } func (a *AndroidAppImport) IsInstallable() bool { @@ -201,9 +210,9 @@ func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool { return false } - // Uncompress dex in APKs of privileged apps - if ctx.Config().UncompressPrivAppDex() && a.Privileged() { - return true + // Uncompress dex in APKs of priv-apps if and only if DONT_UNCOMPRESS_PRIV_APPS_DEXS is false. + if a.Privileged() { + return ctx.Config().UncompressPrivAppDex() } return shouldUncompressDex(ctx, &a.dexpreopter) @@ -263,20 +272,25 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext jnisUncompressed := android.PathForModuleOut(ctx, "jnis-uncompressed", ctx.ModuleName()+".apk") a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed.OutputPath) - var installDir android.InstallPath + var pathFragments []string + relInstallPath := String(a.properties.Relative_install_path) if a.isPrebuiltFrameworkRes() { // framework-res.apk is installed as system/framework/framework-res.apk - installDir = android.PathForModuleInstall(ctx, "framework") + if relInstallPath != "" { + ctx.PropertyErrorf("relative_install_path", "Relative_install_path cannot be set for framework-res") + } + pathFragments = []string{"framework"} a.preprocessed = true } else if Bool(a.properties.Privileged) { - installDir = android.PathForModuleInstall(ctx, "priv-app", a.BaseModuleName()) + pathFragments = []string{"priv-app", relInstallPath, a.BaseModuleName()} } else if ctx.InstallInTestcases() { - installDir = android.PathForModuleInstall(ctx, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch()) + pathFragments = []string{relInstallPath, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch()} } else { - installDir = android.PathForModuleInstall(ctx, "app", a.BaseModuleName()) + pathFragments = []string{"app", relInstallPath, a.BaseModuleName()} } + installDir := android.PathForModuleInstall(ctx, pathFragments...) a.dexpreopter.isApp = true a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk") a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned) @@ -322,7 +336,10 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext if lineage := String(a.properties.Lineage); lineage != "" { lineageFile = android.PathForModuleSrc(ctx, lineage) } - SignAppPackage(ctx, signed, jnisUncompressed, certificates, nil, lineageFile) + + rotationMinSdkVersion := String(a.properties.RotationMinSdkVersion) + + SignAppPackage(ctx, signed, jnisUncompressed, certificates, nil, lineageFile, rotationMinSdkVersion) a.outputFile = signed } else { alignedApk := android.PathForModuleOut(ctx, "zip-aligned", apkFilename) @@ -335,6 +352,8 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext if apexInfo.IsForPlatform() { a.installPath = ctx.InstallFile(installDir, apkFilename, a.outputFile) + artifactPath := android.PathForModuleSrc(ctx, *a.properties.Apk) + a.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, artifactPath, a.installPath) } // TODO: androidmk converter jni libs @@ -360,6 +379,10 @@ func (a *AndroidAppImport) Certificate() Certificate { return a.certificate } +func (a *AndroidAppImport) ProvenanceMetaDataFile() android.OutputPath { + return a.provenanceMetaDataFile +} + var dpiVariantGroupType reflect.Type var archVariantGroupType reflect.Type var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"} @@ -449,7 +472,7 @@ func createVariantGroupType(variants []string, variantGroupName string) reflect. // apk: "prebuilts/example_xhdpi.apk", // }, // }, -// certificate: "PRESIGNED", +// presigned: true, // } func AndroidAppImportFactory() android.Module { module := &AndroidAppImport{} diff --git a/java/app_import_test.go b/java/app_import_test.go index 147ae45bb..41be092e2 100644 --- a/java/app_import_test.go +++ b/java/app_import_test.go @@ -15,6 +15,7 @@ package java import ( + "fmt" "reflect" "regexp" "strings" @@ -52,6 +53,11 @@ func TestAndroidAppImport(t *testing.T) { if expected != signingFlag { t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag) } + rule := variant.Rule("genProvenanceMetaData") + android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String()) + android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String()) + android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"]) + android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"]) } func TestAndroidAppImport_NoDexPreopt(t *testing.T) { @@ -73,6 +79,12 @@ func TestAndroidAppImport_NoDexPreopt(t *testing.T) { variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule != nil { t.Errorf("dexpreopt shouldn't have run.") } + + rule := variant.Rule("genProvenanceMetaData") + android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String()) + android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String()) + android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"]) + android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"]) } func TestAndroidAppImport_Presigned(t *testing.T) { @@ -101,6 +113,12 @@ func TestAndroidAppImport_Presigned(t *testing.T) { if variant.MaybeOutput("zip-aligned/foo.apk").Rule == nil { t.Errorf("can't find aligning rule") } + + rule := variant.Rule("genProvenanceMetaData") + android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String()) + android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String()) + android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"]) + android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"]) } func TestAndroidAppImport_SigningLineage(t *testing.T) { @@ -111,6 +129,7 @@ func TestAndroidAppImport_SigningLineage(t *testing.T) { certificate: "platform", additional_certificates: [":additional_certificate"], lineage: "lineage.bin", + rotationMinSdkVersion: "32", } android_app_certificate { @@ -130,12 +149,19 @@ func TestAndroidAppImport_SigningLineage(t *testing.T) { if expected != certificatesFlag { t.Errorf("Incorrect certificates flags, expected: %q, got: %q", expected, certificatesFlag) } - // Check cert signing lineage flag. - signingFlag := signedApk.Args["flags"] - expected = "--lineage lineage.bin" - if expected != signingFlag { - t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag) + + // Check cert signing flags. + actualCertSigningFlags := signedApk.Args["flags"] + expectedCertSigningFlags := "--lineage lineage.bin --rotation-min-sdk-version 32" + if expectedCertSigningFlags != actualCertSigningFlags { + t.Errorf("Incorrect signing flags, expected: %q, got: %q", expectedCertSigningFlags, actualCertSigningFlags) } + + rule := variant.Rule("genProvenanceMetaData") + android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String()) + android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String()) + android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"]) + android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"]) } func TestAndroidAppImport_SigningLineageFilegroup(t *testing.T) { @@ -162,6 +188,12 @@ func TestAndroidAppImport_SigningLineageFilegroup(t *testing.T) { if expected != signingFlag { t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag) } + + rule := variant.Rule("genProvenanceMetaData") + android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String()) + android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String()) + android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"]) + android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"]) } func TestAndroidAppImport_DefaultDevCert(t *testing.T) { @@ -191,6 +223,12 @@ func TestAndroidAppImport_DefaultDevCert(t *testing.T) { if expected != signingFlag { t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag) } + + rule := variant.Rule("genProvenanceMetaData") + android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String()) + android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String()) + android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"]) + android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"]) } func TestAndroidAppImport_DpiVariants(t *testing.T) { @@ -213,40 +251,46 @@ func TestAndroidAppImport_DpiVariants(t *testing.T) { } ` testCases := []struct { - name string - aaptPreferredConfig *string - aaptPrebuiltDPI []string - expected string + name string + aaptPreferredConfig *string + aaptPrebuiltDPI []string + expected string + expectedProvenanceMetaDataArtifactPath string }{ { - name: "no preferred", - aaptPreferredConfig: nil, - aaptPrebuiltDPI: []string{}, - expected: "verify_uses_libraries/apk/app.apk", + name: "no preferred", + aaptPreferredConfig: nil, + aaptPrebuiltDPI: []string{}, + expected: "verify_uses_libraries/apk/app.apk", + expectedProvenanceMetaDataArtifactPath: "prebuilts/apk/app.apk", }, { - name: "AAPTPreferredConfig matches", - aaptPreferredConfig: proptools.StringPtr("xhdpi"), - aaptPrebuiltDPI: []string{"xxhdpi", "ldpi"}, - expected: "verify_uses_libraries/apk/app_xhdpi.apk", + name: "AAPTPreferredConfig matches", + aaptPreferredConfig: proptools.StringPtr("xhdpi"), + aaptPrebuiltDPI: []string{"xxhdpi", "ldpi"}, + expected: "verify_uses_libraries/apk/app_xhdpi.apk", + expectedProvenanceMetaDataArtifactPath: "prebuilts/apk/app_xhdpi.apk", }, { - name: "AAPTPrebuiltDPI matches", - aaptPreferredConfig: proptools.StringPtr("mdpi"), - aaptPrebuiltDPI: []string{"xxhdpi", "xhdpi"}, - expected: "verify_uses_libraries/apk/app_xxhdpi.apk", + name: "AAPTPrebuiltDPI matches", + aaptPreferredConfig: proptools.StringPtr("mdpi"), + aaptPrebuiltDPI: []string{"xxhdpi", "xhdpi"}, + expected: "verify_uses_libraries/apk/app_xxhdpi.apk", + expectedProvenanceMetaDataArtifactPath: "prebuilts/apk/app_xxhdpi.apk", }, { - name: "non-first AAPTPrebuiltDPI matches", - aaptPreferredConfig: proptools.StringPtr("mdpi"), - aaptPrebuiltDPI: []string{"ldpi", "xhdpi"}, - expected: "verify_uses_libraries/apk/app_xhdpi.apk", + name: "non-first AAPTPrebuiltDPI matches", + aaptPreferredConfig: proptools.StringPtr("mdpi"), + aaptPrebuiltDPI: []string{"ldpi", "xhdpi"}, + expected: "verify_uses_libraries/apk/app_xhdpi.apk", + expectedProvenanceMetaDataArtifactPath: "prebuilts/apk/app_xhdpi.apk", }, { - name: "no matches", - aaptPreferredConfig: proptools.StringPtr("mdpi"), - aaptPrebuiltDPI: []string{"ldpi", "xxxhdpi"}, - expected: "verify_uses_libraries/apk/app.apk", + name: "no matches", + aaptPreferredConfig: proptools.StringPtr("mdpi"), + aaptPrebuiltDPI: []string{"ldpi", "xxxhdpi"}, + expected: "verify_uses_libraries/apk/app.apk", + expectedProvenanceMetaDataArtifactPath: "prebuilts/apk/app.apk", }, } @@ -269,6 +313,12 @@ func TestAndroidAppImport_DpiVariants(t *testing.T) { if strings.HasSuffix(matches[1], test.expected) { t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1]) } + + provenanceMetaDataRule := variant.Rule("genProvenanceMetaData") + android.AssertStringEquals(t, "Invalid input", test.expectedProvenanceMetaDataArtifactPath, provenanceMetaDataRule.Inputs[0].String()) + android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", provenanceMetaDataRule.Output.String()) + android.AssertStringEquals(t, "Invalid args", "foo", provenanceMetaDataRule.Args["module_name"]) + android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", provenanceMetaDataRule.Args["install_path"]) } } @@ -289,16 +339,25 @@ func TestAndroidAppImport_Filename(t *testing.T) { `) testCases := []struct { - name string - expected string + name string + expected string + onDevice string + expectedArtifactPath string + expectedMetaDataPath string }{ { - name: "foo", - expected: "foo.apk", + name: "foo", + expected: "foo.apk", + onDevice: "/system/app/foo/foo.apk", + expectedArtifactPath: "prebuilts/apk/app.apk", + expectedMetaDataPath: "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", }, { - name: "bar", - expected: "bar_sample.apk", + name: "bar", + expected: "bar_sample.apk", + onDevice: "/system/app/bar/bar_sample.apk", + expectedArtifactPath: "prebuilts/apk/app.apk", + expectedMetaDataPath: "out/soong/.intermediates/provenance_metadata/bar/provenance_metadata.textproto", }, } @@ -315,15 +374,23 @@ func TestAndroidAppImport_Filename(t *testing.T) { t.Errorf("Incorrect LOCAL_INSTALLED_MODULE_STEM value '%s', expected '%s'", actualValues, expectedValues) } + rule := variant.Rule("genProvenanceMetaData") + android.AssertStringEquals(t, "Invalid input", test.expectedArtifactPath, rule.Inputs[0].String()) + android.AssertStringEquals(t, "Invalid output", test.expectedMetaDataPath, rule.Output.String()) + android.AssertStringEquals(t, "Invalid args", test.name, rule.Args["module_name"]) + android.AssertStringEquals(t, "Invalid args", test.onDevice, rule.Args["install_path"]) } } func TestAndroidAppImport_ArchVariants(t *testing.T) { // The test config's target arch is ARM64. testCases := []struct { - name string - bp string - expected string + name string + bp string + expected string + artifactPath string + metaDataPath string + installPath string }{ { name: "matching arch", @@ -342,7 +409,9 @@ func TestAndroidAppImport_ArchVariants(t *testing.T) { }, } `, - expected: "verify_uses_libraries/apk/app_arm64.apk", + expected: "verify_uses_libraries/apk/app_arm64.apk", + artifactPath: "prebuilts/apk/app_arm64.apk", + installPath: "/system/app/foo/foo.apk", }, { name: "no matching arch", @@ -361,7 +430,9 @@ func TestAndroidAppImport_ArchVariants(t *testing.T) { }, } `, - expected: "verify_uses_libraries/apk/app.apk", + expected: "verify_uses_libraries/apk/app.apk", + artifactPath: "prebuilts/apk/app.apk", + installPath: "/system/app/foo/foo.apk", }, { name: "no matching arch without default", @@ -379,7 +450,9 @@ func TestAndroidAppImport_ArchVariants(t *testing.T) { }, } `, - expected: "", + expected: "", + artifactPath: "prebuilts/apk/app_arm.apk", + installPath: "/system/app/foo/foo.apk", }, } @@ -392,6 +465,8 @@ func TestAndroidAppImport_ArchVariants(t *testing.T) { if variant.Module().Enabled() { t.Error("module should have been disabled, but wasn't") } + rule := variant.MaybeRule("genProvenanceMetaData") + android.AssertDeepEquals(t, "Provenance metadata is not empty", android.TestingBuildParams{}, rule) continue } jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command @@ -402,6 +477,11 @@ func TestAndroidAppImport_ArchVariants(t *testing.T) { if strings.HasSuffix(matches[1], test.expected) { t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1]) } + rule := variant.Rule("genProvenanceMetaData") + android.AssertStringEquals(t, "Invalid input", test.artifactPath, rule.Inputs[0].String()) + android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String()) + android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"]) + android.AssertStringEquals(t, "Invalid args", test.installPath, rule.Args["install_path"]) } } @@ -493,6 +573,69 @@ func TestAndroidAppImport_frameworkRes(t *testing.T) { } } +func TestAndroidAppImport_relativeInstallPath(t *testing.T) { + bp := ` + android_app_import { + name: "no_relative_install_path", + apk: "prebuilts/apk/app.apk", + presigned: true, + } + + android_app_import { + name: "relative_install_path", + apk: "prebuilts/apk/app.apk", + presigned: true, + relative_install_path: "my/path", + } + + android_app_import { + name: "framework-res", + apk: "prebuilts/apk/app.apk", + presigned: true, + prefer: true, + } + + android_app_import { + name: "privileged_relative_install_path", + apk: "prebuilts/apk/app.apk", + presigned: true, + privileged: true, + relative_install_path: "my/path" + } + ` + testCases := []struct { + name string + expectedInstallPath string + errorMessage string + }{ + { + name: "no_relative_install_path", + expectedInstallPath: "out/soong/target/product/test_device/system/app/no_relative_install_path/no_relative_install_path.apk", + errorMessage: "Install path is not correct when relative_install_path is missing", + }, + { + name: "relative_install_path", + expectedInstallPath: "out/soong/target/product/test_device/system/app/my/path/relative_install_path/relative_install_path.apk", + errorMessage: "Install path is not correct for app when relative_install_path is present", + }, + { + name: "prebuilt_framework-res", + expectedInstallPath: "out/soong/target/product/test_device/system/framework/framework-res.apk", + errorMessage: "Install path is not correct for framework-res", + }, + { + name: "privileged_relative_install_path", + expectedInstallPath: "out/soong/target/product/test_device/system/priv-app/my/path/privileged_relative_install_path/privileged_relative_install_path.apk", + errorMessage: "Install path is not correct for privileged app when relative_install_path is present", + }, + } + for _, testCase := range testCases { + ctx, _ := testJava(t, bp) + mod := ctx.ModuleForTests(testCase.name, "android_common").Module().(*AndroidAppImport) + android.AssertPathRelativeToTopEquals(t, testCase.errorMessage, testCase.expectedInstallPath, mod.installPath) + } +} + func TestAndroidTestImport(t *testing.T) { ctx, _ := testJava(t, ` android_test_import { @@ -593,3 +736,74 @@ func TestAndroidTestImport_Preprocessed(t *testing.T) { } } } + +func TestAndroidTestImport_UncompressDex(t *testing.T) { + testCases := []struct { + name string + bp string + }{ + { + name: "normal", + bp: ` + android_app_import { + name: "foo", + presigned: true, + apk: "prebuilts/apk/app.apk", + } + `, + }, + { + name: "privileged", + bp: ` + android_app_import { + name: "foo", + presigned: true, + privileged: true, + apk: "prebuilts/apk/app.apk", + } + `, + }, + } + + test := func(t *testing.T, bp string, unbundled bool, dontUncompressPrivAppDexs bool) { + t.Helper() + + result := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + if unbundled { + variables.Unbundled_build = proptools.BoolPtr(true) + } + variables.UncompressPrivAppDex = proptools.BoolPtr(!dontUncompressPrivAppDexs) + }), + ).RunTestWithBp(t, bp) + + foo := result.ModuleForTests("foo", "android_common") + actual := foo.MaybeRule("uncompress-dex").Rule != nil + + expect := !unbundled + if strings.Contains(bp, "privileged: true") { + if dontUncompressPrivAppDexs { + expect = false + } else { + // TODO(b/194504107): shouldn't priv-apps be always uncompressed unless + // DONT_UNCOMPRESS_PRIV_APPS_DEXS is true (regardless of unbundling)? + // expect = true + } + } + + android.AssertBoolEquals(t, "uncompress dex", expect, actual) + } + + for _, unbundled := range []bool{false, true} { + for _, dontUncompressPrivAppDexs := range []bool{false, true} { + for _, tt := range testCases { + name := fmt.Sprintf("%s,unbundled:%t,dontUncompressPrivAppDexs:%t", + tt.name, unbundled, dontUncompressPrivAppDexs) + t.Run(name, func(t *testing.T) { + test(t, tt.bp, unbundled, dontUncompressPrivAppDexs) + }) + } + } + } +} diff --git a/java/app_set.go b/java/app_set.go index 6b25638da..694b1670e 100644 --- a/java/app_set.go +++ b/java/app_set.go @@ -55,10 +55,10 @@ type AndroidAppSet struct { android.DefaultableModuleBase prebuilt android.Prebuilt - properties AndroidAppSetProperties - packedOutput android.WritablePath - installFile string - apkcertsFile android.ModuleOutPath + properties AndroidAppSetProperties + packedOutput android.WritablePath + primaryOutput android.WritablePath + apkcertsFile android.ModuleOutPath } func (as *AndroidAppSet) Name() string { @@ -78,11 +78,11 @@ func (as *AndroidAppSet) Privileged() bool { } func (as *AndroidAppSet) OutputFile() android.Path { - return as.packedOutput + return as.primaryOutput } -func (as *AndroidAppSet) InstallFile() string { - return as.installFile +func (as *AndroidAppSet) PackedAdditionalOutputs() android.Path { + return as.packedOutput } func (as *AndroidAppSet) APKCertsFile() android.Path { @@ -114,11 +114,11 @@ func SupportedAbis(ctx android.ModuleContext) []string { func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) { as.packedOutput = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip") + as.primaryOutput = android.PathForModuleOut(ctx, as.BaseModuleName()+".apk") as.apkcertsFile = android.PathForModuleOut(ctx, "apkcerts.txt") // We are assuming here that the install file in the APK // set has `.apk` suffix. If it doesn't the build will fail. // APK sets containing APEX files are handled elsewhere. - as.installFile = as.BaseModuleName() + ".apk" screenDensities := "all" if dpis := ctx.Config().ProductAAPTPrebuiltDPI(); len(dpis) > 0 { screenDensities = strings.ToUpper(strings.Join(dpis, ",")) @@ -127,11 +127,11 @@ func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) // TODO(asmundak): do we support device features ctx.Build(pctx, android.BuildParams{ - Rule: extractMatchingApks, - Description: "Extract APKs from APK set", - Output: as.packedOutput, - ImplicitOutput: as.apkcertsFile, - Inputs: android.Paths{as.prebuilt.SingleSourcePath(ctx)}, + Rule: extractMatchingApks, + Description: "Extract APKs from APK set", + Output: as.primaryOutput, + ImplicitOutputs: android.WritablePaths{as.packedOutput, as.apkcertsFile}, + Inputs: android.Paths{as.prebuilt.SingleSourcePath(ctx)}, Args: map[string]string{ "abis": strings.Join(SupportedAbis(ctx), ","), "allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)), @@ -140,10 +140,21 @@ func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) "stem": as.BaseModuleName(), "apkcerts": as.apkcertsFile.String(), "partition": as.PartitionTag(ctx.DeviceConfig()), + "zip": as.packedOutput.String(), }, }) + + var installDir android.InstallPath + if as.Privileged() { + installDir = android.PathForModuleInstall(ctx, "priv-app", as.BaseModuleName()) + } else { + installDir = android.PathForModuleInstall(ctx, "app", as.BaseModuleName()) + } + ctx.InstallFileWithExtraFilesZip(installDir, as.BaseModuleName()+".apk", as.primaryOutput, as.packedOutput) } +func (as *AndroidAppSet) InstallBypassMake() bool { return true } + // android_app_set extracts a set of APKs based on the target device // configuration and installs this set as "split APKs". // The extracted set always contains an APK whose name is diff --git a/java/app_set_test.go b/java/app_set_test.go index adaf71bab..03eb66786 100644 --- a/java/app_set_test.go +++ b/java/app_set_test.go @@ -17,19 +17,20 @@ package java import ( "fmt" "reflect" + "strings" "testing" "android/soong/android" ) func TestAndroidAppSet(t *testing.T) { - ctx, _ := testJava(t, ` + result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, ` android_app_set { name: "foo", set: "prebuilts/apks/app.apks", prerelease: true, }`) - module := ctx.ModuleForTests("foo", "android_common") + module := result.ModuleForTests("foo", "android_common") const packedSplitApks = "foo.zip" params := module.Output(packedSplitApks) if params.Rule == nil { @@ -41,9 +42,22 @@ func TestAndroidAppSet(t *testing.T) { if s := params.Args["partition"]; s != "system" { t.Errorf("wrong partition value: '%s', expected 'system'", s) } - mkEntries := android.AndroidMkEntriesForTest(t, ctx, module.Module())[0] + + android.AssertPathRelativeToTopEquals(t, "incorrect output path", + "out/soong/.intermediates/foo/android_common/foo.apk", params.Output) + + android.AssertPathsRelativeToTopEquals(t, "incorrect implicit output paths", + []string{ + "out/soong/.intermediates/foo/android_common/foo.zip", + "out/soong/.intermediates/foo/android_common/apkcerts.txt", + }, + params.ImplicitOutputs.Paths()) + + mkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, module.Module())[0] actualInstallFile := mkEntries.EntryMap["LOCAL_APK_SET_INSTALL_FILE"] - expectedInstallFile := []string{"foo.apk"} + expectedInstallFile := []string{ + strings.Replace(params.ImplicitOutputs[0].String(), android.OutSoongDir, result.Config.SoongOutDir(), 1), + } if !reflect.DeepEqual(actualInstallFile, expectedInstallFile) { t.Errorf("Unexpected LOCAL_APK_SET_INSTALL_FILE value: '%s', expected: '%s',", actualInstallFile, expectedInstallFile) diff --git a/java/app_test.go b/java/app_test.go index a99ac62da..8e331d46e 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -27,7 +27,6 @@ import ( "android/soong/android" "android/soong/cc" "android/soong/dexpreopt" - "android/soong/genrule" ) // testApp runs tests using the prepareForJavaTest @@ -144,14 +143,14 @@ func TestPlatformAPIs(t *testing.T) { } `) - testJavaError(t, "platform_apis must be true when sdk_version is empty.", ` + testJavaError(t, "This module has conflicting settings. sdk_version is empty, which means that this module is build against platform APIs. However platform_apis is not set to true", ` android_app { name: "bar", srcs: ["b.java"], } `) - testJavaError(t, "platform_apis must be false when sdk_version is not empty.", ` + testJavaError(t, "This module has conflicting settings. sdk_version is not empty, which means this module cannot use platform APIs. However platform_apis is set to true.", ` android_app { name: "bar", srcs: ["b.java"], @@ -428,7 +427,8 @@ func TestUpdatableApps_JniLibShouldBeBuiltAgainstMinSdkVersion(t *testing.T) { name: "libjni", stl: "none", system_shared_libs: [], - sdk_version: "29", + sdk_version: "current", + min_sdk_version: "29", } ` fs := map[string][]byte{ @@ -482,12 +482,13 @@ func TestUpdatableApps_ErrorIfJniLibDoesntSupportMinSdkVersion(t *testing.T) { name: "libjni", stl: "none", sdk_version: "current", + min_sdk_version: "current", } ` - testJavaError(t, `"libjni" .*: sdk_version\(current\) is higher than min_sdk_version\(29\)`, bp) + testJavaError(t, `"libjni" .*: min_sdk_version\(current\) is higher than min_sdk_version\(29\)`, bp) } -func TestUpdatableApps_ErrorIfDepSdkVersionIsHigher(t *testing.T) { +func TestUpdatableApps_ErrorIfDepMinSdkVersionIsHigher(t *testing.T) { bp := cc.GatherRequiredDepsForTest(android.Android) + ` android_app { name: "foo", @@ -504,6 +505,7 @@ func TestUpdatableApps_ErrorIfDepSdkVersionIsHigher(t *testing.T) { shared_libs: ["libbar"], system_shared_libs: [], sdk_version: "27", + min_sdk_version: "27", } cc_library { @@ -511,6 +513,7 @@ func TestUpdatableApps_ErrorIfDepSdkVersionIsHigher(t *testing.T) { stl: "none", system_shared_libs: [], sdk_version: "current", + min_sdk_version: "current", } ` testJavaError(t, `"libjni" .*: links "libbar" built against newer API version "current"`, bp) @@ -1485,11 +1488,11 @@ func TestJNISDK(t *testing.T) { func TestCertificates(t *testing.T) { testCases := []struct { - name string - bp string - certificateOverride string - expectedLineage string - expectedCertificate string + name string + bp string + certificateOverride string + expectedCertSigningFlags string + expectedCertificate string }{ { name: "default", @@ -1500,9 +1503,9 @@ func TestCertificates(t *testing.T) { sdk_version: "current", } `, - certificateOverride: "", - expectedLineage: "", - expectedCertificate: "build/make/target/product/security/testkey.x509.pem build/make/target/product/security/testkey.pk8", + certificateOverride: "", + expectedCertSigningFlags: "", + expectedCertificate: "build/make/target/product/security/testkey.x509.pem build/make/target/product/security/testkey.pk8", }, { name: "module certificate property", @@ -1519,9 +1522,9 @@ func TestCertificates(t *testing.T) { certificate: "cert/new_cert", } `, - certificateOverride: "", - expectedLineage: "", - expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8", + certificateOverride: "", + expectedCertSigningFlags: "", + expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8", }, { name: "path certificate property", @@ -1533,9 +1536,9 @@ func TestCertificates(t *testing.T) { sdk_version: "current", } `, - certificateOverride: "", - expectedLineage: "", - expectedCertificate: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", + certificateOverride: "", + expectedCertSigningFlags: "", + expectedCertificate: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", }, { name: "certificate overrides", @@ -1552,18 +1555,19 @@ func TestCertificates(t *testing.T) { certificate: "cert/new_cert", } `, - certificateOverride: "foo:new_certificate", - expectedLineage: "", - expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8", + certificateOverride: "foo:new_certificate", + expectedCertSigningFlags: "", + expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8", }, { - name: "certificate lineage", + name: "certificate signing flags", bp: ` android_app { name: "foo", srcs: ["a.java"], certificate: ":new_certificate", lineage: "lineage.bin", + rotationMinSdkVersion: "32", sdk_version: "current", } @@ -1572,18 +1576,19 @@ func TestCertificates(t *testing.T) { certificate: "cert/new_cert", } `, - certificateOverride: "", - expectedLineage: "--lineage lineage.bin", - expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8", + certificateOverride: "", + expectedCertSigningFlags: "--lineage lineage.bin --rotation-min-sdk-version 32", + expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8", }, { - name: "lineage from filegroup", + name: "cert signing flags from filegroup", bp: ` android_app { name: "foo", srcs: ["a.java"], certificate: ":new_certificate", lineage: ":lineage_bin", + rotationMinSdkVersion: "32", sdk_version: "current", } @@ -1597,9 +1602,9 @@ func TestCertificates(t *testing.T) { srcs: ["lineage.bin"], } `, - certificateOverride: "", - expectedLineage: "--lineage lineage.bin", - expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8", + certificateOverride: "", + expectedCertSigningFlags: "--lineage lineage.bin --rotation-min-sdk-version 32", + expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8", }, } @@ -1620,8 +1625,8 @@ func TestCertificates(t *testing.T) { signCertificateFlags := signapk.Args["certificates"] android.AssertStringEquals(t, "certificates flags", test.expectedCertificate, signCertificateFlags) - signFlags := signapk.Args["flags"] - android.AssertStringEquals(t, "signing flags", test.expectedLineage, signFlags) + certSigningFlags := signapk.Args["flags"] + android.AssertStringEquals(t, "cert signing flags", test.expectedCertSigningFlags, certSigningFlags) }) } } @@ -1707,7 +1712,7 @@ func TestPackageNameOverride(t *testing.T) { }, }, { - name: "overridden", + name: "overridden via PRODUCT_PACKAGE_NAME_OVERRIDES", bp: ` android_app { name: "foo", @@ -1722,6 +1727,22 @@ func TestPackageNameOverride(t *testing.T) { "out/soong/target/product/test_device/system/app/bar/bar.apk", }, }, + { + name: "overridden via stem", + bp: ` + android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "current", + stem: "bar", + } + `, + packageNameOverride: "", + expected: []string{ + "out/soong/.intermediates/foo/android_common/bar.apk", + "out/soong/target/product/test_device/system/app/bar/bar.apk", + }, + }, } for _, test := range testCases { @@ -1737,7 +1758,7 @@ func TestPackageNameOverride(t *testing.T) { foo := result.ModuleForTests("foo", "android_common") - outSoongDir := result.Config.BuildDir() + outSoongDir := result.Config.SoongOutDir() outputs := foo.AllOutputs() outputMap := make(map[string]bool) @@ -1800,6 +1821,7 @@ func TestOverrideAndroidApp(t *testing.T) { base: "foo", certificate: ":new_certificate", lineage: "lineage.bin", + rotationMinSdkVersion: "32", logging_parent: "bah", } @@ -1845,89 +1867,89 @@ func TestOverrideAndroidApp(t *testing.T) { `) expectedVariants := []struct { - name string - moduleName string - variantName string - apkName string - apkPath string - certFlag string - lineageFlag string - overrides []string - packageFlag string - renameResources bool - logging_parent string + name string + moduleName string + variantName string + apkName string + apkPath string + certFlag string + certSigningFlags string + overrides []string + packageFlag string + renameResources bool + logging_parent string }{ { - name: "foo", - moduleName: "foo", - variantName: "android_common", - apkPath: "out/soong/target/product/test_device/system/app/foo/foo.apk", - certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", - lineageFlag: "", - overrides: []string{"qux"}, - packageFlag: "", - renameResources: false, - logging_parent: "", - }, - { - name: "foo", - moduleName: "bar", - variantName: "android_common_bar", - apkPath: "out/soong/target/product/test_device/system/app/bar/bar.apk", - certFlag: "cert/new_cert.x509.pem cert/new_cert.pk8", - lineageFlag: "--lineage lineage.bin", - overrides: []string{"qux", "foo"}, - packageFlag: "", - renameResources: false, - logging_parent: "bah", - }, - { - name: "foo", - moduleName: "baz", - variantName: "android_common_baz", - apkPath: "out/soong/target/product/test_device/system/app/baz/baz.apk", - certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", - lineageFlag: "", - overrides: []string{"qux", "foo"}, - packageFlag: "org.dandroid.bp", - renameResources: true, - logging_parent: "", - }, - { - name: "foo", - moduleName: "baz_no_rename_resources", - variantName: "android_common_baz_no_rename_resources", - apkPath: "out/soong/target/product/test_device/system/app/baz_no_rename_resources/baz_no_rename_resources.apk", - certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", - lineageFlag: "", - overrides: []string{"qux", "foo"}, - packageFlag: "org.dandroid.bp", - renameResources: false, - logging_parent: "", - }, - { - name: "foo_no_rename_resources", - moduleName: "baz_base_no_rename_resources", - variantName: "android_common_baz_base_no_rename_resources", - apkPath: "out/soong/target/product/test_device/system/app/baz_base_no_rename_resources/baz_base_no_rename_resources.apk", - certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", - lineageFlag: "", - overrides: []string{"qux", "foo_no_rename_resources"}, - packageFlag: "org.dandroid.bp", - renameResources: false, - logging_parent: "", - }, - { - name: "foo_no_rename_resources", - moduleName: "baz_override_base_rename_resources", - variantName: "android_common_baz_override_base_rename_resources", - apkPath: "out/soong/target/product/test_device/system/app/baz_override_base_rename_resources/baz_override_base_rename_resources.apk", - certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", - lineageFlag: "", - overrides: []string{"qux", "foo_no_rename_resources"}, - packageFlag: "org.dandroid.bp", - renameResources: true, - logging_parent: "", + name: "foo", + moduleName: "foo", + variantName: "android_common", + apkPath: "out/soong/target/product/test_device/system/app/foo/foo.apk", + certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", + certSigningFlags: "", + overrides: []string{"qux"}, + packageFlag: "", + renameResources: false, + logging_parent: "", + }, + { + name: "foo", + moduleName: "bar", + variantName: "android_common_bar", + apkPath: "out/soong/target/product/test_device/system/app/bar/bar.apk", + certFlag: "cert/new_cert.x509.pem cert/new_cert.pk8", + certSigningFlags: "--lineage lineage.bin --rotation-min-sdk-version 32", + overrides: []string{"qux", "foo"}, + packageFlag: "", + renameResources: false, + logging_parent: "bah", + }, + { + name: "foo", + moduleName: "baz", + variantName: "android_common_baz", + apkPath: "out/soong/target/product/test_device/system/app/baz/baz.apk", + certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", + certSigningFlags: "", + overrides: []string{"qux", "foo"}, + packageFlag: "org.dandroid.bp", + renameResources: true, + logging_parent: "", + }, + { + name: "foo", + moduleName: "baz_no_rename_resources", + variantName: "android_common_baz_no_rename_resources", + apkPath: "out/soong/target/product/test_device/system/app/baz_no_rename_resources/baz_no_rename_resources.apk", + certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", + certSigningFlags: "", + overrides: []string{"qux", "foo"}, + packageFlag: "org.dandroid.bp", + renameResources: false, + logging_parent: "", + }, + { + name: "foo_no_rename_resources", + moduleName: "baz_base_no_rename_resources", + variantName: "android_common_baz_base_no_rename_resources", + apkPath: "out/soong/target/product/test_device/system/app/baz_base_no_rename_resources/baz_base_no_rename_resources.apk", + certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", + certSigningFlags: "", + overrides: []string{"qux", "foo_no_rename_resources"}, + packageFlag: "org.dandroid.bp", + renameResources: false, + logging_parent: "", + }, + { + name: "foo_no_rename_resources", + moduleName: "baz_override_base_rename_resources", + variantName: "android_common_baz_override_base_rename_resources", + apkPath: "out/soong/target/product/test_device/system/app/baz_override_base_rename_resources/baz_override_base_rename_resources.apk", + certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", + certSigningFlags: "", + overrides: []string{"qux", "foo_no_rename_resources"}, + packageFlag: "org.dandroid.bp", + renameResources: true, + logging_parent: "", }, } for _, expected := range expectedVariants { @@ -1941,13 +1963,13 @@ func TestOverrideAndroidApp(t *testing.T) { certFlag := signapk.Args["certificates"] android.AssertStringEquals(t, "certificates flags", expected.certFlag, certFlag) - // Check the lineage flags - lineageFlag := signapk.Args["flags"] - android.AssertStringEquals(t, "signing flags", expected.lineageFlag, lineageFlag) + // Check the cert signing flags + certSigningFlags := signapk.Args["flags"] + android.AssertStringEquals(t, "cert signing flags", expected.certSigningFlags, certSigningFlags) // Check if the overrides field values are correctly aggregated. mod := variant.Module().(*AndroidApp) - android.AssertDeepEquals(t, "overrides property", expected.overrides, mod.appProperties.Overrides) + android.AssertDeepEquals(t, "overrides property", expected.overrides, mod.overridableAppProperties.Overrides) // Test Overridable property: Logging_parent logging_parent := mod.aapt.LoggingParent @@ -1965,6 +1987,173 @@ func TestOverrideAndroidApp(t *testing.T) { } } +func TestOverrideAndroidAppOverrides(t *testing.T) { + ctx, _ := testJava( + t, ` + android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "current", + overrides: ["qux"] + } + + android_app { + name: "bar", + srcs: ["b.java"], + sdk_version: "current", + overrides: ["foo"] + } + + override_android_app { + name: "foo_override", + base: "foo", + overrides: ["bar"] + } + `) + + expectedVariants := []struct { + name string + moduleName string + variantName string + overrides []string + }{ + { + name: "foo", + moduleName: "foo", + variantName: "android_common", + overrides: []string{"qux"}, + }, + { + name: "bar", + moduleName: "bar", + variantName: "android_common", + overrides: []string{"foo"}, + }, + { + name: "foo", + moduleName: "foo_override", + variantName: "android_common_foo_override", + overrides: []string{"bar", "foo"}, + }, + } + for _, expected := range expectedVariants { + variant := ctx.ModuleForTests(expected.name, expected.variantName) + + // Check if the overrides field values are correctly aggregated. + mod := variant.Module().(*AndroidApp) + android.AssertDeepEquals(t, "overrides property", expected.overrides, mod.overridableAppProperties.Overrides) + } +} + +func TestOverrideAndroidAppWithPrebuilt(t *testing.T) { + result := PrepareForTestWithJavaDefaultModules.RunTestWithBp( + t, ` + android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "current", + } + + override_android_app { + name: "bar", + base: "foo", + } + + android_app_import { + name: "bar", + prefer: true, + apk: "bar.apk", + presigned: true, + } + `) + + // An app that has an override that also has a prebuilt should not be hidden. + foo := result.ModuleForTests("foo", "android_common") + if foo.Module().IsHideFromMake() { + t.Errorf("expected foo to have HideFromMake false") + } + + // An override that also has a prebuilt should be hidden. + barOverride := result.ModuleForTests("foo", "android_common_bar") + if !barOverride.Module().IsHideFromMake() { + t.Errorf("expected bar override variant of foo to have HideFromMake true") + } +} + +func TestOverrideAndroidAppStem(t *testing.T) { + ctx, _ := testJava(t, ` + android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "current", + } + override_android_app { + name: "bar", + base: "foo", + } + override_android_app { + name: "baz", + base: "foo", + stem: "baz_stem", + } + android_app { + name: "foo2", + srcs: ["a.java"], + sdk_version: "current", + stem: "foo2_stem", + } + override_android_app { + name: "bar2", + base: "foo2", + } + override_android_app { + name: "baz2", + base: "foo2", + stem: "baz2_stem", + } + `) + for _, expected := range []struct { + moduleName string + variantName string + apkPath string + }{ + { + moduleName: "foo", + variantName: "android_common", + apkPath: "out/soong/target/product/test_device/system/app/foo/foo.apk", + }, + { + moduleName: "foo", + variantName: "android_common_bar", + apkPath: "out/soong/target/product/test_device/system/app/bar/bar.apk", + }, + { + moduleName: "foo", + variantName: "android_common_baz", + apkPath: "out/soong/target/product/test_device/system/app/baz_stem/baz_stem.apk", + }, + { + moduleName: "foo2", + variantName: "android_common", + apkPath: "out/soong/target/product/test_device/system/app/foo2_stem/foo2_stem.apk", + }, + { + moduleName: "foo2", + variantName: "android_common_bar2", + // Note that this may cause the duplicate output error. + apkPath: "out/soong/target/product/test_device/system/app/foo2_stem/foo2_stem.apk", + }, + { + moduleName: "foo2", + variantName: "android_common_baz2", + apkPath: "out/soong/target/product/test_device/system/app/baz2_stem/baz2_stem.apk", + }, + } { + variant := ctx.ModuleForTests(expected.moduleName, expected.variantName) + variant.Output(expected.apkPath) + } +} + func TestOverrideAndroidAppDependency(t *testing.T) { ctx, _ := testJava(t, ` android_app { @@ -2071,9 +2260,9 @@ func TestOverrideAndroidTest(t *testing.T) { // Check if the overrides field values are correctly aggregated. mod := variant.Module().(*AndroidTest) - if !reflect.DeepEqual(expected.overrides, mod.appProperties.Overrides) { + if !reflect.DeepEqual(expected.overrides, mod.overridableAppProperties.Overrides) { t.Errorf("Incorrect overrides property value, expected: %q, got: %q", - expected.overrides, mod.appProperties.Overrides) + expected.overrides, mod.overridableAppProperties.Overrides) } // Check if javac classpath has the correct jar file path. This checks instrumentation_for overrides. @@ -2168,10 +2357,33 @@ func TestAndroidTest_FixTestConfig(t *testing.T) { t.Errorf("test_config_fixer was not expected to run, but did: %q", params.RuleParams.Command) } } - } } +func TestInstrumentationTargetPrebuilt(t *testing.T) { + bp := ` + android_app_import { + name: "foo", + apk: "foo.apk", + presigned: true, + } + + android_test { + name: "bar", + srcs: ["a.java"], + instrumentation_for: "foo", + sdk_version: "current", + } + ` + + android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + ).ExtendWithErrorHandler( + android.FixtureExpectsAtLeastOneErrorMatchingPattern( + "instrumentation_for: dependency \"foo\" of type \"android_app_import\" does not provide JavaInfo so is unsuitable for use with this property")). + RunTestWithBp(t, bp) +} + func TestStl(t *testing.T) { ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+` cc_library { @@ -2285,6 +2497,49 @@ func TestUsesLibraries(t *testing.T) { sdk_version: "current", } + java_library { + name: "runtime-required-x", + srcs: ["a.java"], + installable: true, + sdk_version: "current", + } + + java_library { + name: "runtime-optional-x", + srcs: ["a.java"], + installable: true, + sdk_version: "current", + } + + android_library { + name: "static-x", + uses_libs: ["runtime-required-x"], + optional_uses_libs: ["runtime-optional-x"], + sdk_version: "current", + } + + java_library { + name: "runtime-required-y", + srcs: ["a.java"], + installable: true, + sdk_version: "current", + } + + java_library { + name: "runtime-optional-y", + srcs: ["a.java"], + installable: true, + sdk_version: "current", + } + + java_library { + name: "static-y", + srcs: ["a.java"], + uses_libs: ["runtime-required-y"], + optional_uses_libs: ["runtime-optional-y"], + sdk_version: "current", + } + // A library that has to use "provides_uses_lib", because: // - it is not an SDK library // - its library name is different from its module name @@ -2307,6 +2562,8 @@ func TestUsesLibraries(t *testing.T) { // statically linked component libraries should not pull their SDK libraries, // so "fred" should not be added to class loader context "fred.stubs", + "static-x", + "static-y", ], uses_libs: [ "foo", @@ -2353,11 +2610,8 @@ func TestUsesLibraries(t *testing.T) { expectManifestFixerArgs := `--extract-native-libs=true ` + `--uses-library qux ` + `--uses-library quuz ` + - `--uses-library foo ` + // TODO(b/132357300): "foo" should not be passed to manifest_fixer - `--uses-library com.non.sdk.lib ` + // TODO(b/132357300): "com.non.sdk.lib" should not be passed to manifest_fixer - `--uses-library bar ` + // TODO(b/132357300): "bar" should not be passed to manifest_fixer `--uses-library runtime-library` - android.AssertStringEquals(t, "manifest_fixer args", expectManifestFixerArgs, actualManifestFixerArgs) + android.AssertStringDoesContain(t, "manifest_fixer args", actualManifestFixerArgs, expectManifestFixerArgs) // Test that all libraries are verified (library order matters). verifyCmd := app.Rule("verify_uses_libraries").RuleParams.Command @@ -2366,8 +2620,12 @@ func TestUsesLibraries(t *testing.T) { `--uses-library qux ` + `--uses-library quuz ` + `--uses-library runtime-library ` + + `--uses-library runtime-required-x ` + + `--uses-library runtime-required-y ` + `--optional-uses-library bar ` + - `--optional-uses-library baz ` + `--optional-uses-library baz ` + + `--optional-uses-library runtime-optional-x ` + + `--optional-uses-library runtime-optional-y ` android.AssertStringDoesContain(t, "verify cmd args", verifyCmd, verifyArgs) // Test that all libraries are verified for an APK (library order matters). @@ -2387,7 +2645,11 @@ func TestUsesLibraries(t *testing.T) { `PCL[/system/framework/foo.jar]#` + `PCL[/system/framework/non-sdk-lib.jar]#` + `PCL[/system/framework/bar.jar]#` + - `PCL[/system/framework/runtime-library.jar]` + `PCL[/system/framework/runtime-library.jar]#` + + `PCL[/system/framework/runtime-required-x.jar]#` + + `PCL[/system/framework/runtime-optional-x.jar]#` + + `PCL[/system/framework/runtime-required-y.jar]#` + + `PCL[/system/framework/runtime-optional-y.jar] ` android.AssertStringDoesContain(t, "dexpreopt app cmd args", cmd, w) // Test conditional context for target SDK version 28. @@ -2471,7 +2733,7 @@ func TestDexpreoptBcp(t *testing.T) { PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("runtime-library", "foo", "bar"), dexpreopt.FixtureSetBootJars("platform:foo"), - dexpreopt.FixtureSetUpdatableBootJars("platform:bar"), + dexpreopt.FixtureSetApexBootJars("platform:bar"), dexpreopt.FixtureSetPreoptWithUpdatableBcp(test.with), ).RunTestWithBp(t, bp) @@ -2559,116 +2821,6 @@ func TestCodelessApp(t *testing.T) { } } -func TestEmbedNotice(t *testing.T) { - result := android.GroupFixturePreparers( - PrepareForTestWithJavaDefaultModules, - cc.PrepareForTestWithCcDefaultModules, - genrule.PrepareForTestWithGenRuleBuildComponents, - android.MockFS{ - "APP_NOTICE": nil, - "GENRULE_NOTICE": nil, - "LIB_NOTICE": nil, - "TOOL_NOTICE": nil, - }.AddToFixture(), - ).RunTestWithBp(t, ` - android_app { - name: "foo", - srcs: ["a.java"], - static_libs: ["javalib"], - jni_libs: ["libjni"], - notice: "APP_NOTICE", - embed_notices: true, - sdk_version: "current", - } - - // No embed_notice flag - android_app { - name: "bar", - srcs: ["a.java"], - jni_libs: ["libjni"], - notice: "APP_NOTICE", - sdk_version: "current", - } - - // No NOTICE files - android_app { - name: "baz", - srcs: ["a.java"], - embed_notices: true, - sdk_version: "current", - } - - cc_library { - name: "libjni", - system_shared_libs: [], - stl: "none", - notice: "LIB_NOTICE", - sdk_version: "current", - } - - java_library { - name: "javalib", - srcs: [ - ":gen", - ], - sdk_version: "current", - } - - genrule { - name: "gen", - tools: ["gentool"], - out: ["gen.java"], - notice: "GENRULE_NOTICE", - } - - java_binary_host { - name: "gentool", - srcs: ["b.java"], - notice: "TOOL_NOTICE", - } - `) - - // foo has NOTICE files to process, and embed_notices is true. - foo := result.ModuleForTests("foo", "android_common") - // verify merge notices rule. - mergeNotices := foo.Rule("mergeNoticesRule") - noticeInputs := mergeNotices.Inputs.Strings() - // TOOL_NOTICE should be excluded as it's a host module. - if len(mergeNotices.Inputs) != 3 { - t.Errorf("number of input notice files: expected = 3, actual = %q", noticeInputs) - } - if !inList("APP_NOTICE", noticeInputs) { - t.Errorf("APP_NOTICE is missing from notice files, %q", noticeInputs) - } - if !inList("LIB_NOTICE", noticeInputs) { - t.Errorf("LIB_NOTICE is missing from notice files, %q", noticeInputs) - } - if !inList("GENRULE_NOTICE", noticeInputs) { - t.Errorf("GENRULE_NOTICE is missing from notice files, %q", noticeInputs) - } - // aapt2 flags should include -A <NOTICE dir> so that its contents are put in the APK's /assets. - res := foo.Output("package-res.apk") - aapt2Flags := res.Args["flags"] - e := "-A out/soong/.intermediates/foo/android_common/NOTICE" - android.AssertStringDoesContain(t, "expected.apkPath", aapt2Flags, e) - - // bar has NOTICE files to process, but embed_notices is not set. - bar := result.ModuleForTests("bar", "android_common") - res = bar.Output("package-res.apk") - aapt2Flags = res.Args["flags"] - e = "-A out/soong/.intermediates/bar/android_common/NOTICE" - android.AssertStringDoesNotContain(t, "bar shouldn't have the asset dir flag for NOTICE", aapt2Flags, e) - - // baz's embed_notice is true, but it doesn't have any NOTICE files. - baz := result.ModuleForTests("baz", "android_common") - res = baz.Output("package-res.apk") - aapt2Flags = res.Args["flags"] - e = "-A out/soong/.intermediates/baz/android_common/NOTICE" - if strings.Contains(aapt2Flags, e) { - t.Errorf("baz shouldn't have the asset dir flag for NOTICE: %q", e) - } -} - func TestUncompressDex(t *testing.T) { testCases := []struct { name string @@ -2823,3 +2975,97 @@ func TestExportedProguardFlagFiles(t *testing.T) { t.Errorf("App does not use library proguard config") } } + +func TestTargetSdkVersionManifestFixer(t *testing.T) { + platform_sdk_codename := "Tiramisu" + testCases := []struct { + name string + targetSdkVersionInBp string + targetSdkVersionExpected string + unbundledBuild bool + }{ + { + name: "Non-Unbundled build: Android.bp has targetSdkVersion", + targetSdkVersionInBp: "30", + targetSdkVersionExpected: "30", + unbundledBuild: false, + }, + { + name: "Unbundled build: Android.bp has targetSdkVersion", + targetSdkVersionInBp: "30", + targetSdkVersionExpected: "30", + unbundledBuild: true, + }, + { + name: "Non-Unbundled build: Android.bp has targetSdkVersion equal to platform_sdk_codename", + targetSdkVersionInBp: platform_sdk_codename, + targetSdkVersionExpected: platform_sdk_codename, + unbundledBuild: false, + }, + { + name: "Unbundled build: Android.bp has targetSdkVersion equal to platform_sdk_codename", + targetSdkVersionInBp: platform_sdk_codename, + targetSdkVersionExpected: "10000", + unbundledBuild: true, + }, + + { + name: "Non-Unbundled build: Android.bp has no targetSdkVersion", + targetSdkVersionExpected: platform_sdk_codename, + unbundledBuild: false, + }, + { + name: "Unbundled build: Android.bp has no targetSdkVersion", + targetSdkVersionExpected: "10000", + unbundledBuild: true, + }, + } + for _, testCase := range testCases { + bp := fmt.Sprintf(` + android_app { + name: "foo", + sdk_version: "current", + target_sdk_version: "%v", + } + `, testCase.targetSdkVersionInBp) + fixture := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + // explicitly set platform_sdk_codename to make the test deterministic + variables.Platform_sdk_codename = &platform_sdk_codename + variables.Platform_version_active_codenames = []string{platform_sdk_codename} + // create a non-empty list if unbundledBuild==true + if testCase.unbundledBuild { + variables.Unbundled_build_apps = []string{"apex_a", "apex_b"} + } + }), + ) + + result := fixture.RunTestWithBp(t, bp) + foo := result.ModuleForTests("foo", "android_common") + + manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"] + android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion "+testCase.targetSdkVersionExpected) + } +} + +func TestAppMissingCertificateAllowMissingDependencies(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + android.PrepareForTestWithAllowMissingDependencies, + android.PrepareForTestWithAndroidMk, + ).RunTestWithBp(t, ` + android_app { + name: "foo", + srcs: ["a.java"], + certificate: ":missing_certificate", + sdk_version: "current", + }`) + + foo := result.ModuleForTests("foo", "android_common") + fooApk := foo.Output("foo.apk") + if fooApk.Rule != android.ErrorRule { + t.Fatalf("expected ErrorRule for foo.apk, got %s", fooApk.Rule.String()) + } + android.AssertStringDoesContain(t, "expected error rule message", fooApk.Args["error"], "missing dependencies: missing_certificate\n") +} diff --git a/java/base.go b/java/base.go index df70efb44..7aa281495 100644 --- a/java/base.go +++ b/java/base.go @@ -155,6 +155,13 @@ type CommonProperties struct { // List of java_plugin modules that provide extra errorprone checks. Extra_check_modules []string + + // This property can be in 3 states. When set to true, errorprone will + // be run during the regular build. When set to false, errorprone will + // never be run. When unset, errorprone will be run when the RUN_ERROR_PRONE + // environment variable is true. Setting this to false will improve build + // performance more than adding -XepDisableAllChecks in javacflags. + Enabled *bool } Proto struct { @@ -163,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"` @@ -177,21 +187,30 @@ type CommonProperties struct { // Properties that are specific to device modules. Host module factories should not add these when // constructing a new module. type DeviceProperties struct { - // if not blank, set to the version of the sdk to compile against. - // Defaults to compiling against the current platform. + // If not blank, set to the version of the sdk to compile against. + // Defaults to private. + // Values are of one of the following forms: + // 1) numerical API level, "current", "none", or "core_platform" + // 2) An SDK kind with an API level: "<sdk kind>_<API level>" + // See build/soong/android/sdk_version.go for the complete and up to date list of SDK kinds. + // If the SDK kind is empty, it will be set to public. Sdk_version *string // if not blank, set the minimum version of the sdk that the compiled artifacts will run against. - // Defaults to sdk_version if not set. + // Defaults to sdk_version if not set. See sdk_version for possible values. Min_sdk_version *string + // if not blank, set the maximum version of the sdk that the compiled artifacts will run against. + // Defaults to empty string "". See sdk_version for possible values. + Max_sdk_version *string + // if not blank, set the targetSdkVersion in the AndroidManifest.xml. - // Defaults to sdk_version if not set. + // Defaults to sdk_version if not set. See sdk_version for possible values. Target_sdk_version *string // Whether to compile against the platform APIs instead of an SDK. // If true, then sdk_version must be empty. The value of this field - // is ignored when module's type isn't android_app. + // is ignored when module's type isn't android_app, android_test, or android_test_helper_app. Platform_apis *bool Aidl struct { @@ -211,6 +230,12 @@ type DeviceProperties struct { // whether to generate Binder#GetTransaction name method. Generate_get_transaction_name *bool + // whether all interfaces should be annotated with required permissions. + Enforce_permissions *bool + + // allowlist for interfaces that (temporarily) do not require annotation for permissions. + Enforce_permissions_exceptions []string `android:"path"` + // list of flags that will be passed to the AIDL compiler Flags []string } @@ -229,9 +254,6 @@ type DeviceProperties struct { // otherwise provides defaults libraries to add to the bootclasspath. System_modules *string - // set the name of the output - Stem *string - IsSDKLibrary bool `blueprint:"mutated"` // If true, generate the signature file of APK Signing Scheme V4, along side the signed APK file. @@ -243,6 +265,15 @@ type DeviceProperties struct { SyspropPublicStub string `blueprint:"mutated"` } +// Device properties that can be overridden by overriding module (e.g. override_android_app) +type OverridableDeviceProperties struct { + // set the name of the output. If not set, `name` is used. + // To override a module with this property set, overriding module might need to set this as well. + // Otherwise, both the overridden and the overriding modules will have the same output name, which + // can cause the duplicate output error. + Stem *string +} + // Functionality common to Module and Import // // It is embedded in Module so its functionality can be used by methods in Module @@ -269,12 +300,94 @@ func (e *embeddableInModuleAndImport) depIsInSameApex(ctx android.BaseModuleCont return false } +// OptionalDexJarPath can be either unset, hold a valid path to a dex jar file, +// or an invalid path describing the reason it is invalid. +// +// It is unset if a dex jar isn't applicable, i.e. no build rule has been +// requested to create one. +// +// If a dex jar has been requested to be built then it is set, and it may be +// either a valid android.Path, or invalid with a reason message. The latter +// happens if the source that should produce the dex file isn't able to. +// +// E.g. it is invalid with a reason message if there is a prebuilt APEX that +// could produce the dex jar through a deapexer module, but the APEX isn't +// installable so doing so wouldn't be safe. +type OptionalDexJarPath struct { + isSet bool + path android.OptionalPath +} + +// IsSet returns true if a path has been set, either invalid or valid. +func (o OptionalDexJarPath) IsSet() bool { + return o.isSet +} + +// Valid returns true if there is a path that is valid. +func (o OptionalDexJarPath) Valid() bool { + return o.isSet && o.path.Valid() +} + +// Path returns the valid path, or panics if it's either not set or is invalid. +func (o OptionalDexJarPath) Path() android.Path { + if !o.isSet { + panic("path isn't set") + } + return o.path.Path() +} + +// PathOrNil returns the path if it's set and valid, or else nil. +func (o OptionalDexJarPath) PathOrNil() android.Path { + if o.Valid() { + return o.Path() + } + return nil +} + +// InvalidReason returns the reason for an invalid path, which is never "". It +// returns "" for an unset or valid path. +func (o OptionalDexJarPath) InvalidReason() string { + if !o.isSet { + return "" + } + return o.path.InvalidReason() +} + +func (o OptionalDexJarPath) String() string { + if !o.isSet { + return "<unset>" + } + return o.path.String() +} + +// makeUnsetDexJarPath returns an unset OptionalDexJarPath. +func makeUnsetDexJarPath() OptionalDexJarPath { + return OptionalDexJarPath{isSet: false} +} + +// makeDexJarPathFromOptionalPath returns an OptionalDexJarPath that is set with +// the given OptionalPath, which may be valid or invalid. +func makeDexJarPathFromOptionalPath(path android.OptionalPath) OptionalDexJarPath { + return OptionalDexJarPath{isSet: true, path: path} +} + +// makeDexJarPathFromPath returns an OptionalDexJarPath that is set with the +// valid given path. It returns an unset OptionalDexJarPath if the given path is +// nil. +func makeDexJarPathFromPath(path android.Path) OptionalDexJarPath { + if path == nil { + return makeUnsetDexJarPath() + } + return makeDexJarPathFromOptionalPath(android.OptionalPathForPath(path)) +} + // Module contains the properties and members used by all java module types type Module struct { android.ModuleBase android.DefaultableModuleBase android.ApexModuleBase android.SdkBase + android.BazelModuleBase // Functionality common to Module and Import. embeddableInModuleAndImport @@ -283,6 +396,8 @@ type Module struct { protoProperties android.ProtoProperties deviceProperties DeviceProperties + overridableDeviceProperties OverridableDeviceProperties + // jar file containing header classes including static library dependencies, suitable for // inserting into the bootclasspath/classpath of another compile headerJarFile android.Path @@ -303,7 +418,7 @@ type Module struct { implementationAndResourcesJar android.Path // output file containing classes.dex and resources - dexJarFile android.Path + dexJarFile OptionalDexJarPath // output file containing uninstrumented classes that will be instrumented by jacoco jacocoReportClassesFile android.Path @@ -312,13 +427,17 @@ type Module struct { outputFile android.Path extraOutputFiles android.Paths - exportAidlIncludeDirs android.Paths + exportAidlIncludeDirs android.Paths + ignoredAidlPermissionList android.Paths logtagsSrcs android.Paths // installed file for binary dependency installFile android.Path + // installed file for hostdex copy + hostdexInstallFile android.InstallPath + // list of .java files and srcjars that was passed to javac compiledJavaSrcs android.Paths compiledSrcJars android.Paths @@ -345,9 +464,6 @@ type Module struct { // expanded Jarjar_rules expandJarjarRules android.Path - // list of additional targets for checkbuild - additionalCheckedModules android.Paths - // Extra files generated by the module type to be added as java resources. extraResources android.Paths @@ -367,6 +483,9 @@ type Module struct { sdkVersion android.SdkSpec minSdkVersion android.SdkSpec + maxSdkVersion android.SdkSpec + + sourceExtensions []string } func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error { @@ -375,7 +494,7 @@ func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error { return nil } if sdkVersion.Kind == android.SdkCorePlatform { - if useLegacyCorePlatformApiByName(j.BaseModuleName()) { + if useLegacyCorePlatformApi(ctx, j.BaseModuleName()) { return fmt.Errorf("non stable SDK %v - uses legacy core platform", sdkVersion) } else { // Treat stable core platform as stable. @@ -417,9 +536,9 @@ func (j *Module) checkPlatformAPI(ctx android.ModuleContext) { usePlatformAPI := proptools.Bool(j.deviceProperties.Platform_apis) sdkVersionSpecified := sc.SdkVersion(ctx).Specified() if usePlatformAPI && sdkVersionSpecified { - ctx.PropertyErrorf("platform_apis", "platform_apis must be false when sdk_version is not empty.") + ctx.PropertyErrorf("platform_apis", "This module has conflicting settings. sdk_version is not empty, which means this module cannot use platform APIs. However platform_apis is set to true.") } else if !usePlatformAPI && !sdkVersionSpecified { - ctx.PropertyErrorf("platform_apis", "platform_apis must be true when sdk_version is empty.") + ctx.PropertyErrorf("platform_apis", "This module has conflicting settings. sdk_version is empty, which means that this module is build against platform APIs. However platform_apis is not set to true") } } @@ -437,6 +556,7 @@ func (j *Module) addHostAndDeviceProperties() { j.addHostProperties() j.AddProperties( &j.deviceProperties, + &j.overridableDeviceProperties, &j.dexer.dexProperties, &j.dexpreoptProperties, &j.linter.properties, @@ -488,7 +608,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()) } @@ -524,6 +645,13 @@ func (j *Module) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec { return j.SdkVersion(ctx) } +func (j *Module) MaxSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec { + maxSdkVersion := proptools.StringDefault(j.deviceProperties.Max_sdk_version, "") + // SdkSpecFrom returns SdkSpecPrivate for this, which may be confusing. + // TODO(b/208456999): ideally MaxSdkVersion should be an ApiLevel and not SdkSpec. + return android.SdkSpecFrom(ctx, maxSdkVersion) +} + func (j *Module) MinSdkVersionString() string { return j.minSdkVersion.Raw } @@ -598,7 +726,10 @@ func (j *Module) deps(ctx android.BottomUpMutatorContext) { if dep != nil { if component, ok := dep.(SdkLibraryComponentDependency); ok { if lib := component.OptionalSdkLibraryImplementation(); lib != nil { - ctx.AddVariationDependencies(nil, usesLibTag, *lib) + // 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) + ctx.AddVariationDependencies(nil, tag, *lib) } } } @@ -633,6 +764,11 @@ func (j *Module) deps(ctx android.BottomUpMutatorContext) { } else if j.shouldInstrumentStatic(ctx) { ctx.AddVariationDependencies(nil, staticLibTag, "jacocoagent") } + + if j.useCompose() { + ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), kotlinPluginTag, + "androidx.compose.compiler_compiler-hosted") + } } func hasSrcExt(srcs []string, ext string) bool { @@ -649,6 +785,17 @@ func (j *Module) hasSrcExt(ext string) bool { return hasSrcExt(j.properties.Srcs, ext) } +func (j *Module) individualAidlFlags(ctx android.ModuleContext, aidlFile android.Path) string { + var flags string + + if Bool(j.deviceProperties.Aidl.Enforce_permissions) { + if !android.InList(aidlFile.String(), j.ignoredAidlPermissionList.Strings()) { + flags = "-Wmissing-permission-annotation -Werror" + } + } + return flags +} + func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.OptionalPath, aidlIncludeDirs android.Paths) (string, android.Paths) { @@ -691,6 +838,14 @@ func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.Opt flags = append(flags, "--transaction_names") } + if Bool(j.deviceProperties.Aidl.Enforce_permissions) { + exceptions := j.deviceProperties.Aidl.Enforce_permissions_exceptions + j.ignoredAidlPermissionList = android.PathsForModuleSrcExcludes(ctx, exceptions, nil) + } + + aidlMinSdkVersion := j.MinSdkVersion(ctx).ApiLevel.String() + flags = append(flags, "--min_sdk_version="+aidlMinSdkVersion) + return strings.Join(flags, " "), deps } @@ -701,7 +856,8 @@ func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaB // javaVersion flag. flags.javaVersion = getJavaVersion(ctx, String(j.properties.Java_version), android.SdkContext(j)) - if ctx.Config().RunErrorProne() { + epEnabled := j.properties.Errorprone.Enabled + if (ctx.Config().RunErrorProne() && epEnabled == nil) || Bool(epEnabled) { if config.ErrorProneClasspath == nil && ctx.Config().TestProductVariables == nil { ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?") } @@ -712,7 +868,7 @@ func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaB } errorProneFlags = append(errorProneFlags, j.properties.Errorprone.Javacflags...) - flags.errorProneExtraJavacFlags = "${config.ErrorProneFlags} " + + flags.errorProneExtraJavacFlags = "${config.ErrorProneHeapFlags} ${config.ErrorProneFlags} " + "'" + strings.Join(errorProneFlags, " ") + "'" flags.errorProneProcessorPath = classpath(android.PathsForSource(ctx, config.ErrorProneClasspath)) } @@ -720,6 +876,7 @@ func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaB // classpath flags.bootClasspath = append(flags.bootClasspath, deps.bootClasspath...) flags.classpath = append(flags.classpath, deps.classpath...) + flags.dexClasspath = append(flags.dexClasspath, deps.dexClasspath...) flags.java9Classpath = append(flags.java9Classpath, deps.java9Classpath...) flags.processorPath = append(flags.processorPath, deps.processorPath...) flags.errorProneProcessorPath = append(flags.errorProneProcessorPath, deps.errorProneProcessorPath...) @@ -780,7 +937,7 @@ func (j *Module) collectJavacFlags( // Manually specify build directory in case it is not under the repo root. // (javac doesn't seem to expand into symbolic links when searching for patch-module targets, so // just adding a symlink under the root doesn't help.) - patchPaths := []string{".", ctx.Config().BuildDir()} + patchPaths := []string{".", ctx.Config().SoongOutDir()} // b/150878007 // @@ -832,6 +989,14 @@ func (j *Module) collectJavacFlags( return flags } +func (j *Module) AddJSONData(d *map[string]interface{}) { + (&j.ModuleBase).AddJSONData(d) + (*d)["Java"] = map[string]interface{}{ + "SourceExtensions": j.sourceExtensions, + } + +} + func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs) @@ -841,7 +1006,14 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { if flags.javaVersion.usesJavaModules() { j.properties.Srcs = append(j.properties.Srcs, j.properties.Openjdk9.Srcs...) } + srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs) + j.sourceExtensions = []string{} + for _, ext := range []string{".kt", ".proto", ".aidl", ".java", ".logtags"} { + if hasSrcExt(srcFiles.Strings(), ext) { + j.sourceExtensions = append(j.sourceExtensions, ext) + } + } if hasSrcExt(srcFiles.Strings(), ".proto") { flags = protoFlags(ctx, &j.properties, &j.protoProperties, flags) } @@ -862,6 +1034,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { if aaptSrcJar != nil { srcJars = append(srcJars, aaptSrcJar) } + srcFiles = srcFiles.FilterOutByExt(".srcjar") if j.properties.Jarjar_rules != nil { j.expandJarjarRules = android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules) @@ -879,18 +1052,30 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { } } + // We don't currently run annotation processors in turbine, which means we can't use turbine + // generated header jars when an annotation processor that generates API is enabled. One + // exception (handled further below) is when kotlin sources are enabled, in which case turbine + // is used to run all of the annotation processors. + disableTurbine := deps.disableTurbine + // Collect .java files for AIDEGen j.expandIDEInfoCompiledSrcs = append(j.expandIDEInfoCompiledSrcs, uniqueSrcFiles.Strings()...) var kotlinJars android.Paths + var kotlinHeaderJars android.Paths if srcFiles.HasExt(".kt") { + // When using kotlin sources turbine is used to generate annotation processor sources, + // including for annotation processors that generate API, so we can use turbine for + // java sources too. + disableTurbine = false + // user defined kotlin flags. kotlincFlags := j.properties.Kotlincflags CheckKotlincFlags(ctx, kotlincFlags) - // Dogfood the JVM_IR backend. - kotlincFlags = append(kotlincFlags, "-Xuse-ir") + // Workaround for KT-46512 + kotlincFlags = append(kotlincFlags, "-Xsam-conversions=class") // If there are kotlin files, compile them first but pass all the kotlin and java files // kotlinc will use the java files to resolve types referenced by the kotlin files, but @@ -899,6 +1084,12 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { if ctx.Device() { kotlincFlags = append(kotlincFlags, "-no-jdk") } + + for _, plugin := range deps.kotlinPlugins { + kotlincFlags = append(kotlincFlags, "-Xplugin="+plugin.String()) + } + flags.kotlincDeps = append(flags.kotlincDeps, deps.kotlinPlugins...) + if len(kotlincFlags) > 0 { // optimization. ctx.Variable(pctx, "kotlincFlags", strings.Join(kotlincFlags, " ")) @@ -916,6 +1107,8 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { flags.classpath = append(flags.classpath, deps.kotlinStdlib...) flags.classpath = append(flags.classpath, deps.kotlinAnnotations...) + flags.dexClasspath = append(flags.dexClasspath, deps.kotlinAnnotations...) + flags.kotlincClasspath = append(flags.kotlincClasspath, flags.bootClasspath...) flags.kotlincClasspath = append(flags.kotlincClasspath, flags.classpath...) @@ -932,18 +1125,24 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { } kotlinJar := android.PathForModuleOut(ctx, "kotlin", jarName) - kotlinCompile(ctx, kotlinJar, kotlinSrcFiles, kotlinCommonSrcFiles, srcJars, flags) + kotlinHeaderJar := android.PathForModuleOut(ctx, "kotlin_headers", jarName) + kotlinCompile(ctx, kotlinJar, kotlinHeaderJar, kotlinSrcFiles, kotlinCommonSrcFiles, srcJars, flags) if ctx.Failed() { return } // Make javac rule depend on the kotlinc rule - flags.classpath = append(flags.classpath, kotlinJar) + flags.classpath = append(classpath{kotlinHeaderJar}, flags.classpath...) kotlinJars = append(kotlinJars, kotlinJar) + kotlinHeaderJars = append(kotlinHeaderJars, kotlinHeaderJar) + // Jar kotlin classes into the final jar after javac if BoolDefault(j.properties.Static_kotlin_stdlib, true) { kotlinJars = append(kotlinJars, deps.kotlinStdlib...) + kotlinHeaderJars = append(kotlinHeaderJars, deps.kotlinStdlib...) + } else { + flags.dexClasspath = append(flags.dexClasspath, deps.kotlinStdlib...) } } @@ -954,8 +1153,8 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { j.compiledSrcJars = srcJars enableSharding := false - var headerJarFileWithoutJarjar android.Path - if ctx.Device() && !ctx.Config().IsEnvFalse("TURBINE_ENABLED") && !deps.disableTurbine { + var headerJarFileWithoutDepsOrJarjar android.Path + if ctx.Device() && !ctx.Config().IsEnvFalse("TURBINE_ENABLED") && !disableTurbine { if j.properties.Javac_shard_size != nil && *(j.properties.Javac_shard_size) > 0 { enableSharding = true // Formerly, there was a check here that prevented annotation processors @@ -964,27 +1163,38 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { // allow for the use of annotation processors that do function correctly // with sharding enabled. See: b/77284273. } - headerJarFileWithoutJarjar, j.headerJarFile = - j.compileJavaHeader(ctx, uniqueSrcFiles, srcJars, deps, flags, jarName, kotlinJars) + headerJarFileWithoutDepsOrJarjar, j.headerJarFile = + j.compileJavaHeader(ctx, uniqueSrcFiles, srcJars, deps, flags, jarName, kotlinHeaderJars) if ctx.Failed() { return } } if len(uniqueSrcFiles) > 0 || len(srcJars) > 0 { var extraJarDeps android.Paths - if ctx.Config().RunErrorProne() { - // If error-prone is enabled, add an additional rule to compile the java files into - // a separate set of classes (so that they don't overwrite the normal ones and require - // a rebuild when error-prone is turned off). - // TODO(ccross): Once we always compile with javac9 we may be able to conditionally - // enable error-prone without affecting the output class files. + if Bool(j.properties.Errorprone.Enabled) { + // If error-prone is enabled, enable errorprone flags on the regular + // build. + flags = enableErrorproneFlags(flags) + } else if ctx.Config().RunErrorProne() && j.properties.Errorprone.Enabled == nil { + // Otherwise, if the RUN_ERROR_PRONE environment variable is set, create + // a new jar file just for compiling with the errorprone compiler to. + // This is because we don't want to cause the java files to get completely + // rebuilt every time the state of the RUN_ERROR_PRONE variable changes. + // We also don't want to run this if errorprone is enabled by default for + // this module, or else we could have duplicated errorprone messages. + errorproneFlags := enableErrorproneFlags(flags) errorprone := android.PathForModuleOut(ctx, "errorprone", jarName) - RunErrorProne(ctx, errorprone, uniqueSrcFiles, srcJars, flags) + + transformJavaToClasses(ctx, errorprone, -1, uniqueSrcFiles, srcJars, errorproneFlags, nil, + "errorprone", "errorprone") + extraJarDeps = append(extraJarDeps, errorprone) } if enableSharding { - flags.classpath = append(flags.classpath, headerJarFileWithoutJarjar) + if headerJarFileWithoutDepsOrJarjar != nil { + flags.classpath = append(classpath{headerJarFileWithoutDepsOrJarjar}, flags.classpath...) + } shardSize := int(*(j.properties.Javac_shard_size)) var shardSrcs []android.Paths if len(uniqueSrcFiles) > 0 { @@ -1152,10 +1362,25 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { // Check package restrictions if necessary. if len(j.properties.Permitted_packages) > 0 { - // Check packages and copy to package-checked file. + // Time stamp file created by the package check rule. pkgckFile := android.PathForModuleOut(ctx, "package-check.stamp") + + // Create a rule to copy the output jar to another path and add a validate dependency that + // will check that the jar only contains the permitted packages. The new location will become + // the output file of this module. + inputFile := outputFile + outputFile = android.PathForModuleOut(ctx, "package-check", jarName).OutputPath + ctx.Build(pctx, android.BuildParams{ + Rule: android.Cp, + Input: inputFile, + Output: outputFile, + // Make sure that any dependency on the output file will cause ninja to run the package check + // rule. + Validation: pkgckFile, + }) + + // Check packages and create a timestamp file when complete. CheckJarPackages(ctx, pkgckFile, outputFile, j.properties.Permitted_packages) - j.additionalCheckedModules = append(j.additionalCheckedModules, pkgckFile) if ctx.Failed() { return @@ -1212,7 +1437,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { } // Dex compilation var dexOutputFile android.OutputPath - dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), outputFile, jarName) + dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), implementationAndResourcesJar, jarName) if ctx.Failed() { return } @@ -1233,12 +1458,13 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { } // Initialize the hiddenapi structure. - j.initHiddenAPI(ctx, dexOutputFile, j.implementationJarFile, j.dexProperties.Uncompress_dex) + + j.initHiddenAPI(ctx, makeDexJarPathFromPath(dexOutputFile), j.implementationJarFile, j.dexProperties.Uncompress_dex) // Encode hidden API flags in dex file, if needed. dexOutputFile = j.hiddenAPIEncodeDex(ctx, dexOutputFile) - j.dexJarFile = dexOutputFile + j.dexJarFile = makeDexJarPathFromPath(dexOutputFile) // Dexpreopting j.dexpreopt(ctx, dexOutputFile) @@ -1248,7 +1474,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { // There is no code to compile into a dex jar, make sure the resources are propagated // to the APK if this is an app. outputFile = implementationAndResourcesJar - j.dexJarFile = j.resourceJar + j.dexJarFile = makeDexJarPathFromPath(j.resourceJar) } if ctx.Failed() { @@ -1259,11 +1485,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) } } @@ -1272,9 +1498,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" @@ -1304,6 +1530,25 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { j.outputFile = outputFile.WithoutRel() } +func (j *Module) useCompose() bool { + return android.InList("androidx.compose.runtime_runtime", j.properties.Static_libs) +} + +// Returns a copy of the supplied flags, but with all the errorprone-related +// fields copied to the regular build's fields. +func enableErrorproneFlags(flags javaBuilderFlags) javaBuilderFlags { + flags.processorPath = append(flags.errorProneProcessorPath, flags.processorPath...) + + if len(flags.errorProneExtraJavacFlags) > 0 { + if len(flags.javacFlags) > 0 { + flags.javacFlags += " " + flags.errorProneExtraJavacFlags + } else { + flags.javacFlags = flags.errorProneExtraJavacFlags + } + } + return flags +} + func (j *Module) compileJavaClasses(ctx android.ModuleContext, jarName string, idx int, srcFiles, srcJars android.Paths, flags javaBuilderFlags, extraJarDeps android.Paths) android.WritablePath { @@ -1352,7 +1597,7 @@ func CheckKotlincFlags(ctx android.ModuleContext, flags []string) { func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars android.Paths, deps deps, flags javaBuilderFlags, jarName string, - extraJars android.Paths) (headerJar, jarjarHeaderJar android.Path) { + extraJars android.Paths) (headerJar, jarjarAndDepsHeaderJar android.Path) { var jars android.Paths if len(srcFiles) > 0 || len(srcJars) > 0 { @@ -1363,6 +1608,7 @@ func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars return nil, nil } jars = append(jars, turbineJar) + headerJar = turbineJar } jars = append(jars, extraJars...) @@ -1376,20 +1622,19 @@ func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars combinedJar := android.PathForModuleOut(ctx, "turbine-combined", jarName) TransformJarsToJar(ctx, combinedJar, "for turbine", jars, android.OptionalPath{}, false, nil, []string{"META-INF/TRANSITIVE"}) - headerJar = combinedJar - jarjarHeaderJar = combinedJar + jarjarAndDepsHeaderJar = combinedJar if j.expandJarjarRules != nil { // Transform classes.jar into classes-jarjar.jar jarjarFile := android.PathForModuleOut(ctx, "turbine-jarjar", jarName) - TransformJarJar(ctx, jarjarFile, headerJar, j.expandJarjarRules) - jarjarHeaderJar = jarjarFile + TransformJarJar(ctx, jarjarFile, jarjarAndDepsHeaderJar, j.expandJarjarRules) + jarjarAndDepsHeaderJar = jarjarFile if ctx.Failed() { return nil, nil } } - return headerJar, jarjarHeaderJar + return headerJar, jarjarAndDepsHeaderJar } func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags, @@ -1419,7 +1664,7 @@ func (j *Module) ImplementationJars() android.Paths { return android.Paths{j.implementationJarFile} } -func (j *Module) DexJarBuildPath() android.Path { +func (j *Module) DexJarBuildPath() OptionalDexJarPath { return j.dexJarFile } @@ -1453,6 +1698,8 @@ func (j *Module) IDEInfo(dpInfo *android.IdeInfo) { dpInfo.Jarjar_rules = append(dpInfo.Jarjar_rules, j.expandJarjarRules.String()) } dpInfo.Paths = append(dpInfo.Paths, j.modulePaths...) + dpInfo.Static_libs = append(dpInfo.Static_libs, j.properties.Static_libs...) + dpInfo.Libs = append(dpInfo.Libs, j.properties.Libs...) } func (j *Module) CompilerDeps() []string { @@ -1473,8 +1720,7 @@ func (j *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Modu } // Implements android.ApexModule -func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, - sdkVersion android.ApiLevel) error { +func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error { sdkSpec := j.MinSdkVersion(ctx) if !sdkSpec.Specified() { return fmt.Errorf("min_sdk_version is not specified") @@ -1482,18 +1728,14 @@ func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, if sdkSpec.Kind == android.SdkCore { return nil } - ver, err := sdkSpec.EffectiveVersion(ctx) - if err != nil { - return err - } - if ver.GreaterThan(sdkVersion) { - return fmt.Errorf("newer SDK(%v)", ver) + if sdkSpec.ApiLevel.GreaterThan(sdkVersion) { + return fmt.Errorf("newer SDK(%v)", sdkSpec.ApiLevel) } return nil } func (j *Module) Stem() string { - return proptools.StringDefault(j.deviceProperties.Stem, j.Name()) + return proptools.StringDefault(j.overridableDeviceProperties.Stem, j.Name()) } func (j *Module) JacocoReportClassesFile() android.Path { @@ -1627,6 +1869,7 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { } else if sdkDep.useFiles { // sdkDep.jar is actually equivalent to turbine header.jar. deps.classpath = append(deps.classpath, sdkDep.jars...) + deps.dexClasspath = append(deps.dexClasspath, sdkDep.jars...) deps.aidlPreprocess = sdkDep.aidl } else { deps.aidlPreprocess = sdkDep.aidl @@ -1651,7 +1894,9 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { if dep, ok := module.(SdkLibraryDependency); ok { switch tag { case libTag: - deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...) + depHeaderJars := dep.SdkHeaderJars(ctx, j.SdkVersion(ctx)) + deps.classpath = append(deps.classpath, depHeaderJars...) + deps.dexClasspath = append(deps.dexClasspath, depHeaderJars...) case staticLibTag: ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName) } @@ -1670,6 +1915,7 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars...) case libTag, instrumentationForTag: deps.classpath = append(deps.classpath, dep.HeaderJars...) + deps.dexClasspath = append(deps.dexClasspath, dep.HeaderJars...) deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...) addPlugins(&deps, dep.ExportedPlugins, dep.ExportedPluginClasses...) deps.disableTurbine = deps.disableTurbine || dep.ExportedPluginDisableTurbine @@ -1723,6 +1969,8 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { deps.kotlinStdlib = append(deps.kotlinStdlib, dep.HeaderJars...) case kotlinAnnotationsTag: deps.kotlinAnnotations = dep.HeaderJars + case kotlinPluginTag: + deps.kotlinPlugins = append(deps.kotlinPlugins, dep.ImplementationAndResourcesJars...) case syspropPublicStubDepTag: // This is a sysprop implementation library, forward the JavaInfoProvider from // the corresponding sysprop public stub library as SyspropPublicStubInfoProvider. @@ -1735,6 +1983,7 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { case libTag: checkProducesJars(ctx, dep) deps.classpath = append(deps.classpath, dep.Srcs()...) + deps.dexClasspath = append(deps.classpath, dep.Srcs()...) case staticLibTag: checkProducesJars(ctx, dep) deps.classpath = append(deps.classpath, dep.Srcs()...) @@ -1756,6 +2005,9 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { sm := module.(SystemModulesProvider) outputDir, outputDeps := sm.OutputDirAndDeps() deps.systemModules = &systemModules{outputDir, outputDeps} + + case instrumentationForTag: + ctx.PropertyErrorf("instrumentation_for", "dependency %q of type %q does not provide JavaInfo so is unsuitable for use with this property", ctx.OtherModuleName(module), ctx.OtherModuleType(module)) } } @@ -1785,3 +2037,16 @@ type ModuleWithStem interface { } var _ ModuleWithStem = (*Module)(nil) + +func (j *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) { + switch ctx.ModuleType() { + case "java_library", "java_library_host", "java_library_static": + if lib, ok := ctx.Module().(*Library); ok { + javaLibraryBp2Build(ctx, lib) + } + case "java_binary_host": + if binary, ok := ctx.Module().(*Binary); ok { + javaBinaryHostBp2Build(ctx, binary) + } + } +} diff --git a/java/boot_jars.go b/java/boot_jars.go index 86ebe36b4..5d40ec389 100644 --- a/java/boot_jars.go +++ b/java/boot_jars.go @@ -31,13 +31,18 @@ func isActiveModule(module android.Module) bool { // buildRuleForBootJarsPackageCheck generates the build rule to perform the boot jars package // check. func buildRuleForBootJarsPackageCheck(ctx android.ModuleContext, bootDexJarByModule bootDexJarByModule) { + bootDexJars := bootDexJarByModule.bootDexJarsWithoutCoverage() + if len(bootDexJars) == 0 { + return + } + timestamp := android.PathForOutput(ctx, "boot-jars-package-check/stamp") rule := android.NewRuleBuilder(pctx, ctx) rule.Command().BuiltTool("check_boot_jars"). Input(ctx.Config().HostToolPath(ctx, "dexdump")). Input(android.PathForSource(ctx, "build/soong/scripts/check_boot_jars/package_allowed_list.txt")). - Inputs(bootDexJarByModule.bootDexJarsWithoutCoverage()). + Inputs(bootDexJars). Text("&& touch").Output(timestamp) rule.Build("boot_jars_package_check", "check boot jar packages") diff --git a/java/bootclasspath.go b/java/bootclasspath.go index d754fe6aa..52ce77d46 100644 --- a/java/bootclasspath.go +++ b/java/bootclasspath.go @@ -95,15 +95,6 @@ func addDependencyOntoApexModulePair(ctx android.BottomUpMutatorContext, apex st if ctx.OtherModuleDependencyVariantExists(variations, prebuiltName) { ctx.AddVariationDependencies(variations, tag, prebuiltName) addedDep = true - } else if ctx.Config().AlwaysUsePrebuiltSdks() && len(variations) > 0 { - // TODO(b/179354495): Remove this code path once the Android build has been fully migrated to - // use bootclasspath_fragment properly. - // Some prebuilt java_sdk_library modules do not yet have an APEX variations so try and add a - // dependency on the non-APEX variant. - if ctx.OtherModuleDependencyVariantExists(nil, prebuiltName) { - ctx.AddVariationDependencies(nil, tag, prebuiltName) - addedDep = true - } } // If no appropriate variant existing for this, so no dependency could be added, then it is an @@ -144,6 +135,8 @@ func gatherApexModulePairDepsWithTag(ctx android.BaseModuleContext, tag blueprin // ApexVariantReference specifies a particular apex variant of a module. type ApexVariantReference struct { + android.BpPrintableBase + // The name of the module apex variant, i.e. the apex containing the module variant. // // If this is not specified then it defaults to "platform" which will cause a dependency to be diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index a0399645f..96009351f 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -16,6 +16,7 @@ package java import ( "fmt" + "io" "path/filepath" "reflect" "strings" @@ -89,7 +90,7 @@ var bootclasspathFragmentContentDepTag = bootclasspathFragmentContentDependencyT var _ android.ExcludeFromVisibilityEnforcementTag = bootclasspathFragmentContentDepTag var _ android.ReplaceSourceWithPrebuilt = bootclasspathFragmentContentDepTag -var _ android.SdkMemberTypeDependencyTag = bootclasspathFragmentContentDepTag +var _ android.SdkMemberDependencyTag = bootclasspathFragmentContentDepTag var _ android.CopyDirectlyInAnyApexTag = bootclasspathFragmentContentDepTag var _ android.RequiresFilesFromPrebuiltApexTag = bootclasspathFragmentContentDepTag @@ -139,6 +140,87 @@ type bootclasspathFragmentProperties struct { BootclasspathFragmentsDepsProperties } +type HiddenApiPackageProperties struct { + Hidden_api struct { + // Contains prefixes of a package hierarchy that is provided solely by this + // bootclasspath_fragment. + // + // This affects the signature patterns file that is used to select the subset of monolithic + // hidden API flags. See split_packages property for more details. + Package_prefixes []string + + // A list of individual packages that are provided solely by this + // bootclasspath_fragment but which cannot be listed in package_prefixes + // because there are sub-packages which are provided by other modules. + // + // This should only be used for legacy packages. New packages should be + // covered by a package prefix. + Single_packages []string + + // The list of split packages provided by this bootclasspath_fragment. + // + // A split package is one that contains classes which are provided by multiple + // bootclasspath_fragment modules. + // + // This defaults to "*" - which treats all packages as being split. A module that has no split + // packages must specify an empty list. + // + // This affects the signature patterns file that is generated by a bootclasspath_fragment and + // used to select the subset of monolithic hidden API flags against which the flags generated + // by the bootclasspath_fragment are compared. + // + // The signature patterns file selects the subset of monolithic hidden API flags using a number + // of patterns, i.e.: + // * The qualified name (including package) of an outermost class, e.g. java/lang/Character. + // This selects all the flags for all the members of this class and any nested classes. + // * A package wildcard, e.g. java/lang/*. This selects all the flags for all the members of all + // the classes in this package (but not in sub-packages). + // * A recursive package wildcard, e.g. java/**. This selects all the flags for all the members + // of all the classes in this package and sub-packages. + // + // The signature patterns file is constructed as follows: + // * All the signatures are retrieved from the all-flags.csv file. + // * The member and inner class names are removed. + // * If a class is in a split package then that is kept, otherwise the class part is removed + // and replaced with a wildcard, i.e. *. + // * If a package matches a package prefix then the package is removed. + // * All the package prefixes are added with a recursive wildcard appended to each, i.e. **. + // * The resulting patterns are sorted. + // + // So, by default (i.e. without specifying any package_prefixes or split_packages) the signature + // patterns is a list of class names, because there are no package packages and all packages are + // assumed to be split. + // + // If any split packages are specified then only those packages are treated as split and all + // other packages are treated as belonging solely to the bootclasspath_fragment and so they use + // wildcard package patterns. + // + // So, if an empty list of split packages is specified then the signature patterns file just + // includes a wildcard package pattern for every package provided by the bootclasspath_fragment. + // + // If split_packages are specified and a package that is split is not listed then it could lead + // to build failures as it will select monolithic flags that are generated by another + // bootclasspath_fragment to compare against the flags provided by this fragment. The latter + // will obviously not contain those flags and that can cause the comparison and build to fail. + // + // If any package prefixes are specified then any matching packages are removed from the + // signature patterns and replaced with a single recursive package pattern. + // + // It is not strictly necessary to specify either package_prefixes or split_packages as the + // defaults will produce a valid set of signature patterns. However, those patterns may include + // implementation details, e.g. names of implementation classes or packages, which will be + // exported to the sdk snapshot in the signature patterns file. That is something that should be + // avoided where possible. Specifying package_prefixes and split_packages allows those + // implementation details to be excluded from the snapshot. + Split_packages []string + } +} + +type SourceOnlyBootclasspathProperties struct { + HiddenApiPackageProperties + Coverage HiddenApiPackageProperties +} + type BootclasspathFragmentModule struct { android.ModuleBase android.ApexModuleBase @@ -146,6 +228,16 @@ type BootclasspathFragmentModule struct { ClasspathFragmentBase properties bootclasspathFragmentProperties + + sourceOnlyProperties SourceOnlyBootclasspathProperties + + // Collect the module directory for IDE info in java/jdeps.go. + modulePaths []string + + // Installs for on-device boot image files. This list has entries only if the installs should be + // handled by Make (e.g., the boot image should be installed on the system partition, rather than + // in the APEX). + bootImageDeviceInstalls []dexpreopterInstall } // commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt @@ -177,7 +269,7 @@ type bootImageFilesByArch map[android.ArchType]android.Paths func bootclasspathFragmentFactory() android.Module { m := &BootclasspathFragmentModule{} - m.AddProperties(&m.properties) + m.AddProperties(&m.properties, &m.sourceOnlyProperties) android.InitApexModule(m) android.InitSdkAwareModule(m) initClasspathFragment(m, BOOTCLASSPATH) @@ -192,6 +284,12 @@ func bootclasspathFragmentFactory() android.Module { ctx.PropertyErrorf("coverage", "error trying to append coverage specific properties: %s", err) return } + + err = proptools.AppendProperties(&m.sourceOnlyProperties.HiddenApiPackageProperties, &m.sourceOnlyProperties.Coverage, nil) + if err != nil { + ctx.PropertyErrorf("coverage", "error trying to append hidden api coverage specific properties: %s", err) + return + } } // Initialize the contents property from the image_name. @@ -314,9 +412,19 @@ type BootclasspathFragmentApexContentInfo struct { // Map from arch type to the boot image files. bootImageFilesByArch bootImageFilesByArch + // True if the boot image should be installed in the APEX. + shouldInstallBootImageInApex bool + // Map from the base module name (without prebuilt_ prefix) of a fragment's contents module to the // hidden API encoded dex jar path. contentModuleDexJarPaths bootDexJarByModule + + // Path to the image profile file on host (or empty, if profile is not generated). + profilePathOnHost android.Path + + // Install path of the boot image profile if it needs to be installed in the APEX, or empty if not + // needed. + profileInstallPathInApex string } func (i BootclasspathFragmentApexContentInfo) Modules() android.ConfiguredJarList { @@ -330,6 +438,11 @@ func (i BootclasspathFragmentApexContentInfo) AndroidBootImageFilesByArchType() return i.bootImageFilesByArch } +// Return true if the boot image should be installed in the APEX. +func (i *BootclasspathFragmentApexContentInfo) ShouldInstallBootImageInApex() bool { + return i.shouldInstallBootImageInApex +} + // DexBootJarPathForContentModule returns the path to the dex boot jar for specified module. // // The dex boot jar is one which has had hidden API encoding performed on it. @@ -345,6 +458,14 @@ func (i BootclasspathFragmentApexContentInfo) DexBootJarPathForContentModule(mod } } +func (i BootclasspathFragmentApexContentInfo) ProfilePathOnHost() android.Path { + return i.profilePathOnHost +} + +func (i BootclasspathFragmentApexContentInfo) ProfileInstallPathInApex() string { + return i.profileInstallPathInApex +} + func (b *BootclasspathFragmentModule) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool { tag := ctx.OtherModuleDependencyTag(dep) if IsBootclasspathFragmentContentDepTag(tag) { @@ -422,6 +543,9 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo // Generate classpaths.proto config b.generateClasspathProtoBuildActions(ctx) + // Collect the module directory for IDE info in java/jdeps.go. + b.modulePaths = append(b.modulePaths, ctx.ModuleDir()) + // Gather the bootclasspath fragment's contents. var contents []android.Module ctx.VisitDirectDeps(func(module android.Module) { @@ -459,6 +583,24 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo // Copy the dex jars of this fragment's content modules to their predefined locations. copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule) } + + for _, variant := range imageConfig.apexVariants() { + arch := variant.target.Arch.ArchType.String() + for _, install := range variant.deviceInstalls { + // 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) + installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir) + + b.bootImageDeviceInstalls = append(b.bootImageDeviceInstalls, dexpreopterInstall{ + name: arch + "-" + installBase, + moduleName: b.Name(), + outputPathOnHost: install.From, + installDirOnDevice: installPath, + installFileOnDevice: installBase, + }) + } + } } // A prebuilt fragment cannot contribute to an apex. @@ -466,6 +608,19 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo // Provide the apex content info. b.provideApexContentInfo(ctx, imageConfig, hiddenAPIOutput, bootImageFilesByArch) } + } else { + // Versioned fragments are not needed by make. + b.HideFromMake() + } + + // In order for information about bootclasspath_fragment modules to be added to module-info.json + // it is necessary to output an entry to Make. As bootclasspath_fragment modules are part of an + // APEX there can be multiple variants, including the default/platform variant and only one can + // be output to Make but it does not really matter which variant is output. The default/platform + // variant is the first (ctx.PrimaryModule()) and is usually hidden from make so this just picks + // the last variant (ctx.FinalModule()). + if ctx.Module() != ctx.FinalModule() { + b.HideFromMake() } } @@ -503,6 +658,13 @@ func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleC if imageConfig != nil { info.modules = imageConfig.modules + global := dexpreopt.GetGlobalConfig(ctx) + if !global.DisableGenerateProfile { + info.profilePathOnHost = imageConfig.profilePathOnHost + info.profileInstallPathInApex = imageConfig.profileInstallPathInApex + } + + info.shouldInstallBootImageInApex = imageConfig.shouldInstallInApex() } info.bootImageFilesByArch = bootImageFilesByArch @@ -514,35 +676,46 @@ func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleC // generateClasspathProtoBuildActions generates all required build actions for classpath.proto config func (b *BootclasspathFragmentModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) { var classpathJars []classpathJar + configuredJars := b.configuredJars(ctx) if "art" == proptools.String(b.properties.Image_name) { // ART and platform boot jars must have a corresponding entry in DEX2OATBOOTCLASSPATH - classpathJars = configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH) + classpathJars = configuredJarListToClasspathJars(ctx, configuredJars, BOOTCLASSPATH, DEX2OATBOOTCLASSPATH) } else { - classpathJars = configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), b.classpathType) + classpathJars = configuredJarListToClasspathJars(ctx, configuredJars, b.classpathType) } - b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars) + b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars) } -func (b *BootclasspathFragmentModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList { +func (b *BootclasspathFragmentModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList { if "art" == proptools.String(b.properties.Image_name) { return b.getImageConfig(ctx).modules } global := dexpreopt.GetGlobalConfig(ctx) - possibleUpdatableModules := gatherPossibleUpdatableModuleNamesAndStems(ctx, b.properties.Contents, bootclasspathFragmentContentDepTag) - - // Only create configs for updatable boot jars. Non-updatable boot jars must be part of the - // platform_bootclasspath's classpath proto config to guarantee that they come before any - // updatable jars at runtime. - jars := global.UpdatableBootJars.Filter(possibleUpdatableModules) + possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, b.properties.Contents, bootclasspathFragmentContentDepTag) + jars, unknown := global.ApexBootJars.Filter(possibleUpdatableModules) // TODO(satayev): for apex_test we want to include all contents unconditionally to classpaths - // config. However, any test specific jars would not be present in UpdatableBootJars. Instead, + // config. However, any test specific jars would not be present in ApexBootJars. Instead, // we should check if we are creating a config for apex_test via ApexInfo and amend the values. // This is an exception to support end-to-end test for SdkExtensions, until such support exists. if android.InList("test_framework-sdkextensions", possibleUpdatableModules) { jars = jars.Append("com.android.sdkext", "test_framework-sdkextensions") + } else if android.InList("AddNewActivity", possibleUpdatableModules) { + jars = jars.Append("test_com.android.cts.frameworkresapkplits", "AddNewActivity") + } else if android.InList("test_framework-apexd", possibleUpdatableModules) { + jars = jars.Append("com.android.apex.test_package", "test_framework-apexd") + } else if global.ApexBootJars.Len() != 0 && !android.IsModuleInVersionedSdk(ctx.Module()) { + unknown = android.RemoveListFromList(unknown, b.properties.Coverage.Contents) + _, unknown = android.RemoveFromList("core-icu4j", unknown) + // This module only exists in car products. + // So ignore it even if it is not in PRODUCT_APEX_BOOT_JARS. + // TODO(b/202896428): Add better way to handle this. + _, unknown = android.RemoveFromList("android.car-module", unknown) + if len(unknown) > 0 { + ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown) + } } return jars } @@ -576,6 +749,15 @@ func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android. common := ctx.Module().(commonBootclasspathFragment) output := common.produceHiddenAPIOutput(ctx, contents, input) + // If the source or prebuilts module does not provide a signature patterns file then generate one + // from the flags. + // TODO(b/192868581): Remove once the source and prebuilts provide a signature patterns file of + // their own. + if output.SignaturePatternsPath == nil { + output.SignaturePatternsPath = buildRuleSignaturePatternsFile( + ctx, output.AllFlagsPath, []string{"*"}, nil, nil) + } + // Initialize a HiddenAPIInfo structure. hiddenAPIInfo := HiddenAPIInfo{ // The monolithic hidden API processing needs access to the flag files that override the default @@ -592,7 +774,7 @@ func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android. // The monolithic hidden API processing also needs access to all the output files produced by // hidden API processing of this fragment. - hiddenAPIInfo.HiddenAPIFlagOutput = (*output).HiddenAPIFlagOutput + hiddenAPIInfo.HiddenAPIFlagOutput = output.HiddenAPIFlagOutput // Provide it for use by other modules. ctx.SetProvider(HiddenAPIInfoProvider, hiddenAPIInfo) @@ -642,7 +824,22 @@ func (b *BootclasspathFragmentModule) createHiddenAPIFlagInput(ctx android.Modul func (b *BootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput { // Generate the rules to create the hidden API flags and update the supplied hiddenAPIInfo with the // paths to the created files. - return hiddenAPIRulesForBootclasspathFragment(ctx, contents, input) + output := hiddenAPIRulesForBootclasspathFragment(ctx, contents, input) + + // If the module specifies split_packages or package_prefixes then use those to generate the + // signature patterns. + splitPackages := b.sourceOnlyProperties.Hidden_api.Split_packages + packagePrefixes := b.sourceOnlyProperties.Hidden_api.Package_prefixes + singlePackages := b.sourceOnlyProperties.Hidden_api.Single_packages + if splitPackages != nil || packagePrefixes != nil || singlePackages != nil { + if splitPackages == nil { + splitPackages = []string{"*"} + } + output.SignaturePatternsPath = buildRuleSignaturePatternsFile( + ctx, output.AllFlagsPath, splitPackages, packagePrefixes, singlePackages) + } + + return output } // produceBootImageFiles builds the boot image files from the source if it is required. @@ -691,12 +888,50 @@ func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android. return androidBootImageFilesByArch } +func (b *BootclasspathFragmentModule) AndroidMkEntries() []android.AndroidMkEntries { + // Use the generated classpath proto as the output. + outputFile := b.outputFilepath + // Create a fake entry that will cause this to be added to the module-info.json file. + entriesList := []android.AndroidMkEntries{{ + Class: "FAKE", + OutputFile: android.OptionalPathForPath(outputFile), + Include: "$(BUILD_PHONY_PACKAGE)", + ExtraFooters: []android.AndroidMkExtraFootersFunc{ + func(w io.Writer, name, prefix, moduleDir string) { + // Allow the bootclasspath_fragment to be built by simply passing its name on the command + // line. + fmt.Fprintln(w, ".PHONY:", b.Name()) + fmt.Fprintln(w, b.Name()+":", outputFile.String()) + }, + }, + }} + for _, install := range b.bootImageDeviceInstalls { + entriesList = append(entriesList, install.ToMakeEntries()) + } + return entriesList +} + +// Returns the names of all Make modules that handle the installation of the boot image. +func (b *BootclasspathFragmentModule) BootImageDeviceInstallMakeModules() []string { + var makeModules []string + for _, install := range b.bootImageDeviceInstalls { + makeModules = append(makeModules, install.FullModuleName()) + } + return makeModules +} + +// Collect information for opening IDE project files in java/jdeps.go. +func (b *BootclasspathFragmentModule) IDEInfo(dpInfo *android.IdeInfo) { + dpInfo.Deps = append(dpInfo.Deps, b.properties.Contents...) + dpInfo.Paths = append(dpInfo.Paths, b.modulePaths...) +} + type bootclasspathFragmentMemberType struct { android.SdkMemberTypeBase } -func (b *bootclasspathFragmentMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) { - mctx.AddVariationDependencies(nil, dependencyTag, names...) +func (b *bootclasspathFragmentMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) { + ctx.AddVariationDependencies(nil, dependencyTag, names...) } func (b *bootclasspathFragmentMemberType) IsInstance(module android.Module) bool { @@ -729,12 +964,12 @@ type bootclasspathFragmentSdkMemberProperties struct { Stub_libs []string Core_platform_stub_libs []string + // Fragment properties + Fragments []ApexVariantReference + // Flag files by *hiddenAPIFlagFileCategory Flag_files_by_category FlagFilesByCategory - // The path to the generated stub-flags.csv file. - Stub_flags_path android.OptionalPath - // The path to the generated annotation-flags.csv file. Annotation_flags_path android.OptionalPath @@ -744,8 +979,20 @@ type bootclasspathFragmentSdkMemberProperties struct { // The path to the generated index.csv file. Index_path android.OptionalPath + // The path to the generated stub-flags.csv file. + Stub_flags_path android.OptionalPath `supported_build_releases:"S"` + // The path to the generated all-flags.csv file. - All_flags_path android.OptionalPath + All_flags_path android.OptionalPath `supported_build_releases:"S"` + + // The path to the generated signature-patterns.csv file. + Signature_patterns_path android.OptionalPath `supported_build_releases:"Tiramisu+"` + + // The path to the generated filtered-stub-flags.csv file. + Filtered_stub_flags_path android.OptionalPath `supported_build_releases:"Tiramisu+"` + + // The path to the generated filtered-flags.csv file. + Filtered_flags_path android.OptionalPath `supported_build_releases:"Tiramisu+"` } func (b *bootclasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) { @@ -760,15 +1007,23 @@ func (b *bootclasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx andro b.Flag_files_by_category = hiddenAPIInfo.FlagFilesByCategory // Copy all the generated file paths. - b.Stub_flags_path = android.OptionalPathForPath(hiddenAPIInfo.StubFlagsPath) b.Annotation_flags_path = android.OptionalPathForPath(hiddenAPIInfo.AnnotationFlagsPath) b.Metadata_path = android.OptionalPathForPath(hiddenAPIInfo.MetadataPath) b.Index_path = android.OptionalPathForPath(hiddenAPIInfo.IndexPath) + + b.Stub_flags_path = android.OptionalPathForPath(hiddenAPIInfo.StubFlagsPath) b.All_flags_path = android.OptionalPathForPath(hiddenAPIInfo.AllFlagsPath) + b.Signature_patterns_path = android.OptionalPathForPath(hiddenAPIInfo.SignaturePatternsPath) + b.Filtered_stub_flags_path = android.OptionalPathForPath(hiddenAPIInfo.FilteredStubFlagsPath) + b.Filtered_flags_path = android.OptionalPathForPath(hiddenAPIInfo.FilteredFlagsPath) + // Copy stub_libs properties. b.Stub_libs = module.properties.Api.Stub_libs b.Core_platform_stub_libs = module.properties.Core_platform_api.Stub_libs + + // Copy fragment properties. + b.Fragments = module.properties.Fragments } func (b *bootclasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) { @@ -791,6 +1046,9 @@ func (b *bootclasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android. corePlatformApiPropertySet := propertySet.AddPropertySet("core_platform_api") corePlatformApiPropertySet.AddPropertyWithTag("stub_libs", b.Core_platform_stub_libs, requiredMemberDependency) } + if len(b.Fragments) > 0 { + propertySet.AddProperty("fragments", b.Fragments) + } hiddenAPISet := propertySet.AddPropertySet("hidden_api") hiddenAPIDir := "hiddenapi" @@ -821,11 +1079,16 @@ func (b *bootclasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android. } // Copy all the generated files, if available. - copyOptionalPath(b.Stub_flags_path, "stub_flags") copyOptionalPath(b.Annotation_flags_path, "annotation_flags") copyOptionalPath(b.Metadata_path, "metadata") copyOptionalPath(b.Index_path, "index") + + copyOptionalPath(b.Stub_flags_path, "stub_flags") copyOptionalPath(b.All_flags_path, "all_flags") + + copyOptionalPath(b.Signature_patterns_path, "signature_patterns") + copyOptionalPath(b.Filtered_stub_flags_path, "filtered_stub_flags") + copyOptionalPath(b.Filtered_flags_path, "filtered_flags") } var _ android.SdkMemberType = (*bootclasspathFragmentMemberType)(nil) @@ -834,9 +1097,6 @@ var _ android.SdkMemberType = (*bootclasspathFragmentMemberType)(nil) // specific properties. type prebuiltBootclasspathFragmentProperties struct { Hidden_api struct { - // The path to the stub-flags.csv file created by the bootclasspath_fragment. - Stub_flags *string `android:"path"` - // The path to the annotation-flags.csv file created by the bootclasspath_fragment. Annotation_flags *string `android:"path"` @@ -846,8 +1106,20 @@ type prebuiltBootclasspathFragmentProperties struct { // The path to the index.csv file created by the bootclasspath_fragment. Index *string `android:"path"` + // The path to the signature-patterns.csv file created by the bootclasspath_fragment. + Signature_patterns *string `android:"path"` + + // The path to the stub-flags.csv file created by the bootclasspath_fragment. + Stub_flags *string `android:"path"` + // The path to the all-flags.csv file created by the bootclasspath_fragment. All_flags *string `android:"path"` + + // The path to the filtered-stub-flags.csv file created by the bootclasspath_fragment. + Filtered_stub_flags *string `android:"path"` + + // The path to the filtered-flags.csv file created by the bootclasspath_fragment. + Filtered_flags *string `android:"path"` } } @@ -856,7 +1128,7 @@ type prebuiltBootclasspathFragmentProperties struct { // At the moment this is basically just a bootclasspath_fragment module that can be used as a // prebuilt. Eventually as more functionality is migrated into the bootclasspath_fragment module // type from the various singletons then this will diverge. -type prebuiltBootclasspathFragmentModule struct { +type PrebuiltBootclasspathFragmentModule struct { BootclasspathFragmentModule prebuilt android.Prebuilt @@ -864,20 +1136,26 @@ type prebuiltBootclasspathFragmentModule struct { prebuiltProperties prebuiltBootclasspathFragmentProperties } -func (module *prebuiltBootclasspathFragmentModule) Prebuilt() *android.Prebuilt { +func (module *PrebuiltBootclasspathFragmentModule) Prebuilt() *android.Prebuilt { return &module.prebuilt } -func (module *prebuiltBootclasspathFragmentModule) Name() string { +func (module *PrebuiltBootclasspathFragmentModule) Name() string { return module.prebuilt.Name(module.ModuleBase.Name()) } // produceHiddenAPIOutput returns a path to the prebuilt all-flags.csv or nil if none is specified. -func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput { - pathForOptionalSrc := func(src *string) android.Path { +func (module *PrebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput { + pathForOptionalSrc := func(src *string, defaultPath android.Path) android.Path { if src == nil { - // TODO(b/179354495): Fail if this is not provided once prebuilts have been updated. - return nil + return defaultPath + } + return android.PathForModuleSrc(ctx, *src) + } + pathForSrc := func(property string, src *string) android.Path { + if src == nil { + ctx.PropertyErrorf(property, "is required but was not specified") + return android.PathForModuleSrc(ctx, "missing", property) } return android.PathForModuleSrc(ctx, *src) } @@ -888,76 +1166,83 @@ func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx an output := HiddenAPIOutput{ HiddenAPIFlagOutput: HiddenAPIFlagOutput{ - StubFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Stub_flags), - AnnotationFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Annotation_flags), - MetadataPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Metadata), - IndexPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Index), - AllFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.All_flags), + AnnotationFlagsPath: pathForSrc("hidden_api.annotation_flags", module.prebuiltProperties.Hidden_api.Annotation_flags), + MetadataPath: pathForSrc("hidden_api.metadata", module.prebuiltProperties.Hidden_api.Metadata), + IndexPath: pathForSrc("hidden_api.index", module.prebuiltProperties.Hidden_api.Index), + SignaturePatternsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Signature_patterns, nil), + // TODO: Temporarily handle stub_flags/all_flags properties until prebuilts have been updated. + StubFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Stub_flags, nil), + AllFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.All_flags, nil), }, + EncodedBootDexFilesByModule: encodedBootDexJarsByModule, } + // TODO: Temporarily fallback to stub_flags/all_flags properties until prebuilts have been updated. + output.FilteredStubFlagsPath = pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Filtered_stub_flags, output.StubFlagsPath) + output.FilteredFlagsPath = pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Filtered_flags, output.AllFlagsPath) + return &output } // produceBootImageFiles extracts the boot image files from the APEX if available. -func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch { +func (module *PrebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch { if !shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) { return nil } - var deapexerModule android.Module - ctx.VisitDirectDeps(func(to android.Module) { - tag := ctx.OtherModuleDependencyTag(to) - // Save away the `deapexer` module on which this depends, if any. - if tag == android.DeapexerTag { - if deapexerModule != nil { - ctx.ModuleErrorf("Ambiguous duplicate deapexer module dependencies %q and %q", - deapexerModule.Name(), to.Name()) - } - deapexerModule = to - } - }) - - if deapexerModule == nil { - // This should never happen as a variant for a prebuilt_apex is only created if the - // deapexer module has been configured to export the dex implementation jar for this module. - ctx.ModuleErrorf("internal error: module does not depend on a `deapexer` module") - return nil + di := android.FindDeapexerProviderForModule(ctx) + if di == nil { + return nil // An error has been reported by FindDeapexerProviderForModule. } - di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo) - files := bootImageFilesByArch{} - for _, variant := range imageConfig.apexVariants() { - arch := variant.target.Arch.ArchType - for _, toPath := range variant.imagesDeps { - apexRelativePath := apexRootRelativePathToBootImageFile(arch, toPath.Base()) - // Get the path to the file that the deapexer extracted from the prebuilt apex file. - fromPath := di.PrebuiltExportPath(apexRelativePath) - - // Return the toPath as the calling code expects the paths in the returned map to be the - // paths predefined in the bootImageConfig. - files[arch] = append(files[arch], toPath) - - // Copy the file to the predefined location. - ctx.Build(pctx, android.BuildParams{ - Rule: android.Cp, - Input: fromPath, - Output: toPath, - }) - } + profile := (android.WritablePath)(nil) + if imageConfig.profileInstallPathInApex != "" { + profile = di.PrebuiltExportPath(imageConfig.profileInstallPathInApex) } - // Build the boot image files for the host variants. These are built from the dex files provided - // by the contents of this module as prebuilt versions of the host boot image files are not - // available, i.e. there is no host specific prebuilt apex containing them. This has to be built - // without a profile as the prebuilt modules do not provide a profile. - buildBootImageVariantsForBuildOs(ctx, imageConfig, nil) + // Build the boot image files for the host variants. These are always built from the dex files + // provided by the contents of this module as prebuilt versions of the host boot image files are + // not available, i.e. there is no host specific prebuilt apex containing them. This has to be + // built without a profile as the prebuilt modules do not provide a profile. + buildBootImageVariantsForBuildOs(ctx, imageConfig, profile) - return files + if imageConfig.shouldInstallInApex() { + // If the boot image files for the android variants are in the prebuilt apex, we must use those + // rather than building new ones because those boot image files are going to be used on device. + files := bootImageFilesByArch{} + for _, variant := range imageConfig.apexVariants() { + arch := variant.target.Arch.ArchType + for _, toPath := range variant.imagesDeps { + apexRelativePath := apexRootRelativePathToBootImageFile(arch, toPath.Base()) + // Get the path to the file that the deapexer extracted from the prebuilt apex file. + fromPath := di.PrebuiltExportPath(apexRelativePath) + + // Return the toPath as the calling code expects the paths in the returned map to be the + // paths predefined in the bootImageConfig. + files[arch] = append(files[arch], toPath) + + // Copy the file to the predefined location. + ctx.Build(pctx, android.BuildParams{ + Rule: android.Cp, + Input: fromPath, + Output: toPath, + }) + } + } + return files + } else { + if profile == nil { + ctx.ModuleErrorf("Unable to produce boot image files: neither boot image files nor profiles exists in the prebuilt apex") + return nil + } + // Build boot image files for the android variants from the dex files provided by the contents + // of this module. + return buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile) + } } -var _ commonBootclasspathFragment = (*prebuiltBootclasspathFragmentModule)(nil) +var _ commonBootclasspathFragment = (*PrebuiltBootclasspathFragmentModule)(nil) // createBootImageTag creates the tag to uniquely identify the boot image file among all of the // files that a module requires from the prebuilt .apex file. @@ -971,16 +1256,22 @@ func createBootImageTag(arch android.ArchType, baseName string) string { // // If there is no image config associated with this fragment then it returns nil. Otherwise, it // returns the files that are listed in the image config. -func (module *prebuiltBootclasspathFragmentModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string { +func (module *PrebuiltBootclasspathFragmentModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string { imageConfig := module.getImageConfig(ctx) if imageConfig != nil { - // Add the boot image files, e.g. .art, .oat and .vdex files. files := []string{} - for _, variant := range imageConfig.apexVariants() { - arch := variant.target.Arch.ArchType - for _, path := range variant.imagesDeps.Paths() { - base := path.Base() - files = append(files, apexRootRelativePathToBootImageFile(arch, base)) + if imageConfig.profileInstallPathInApex != "" { + // Add the boot image profile. + files = append(files, imageConfig.profileInstallPathInApex) + } + if imageConfig.shouldInstallInApex() { + // Add the boot image files, e.g. .art, .oat and .vdex files. + for _, variant := range imageConfig.apexVariants() { + arch := variant.target.Arch.ArchType + for _, path := range variant.imagesDeps.Paths() { + base := path.Base() + files = append(files, apexRootRelativePathToBootImageFile(arch, base)) + } } } return files @@ -992,10 +1283,10 @@ func apexRootRelativePathToBootImageFile(arch android.ArchType, base string) str return filepath.Join("javalib", arch.String(), base) } -var _ android.RequiredFilesFromPrebuiltApex = (*prebuiltBootclasspathFragmentModule)(nil) +var _ android.RequiredFilesFromPrebuiltApex = (*PrebuiltBootclasspathFragmentModule)(nil) func prebuiltBootclasspathFragmentFactory() android.Module { - m := &prebuiltBootclasspathFragmentModule{} + m := &PrebuiltBootclasspathFragmentModule{} m.AddProperties(&m.properties, &m.prebuiltProperties) // This doesn't actually have any prebuilt files of its own so pass a placeholder for the srcs // array. diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go index 3d0e1558f..d3de675d8 100644 --- a/java/bootclasspath_fragment_test.go +++ b/java/bootclasspath_fragment_test.go @@ -164,6 +164,7 @@ func TestBootclasspathFragment_Coverage(t *testing.T) { prepareForTestWithBootclasspathFragment, PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("mysdklibrary", "mycoveragestubs"), + FixtureConfigureApexBootJars("someapex:mybootlib"), prepareWithBp, ) @@ -186,6 +187,7 @@ func TestBootclasspathFragment_StubLibs(t *testing.T) { prepareForTestWithBootclasspathFragment, PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary", "mycoreplatform"), + FixtureConfigureApexBootJars("someapex:mysdklibrary"), ).RunTestWithBp(t, ` bootclasspath_fragment { name: "myfragment", diff --git a/java/builder.go b/java/builder.go index cde87310f..c0fadd42c 100644 --- a/java/builder.go +++ b/java/builder.go @@ -120,42 +120,39 @@ var ( "extractMatchingApks", blueprint.RuleParams{ Command: `rm -rf "$out" && ` + - `${config.ExtractApksCmd} -o "${out}" -allow-prereleased=${allow-prereleased} ` + + `${config.ExtractApksCmd} -o "${out}" -zip "${zip}" -allow-prereleased=${allow-prereleased} ` + `-sdk-version=${sdk-version} -abis=${abis} ` + `--screen-densities=${screen-densities} --stem=${stem} ` + `-apkcerts=${apkcerts} -partition=${partition} ` + `${in}`, CommandDeps: []string{"${config.ExtractApksCmd}"}, }, - "abis", "allow-prereleased", "screen-densities", "sdk-version", "stem", "apkcerts", "partition") + "abis", "allow-prereleased", "screen-densities", "sdk-version", "stem", "apkcerts", "partition", "zip") turbine, turbineRE = pctx.RemoteStaticRules("turbine", blueprint.RuleParams{ - Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` + - `$reTemplate${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.TurbineJar} --output $out.tmp ` + - `--temp_dir "$outDir" --sources @$out.rsp --source_jars $srcJars ` + + Command: `$reTemplate${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.TurbineJar} $outputFlags ` + + `--sources @$out.rsp --source_jars $srcJars ` + `--javacopts ${config.CommonJdkFlags} ` + - `$javacFlags -source $javaVersion -target $javaVersion -- $bootClasspath $classpath && ` + - `${config.Ziptime} $out.tmp && ` + - `(if cmp -s $out.tmp $out ; then rm $out.tmp ; else mv $out.tmp $out ; fi )`, + `$javacFlags -source $javaVersion -target $javaVersion -- $turbineFlags && ` + + `(for o in $outputs; do if cmp -s $${o}.tmp $${o} ; then rm $${o}.tmp ; else mv $${o}.tmp $${o} ; fi; done )`, CommandDeps: []string{ "${config.TurbineJar}", "${config.JavaCmd}", - "${config.Ziptime}", }, Rspfile: "$out.rsp", RspfileContent: "$in", Restat: true, }, &remoteexec.REParams{Labels: map[string]string{"type": "tool", "name": "turbine"}, - ExecStrategy: "${config.RETurbineExecStrategy}", - Inputs: []string{"${config.TurbineJar}", "${out}.rsp", "$implicits"}, - RSPFiles: []string{"${out}.rsp"}, - OutputFiles: []string{"$out.tmp"}, - OutputDirectories: []string{"$outDir"}, - ToolchainInputs: []string{"${config.JavaCmd}"}, - Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"}, - }, []string{"javacFlags", "bootClasspath", "classpath", "srcJars", "outDir", "javaVersion"}, []string{"implicits"}) + ExecStrategy: "${config.RETurbineExecStrategy}", + Inputs: []string{"${config.TurbineJar}", "${out}.rsp", "$implicits"}, + RSPFiles: []string{"${out}.rsp"}, + OutputFiles: []string{"$rbeOutputs"}, + ToolchainInputs: []string{"${config.JavaCmd}"}, + Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"}, + }, + []string{"javacFlags", "turbineFlags", "outputFlags", "javaVersion", "outputs", "rbeOutputs", "srcJars"}, []string{"implicits"}) jar, jarRE = pctx.RemoteStaticRules("jar", blueprint.RuleParams{ @@ -247,22 +244,40 @@ func init() { } type javaBuilderFlags struct { - javacFlags string - bootClasspath classpath - classpath classpath + javacFlags string + + // bootClasspath is the list of jars that form the boot classpath (generally the java.* and + // android.* classes) for tools that still use it. javac targeting 1.9 or higher uses + // systemModules and java9Classpath instead. + bootClasspath classpath + + // classpath is the list of jars that form the classpath for javac and kotlinc rules. It + // contains header jars for all static and non-static dependencies. + classpath classpath + + // dexClasspath is the list of jars that form the classpath for d8 and r8 rules. It contains + // header jars for all non-static dependencies. Static dependencies have already been + // combined into the program jar. + dexClasspath classpath + + // java9Classpath is the list of jars that will be added to the classpath when targeting + // 1.9 or higher. It generally contains the android.* classes, while the java.* classes + // are provided by systemModules. java9Classpath classpath - processorPath classpath - processors []string - systemModules *systemModules - aidlFlags string - aidlDeps android.Paths - javaVersion javaVersion + + processorPath classpath + processors []string + systemModules *systemModules + aidlFlags string + aidlDeps android.Paths + javaVersion javaVersion errorProneExtraJavacFlags string errorProneProcessorPath classpath kotlincFlags string kotlincClasspath classpath + kotlincDeps android.Paths proto android.ProtoFlags } @@ -279,23 +294,6 @@ func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.Writab transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, flags, deps, "javac", desc) } -func RunErrorProne(ctx android.ModuleContext, outputFile android.WritablePath, - srcFiles, srcJars android.Paths, flags javaBuilderFlags) { - - flags.processorPath = append(flags.errorProneProcessorPath, flags.processorPath...) - - if len(flags.errorProneExtraJavacFlags) > 0 { - if len(flags.javacFlags) > 0 { - flags.javacFlags += " " + flags.errorProneExtraJavacFlags - } else { - flags.javacFlags = flags.errorProneExtraJavacFlags - } - } - - transformJavaToClasses(ctx, outputFile, -1, srcFiles, srcJars, flags, nil, - "errorprone", "errorprone") -} - // Emits the rule to generate Xref input file (.kzip file) for the given set of source files and source jars // to compile with given set of builder flags, etc. func emitXrefRule(ctx android.ModuleContext, xrefFile android.WritablePath, idx int, @@ -357,11 +355,8 @@ func emitXrefRule(ctx android.ModuleContext, xrefFile android.WritablePath, idx }) } -func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath, - srcFiles, srcJars android.Paths, flags javaBuilderFlags) { - +func turbineFlags(ctx android.ModuleContext, flags javaBuilderFlags) (string, android.Paths) { var deps android.Paths - deps = append(deps, srcJars...) classpath := flags.classpath @@ -383,20 +378,31 @@ func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android. } deps = append(deps, classpath...) - deps = append(deps, flags.processorPath...) + turbineFlags := bootClasspath + " " + classpath.FormTurbineClassPath("--classpath ") + + return turbineFlags, deps +} + +func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath, + srcFiles, srcJars android.Paths, flags javaBuilderFlags) { + + turbineFlags, deps := turbineFlags(ctx, flags) + + deps = append(deps, srcJars...) rule := turbine args := map[string]string{ - "javacFlags": flags.javacFlags, - "bootClasspath": bootClasspath, - "srcJars": strings.Join(srcJars.Strings(), " "), - "classpath": classpath.FormTurbineClassPath("--classpath "), - "outDir": android.PathForModuleOut(ctx, "turbine", "classes").String(), - "javaVersion": flags.javaVersion.String(), + "javacFlags": flags.javacFlags, + "srcJars": strings.Join(srcJars.Strings(), " "), + "javaVersion": flags.javaVersion.String(), + "turbineFlags": turbineFlags, + "outputFlags": "--output " + outputFile.String() + ".tmp", + "outputs": outputFile.String(), } if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_TURBINE") { rule = turbineRE args["implicits"] = strings.Join(deps.Strings(), ",") + args["rbeOutputs"] = outputFile.String() + ".tmp" } ctx.Build(pctx, android.BuildParams{ Rule: rule, @@ -408,6 +414,47 @@ func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android. }) } +// TurbineApt produces a rule to run annotation processors using turbine. +func TurbineApt(ctx android.ModuleContext, outputSrcJar, outputResJar android.WritablePath, + srcFiles, srcJars android.Paths, flags javaBuilderFlags) { + + turbineFlags, deps := turbineFlags(ctx, flags) + + deps = append(deps, srcJars...) + + deps = append(deps, flags.processorPath...) + turbineFlags += " " + flags.processorPath.FormTurbineClassPath("--processorpath ") + turbineFlags += " --processors " + strings.Join(flags.processors, " ") + + outputs := android.WritablePaths{outputSrcJar, outputResJar} + outputFlags := "--gensrc_output " + outputSrcJar.String() + ".tmp " + + "--resource_output " + outputResJar.String() + ".tmp" + + rule := turbine + args := map[string]string{ + "javacFlags": flags.javacFlags, + "srcJars": strings.Join(srcJars.Strings(), " "), + "javaVersion": flags.javaVersion.String(), + "turbineFlags": turbineFlags, + "outputFlags": outputFlags, + "outputs": strings.Join(outputs.Strings(), " "), + } + if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_TURBINE") { + rule = turbineRE + args["implicits"] = strings.Join(deps.Strings(), ",") + args["rbeOutputs"] = outputSrcJar.String() + ".tmp," + outputResJar.String() + ".tmp" + } + ctx.Build(pctx, android.BuildParams{ + Rule: rule, + Description: "turbine apt", + Output: outputs[0], + ImplicitOutputs: outputs[1:], + Inputs: srcFiles, + Implicits: deps, + Args: args, + }) +} + // transformJavaToClasses takes source files and converts them to a jar containing .class files. // srcFiles is a list of paths to sources, srcJars is a list of paths to jar files that contain // sources. flags contains various command line flags to be passed to the compiler. @@ -669,6 +716,6 @@ func (x *systemModules) FormTurbineSystemModulesPath(forceEmpty bool) (string, a } else if forceEmpty { return `--bootclasspath ""`, nil } else { - return "", nil + return "--system ${config.JavaHome}", nil } } diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go index ecfdfb7e5..259e977d8 100644 --- a/java/classpath_fragment.go +++ b/java/classpath_fragment.go @@ -19,12 +19,13 @@ package java import ( "fmt" "github.com/google/blueprint" + "github.com/google/blueprint/proptools" "strings" "android/soong/android" ) -// Build rules and utilities to generate individual packages/modules/SdkExtensions/proto/classpaths.proto +// Build rules and utilities to generate individual packages/modules/common/proto/classpaths.proto // config files based on build configuration to embed into /system and /apex on a device. // // See `derive_classpath` service that reads the configs at runtime and defines *CLASSPATH variables @@ -33,17 +34,23 @@ import ( type classpathType int const ( - // Matches definition in packages/modules/SdkExtensions/proto/classpaths.proto + // Matches definition in packages/modules/common/proto/classpaths.proto BOOTCLASSPATH classpathType = iota DEX2OATBOOTCLASSPATH SYSTEMSERVERCLASSPATH + STANDALONE_SYSTEMSERVER_JARS ) func (c classpathType) String() string { - return [...]string{"BOOTCLASSPATH", "DEX2OATBOOTCLASSPATH", "SYSTEMSERVERCLASSPATH"}[c] + return [...]string{"BOOTCLASSPATH", "DEX2OATBOOTCLASSPATH", "SYSTEMSERVERCLASSPATH", "STANDALONE_SYSTEMSERVER_JARS"}[c] } type classpathFragmentProperties struct { + // Whether to generated classpaths.proto config instance for the fragment. If the config is not + // generated, then relevant boot jars are added to platform classpath, i.e. platform_bootclasspath + // or platform_systemserverclasspath. This is useful for non-updatable APEX boot jars, to keep + // them as part of dexopt on device. Defaults to true. + Generate_classpaths_proto *bool } // classpathFragment interface is implemented by a module that contributes jars to a *CLASSPATH @@ -52,10 +59,6 @@ type classpathFragment interface { android.Module classpathFragmentBase() *ClasspathFragmentBase - - // ClasspathFragmentToConfiguredJarList returns android.ConfiguredJarList representation of all - // the jars in this classpath fragment. - ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList } // ClasspathFragmentBase is meant to be embedded in any module types that implement classpathFragment; @@ -82,15 +85,14 @@ func initClasspathFragment(c classpathFragment, classpathType classpathType) { // Matches definition of Jar in packages/modules/SdkExtensions/proto/classpaths.proto type classpathJar struct { - path string - classpath classpathType - // TODO(satayev): propagate min/max sdk versions for the jars - minSdkVersion int32 - maxSdkVersion int32 + path string + classpath classpathType + minSdkVersion string + maxSdkVersion string } -// gatherPossibleUpdatableModuleNamesAndStems returns a set of module and stem names from the -// supplied contents that may be in the updatable boot jars. +// gatherPossibleApexModuleNamesAndStems returns a set of module and stem names from the +// supplied contents that may be in the apex boot jars. // // The module names are included because sometimes the stem is set to just change the name of // the installed file and it expects the configuration to still use the actual module name. @@ -98,7 +100,7 @@ type classpathJar struct { // The stem names are included because sometimes the stem is set to change the effective name of the // module that is used in the configuration as well,e .g. when a test library is overriding an // actual boot jar -func gatherPossibleUpdatableModuleNamesAndStems(ctx android.ModuleContext, contents []string, tag blueprint.DependencyTag) []string { +func gatherPossibleApexModuleNamesAndStems(ctx android.ModuleContext, contents []string, tag blueprint.DependencyTag) []string { set := map[string]struct{}{} for _, name := range contents { dep := ctx.GetDirectDepWithTag(name, tag) @@ -118,58 +120,79 @@ func configuredJarListToClasspathJars(ctx android.ModuleContext, configuredJars jars := make([]classpathJar, 0, len(paths)*len(classpaths)) for i := 0; i < len(paths); i++ { for _, classpathType := range classpaths { - jars = append(jars, classpathJar{ + jar := classpathJar{ classpath: classpathType, path: paths[i], + } + ctx.VisitDirectDepsIf(func(m android.Module) bool { + return m.Name() == configuredJars.Jar(i) + }, func(m android.Module) { + if s, ok := m.(*SdkLibrary); ok { + // TODO(208456999): instead of mapping "current" to latest, min_sdk_version should never be set to "current" + if s.minSdkVersion.Specified() { + if s.minSdkVersion.ApiLevel.IsCurrent() { + jar.minSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String() + } else { + jar.minSdkVersion = s.minSdkVersion.ApiLevel.String() + } + } + if s.maxSdkVersion.Specified() { + if s.maxSdkVersion.ApiLevel.IsCurrent() { + jar.maxSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String() + } else { + jar.maxSdkVersion = s.maxSdkVersion.ApiLevel.String() + } + } + } }) + jars = append(jars, jar) } } return jars } -func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, jars []classpathJar) { - outputFilename := strings.ToLower(c.classpathType.String()) + ".pb" - c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath - c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths") - - generatedJson := android.PathForModuleOut(ctx, outputFilename+".json") - writeClasspathsJson(ctx, generatedJson, jars) - - rule := android.NewRuleBuilder(pctx, ctx) - rule.Command(). - BuiltTool("conv_classpaths_proto"). - Flag("encode"). - Flag("--format=json"). - FlagWithInput("--input=", generatedJson). - FlagWithOutput("--output=", c.outputFilepath) - - rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String()) +func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, configuredJars android.ConfiguredJarList, jars []classpathJar) { + generateProto := proptools.BoolDefault(c.properties.Generate_classpaths_proto, true) + if generateProto { + outputFilename := strings.ToLower(c.classpathType.String()) + ".pb" + c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath + c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths") + + generatedTextproto := android.PathForModuleOut(ctx, outputFilename+".textproto") + writeClasspathsTextproto(ctx, generatedTextproto, jars) + + rule := android.NewRuleBuilder(pctx, ctx) + rule.Command(). + BuiltTool("conv_classpaths_proto"). + Flag("encode"). + Flag("--format=textproto"). + FlagWithInput("--input=", generatedTextproto). + FlagWithOutput("--output=", c.outputFilepath) + + rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String()) + } classpathProtoInfo := ClasspathFragmentProtoContentInfo{ + ClasspathFragmentProtoGenerated: generateProto, + ClasspathFragmentProtoContents: configuredJars, ClasspathFragmentProtoInstallDir: c.installDirPath, ClasspathFragmentProtoOutput: c.outputFilepath, } ctx.SetProvider(ClasspathFragmentProtoContentInfoProvider, classpathProtoInfo) } -func writeClasspathsJson(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) { +func writeClasspathsTextproto(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) { var content strings.Builder - fmt.Fprintf(&content, "{\n") - fmt.Fprintf(&content, "\"jars\": [\n") - for idx, jar := range jars { - fmt.Fprintf(&content, "{\n") - - fmt.Fprintf(&content, "\"path\": \"%s\",\n", jar.path) - fmt.Fprintf(&content, "\"classpath\": \"%s\"\n", jar.classpath) - if idx < len(jars)-1 { - fmt.Fprintf(&content, "},\n") - } else { - fmt.Fprintf(&content, "}\n") - } + for _, jar := range jars { + fmt.Fprintf(&content, "jars {\n") + fmt.Fprintf(&content, "path: \"%s\"\n", jar.path) + fmt.Fprintf(&content, "classpath: %s\n", jar.classpath) + fmt.Fprintf(&content, "min_sdk_version: \"%s\"\n", jar.minSdkVersion) + fmt.Fprintf(&content, "max_sdk_version: \"%s\"\n", jar.maxSdkVersion) + fmt.Fprintf(&content, "}\n") } - fmt.Fprintf(&content, "]\n") - fmt.Fprintf(&content, "}\n") + android.WriteFileRule(ctx, output, content.String()) } @@ -181,7 +204,7 @@ func (c *ClasspathFragmentBase) androidMkEntries() []android.AndroidMkEntries { OutputFile: android.OptionalPathForPath(c.outputFilepath), ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - entries.SetString("LOCAL_MODULE_PATH", c.installDirPath.ToMakePath().String()) + entries.SetString("LOCAL_MODULE_PATH", c.installDirPath.String()) entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.outputFilepath.Base()) }, }, @@ -191,6 +214,12 @@ func (c *ClasspathFragmentBase) androidMkEntries() []android.AndroidMkEntries { var ClasspathFragmentProtoContentInfoProvider = blueprint.NewProvider(ClasspathFragmentProtoContentInfo{}) type ClasspathFragmentProtoContentInfo struct { + // Whether the classpaths.proto config is generated for the fragment. + ClasspathFragmentProtoGenerated bool + + // ClasspathFragmentProtoContents contains a list of jars that are part of this classpath fragment. + ClasspathFragmentProtoContents android.ConfiguredJarList + // ClasspathFragmentProtoOutput is an output path for the generated classpaths.proto config of this module. // // The file should be copied to a relevant place on device, see ClasspathFragmentProtoInstallDir diff --git a/java/config/config.go b/java/config/config.go index 30c6f91aa..95b841fa7 100644 --- a/java/config/config.go +++ b/java/config/config.go @@ -26,7 +26,8 @@ import ( ) var ( - pctx = android.NewPackageContext("android/soong/java/config") + pctx = android.NewPackageContext("android/soong/java/config") + exportedVars = android.NewExportedVariables(pctx) LegacyCorePlatformBootclasspathLibraries = []string{"legacy.core.platform.api.stubs", "core-lambda-stubs"} LegacyCorePlatformSystemModules = "legacy-core-platform-api-stubs-system-modules" @@ -50,27 +51,43 @@ var ( "core-icu4j", "core-oj", "core-libart", - // TODO: Could this be all updatable bootclasspath jars? - "updatable-media", - "framework-mediaprovider", - "framework-sdkextensions", - "android.net.ipsec.ike", } ) -const ( - JavaVmFlags = `-XX:OnError="cat hs_err_pid%p.log" -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads` - JavacVmFlags = `-J-XX:OnError="cat hs_err_pid%p.log" -J-XX:CICompilerCount=6 -J-XX:+UseDynamicNumberOfGCThreads` +var ( + JavacVmFlags = strings.Join(javacVmFlagsList, " ") + javaVmFlagsList = []string{ + `-XX:OnError="cat hs_err_pid%p.log"`, + "-XX:CICompilerCount=6", + "-XX:+UseDynamicNumberOfGCThreads", + } + javacVmFlagsList = []string{ + `-J-XX:OnError="cat hs_err_pid%p.log"`, + "-J-XX:CICompilerCount=6", + "-J-XX:+UseDynamicNumberOfGCThreads", + "-J-XX:+TieredCompilation", + "-J-XX:TieredStopAtLevel=1", + } ) func init() { pctx.Import("github.com/google/blueprint/bootstrap") - pctx.StaticVariable("JavacHeapSize", "2048M") - pctx.StaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}") - pctx.StaticVariable("DexFlags", "-JXX:OnError='cat hs_err_pid%p.log' -JXX:CICompilerCount=6 -JXX:+UseDynamicNumberOfGCThreads") + exportedVars.ExportStringStaticVariable("JavacHeapSize", "2048M") + exportedVars.ExportStringStaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}") + + // ErrorProne can use significantly more memory than javac alone, give it a higher heap + // size (b/221480398). + 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", + }) - pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{ + exportedVars.ExportStringListStaticVariable("CommonJdkFlags", []string{ `-Xmaxerrs 9999999`, `-encoding UTF-8`, `-sourcepath ""`, @@ -84,10 +101,10 @@ func init() { // b/65004097: prevent using java.lang.invoke.StringConcatFactory when using -target 1.9 `-XDstringConcat=inline`, - }, " ")) + }) - pctx.StaticVariable("JavaVmFlags", JavaVmFlags) - pctx.StaticVariable("JavacVmFlags", JavacVmFlags) + exportedVars.ExportStringListStaticVariable("JavaVmFlags", javaVmFlagsList) + exportedVars.ExportStringListStaticVariable("JavacVmFlags", javacVmFlagsList) pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS) @@ -99,7 +116,12 @@ func init() { if override := ctx.Config().Getenv("OVERRIDE_JLINK_VERSION_NUMBER"); override != "" { return override } - return "11" + switch ctx.Config().Getenv("EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN") { + case "true": + return "17" + default: + return "11" + } }) pctx.SourcePathVariable("JavaToolchain", "${JavaHome}/bin") @@ -178,6 +200,10 @@ func init() { hostJNIToolVariableWithSdkToolsPrebuilt("SignapkJniLibrary", "libconscrypt_openjdk_jni") } +func BazelJavaToolchainVars(config android.Config) string { + return android.BazelToolchainVars(config, exportedVars) +} + func hostBinToolVariableWithSdkToolsPrebuilt(name, tool string) { pctx.VariableFunc(name, func(ctx android.PackageVarContext) string { if ctx.Config().AlwaysUsePrebuiltSdks() { diff --git a/java/config/error_prone.go b/java/config/error_prone.go index 48681b5c9..5f853c812 100644 --- a/java/config/error_prone.go +++ b/java/config/error_prone.go @@ -16,8 +16,6 @@ package config import ( "strings" - - "android/soong/android" ) var ( @@ -31,23 +29,23 @@ var ( ) // Wrapper that grabs value of val late so it can be initialized by a later module's init function -func errorProneVar(name string, val *[]string, sep string) { - pctx.VariableFunc(name, func(android.PackageVarContext) string { +func errorProneVar(val *[]string, sep string) func() string { + return func() string { return strings.Join(*val, sep) - }) + } } func init() { - errorProneVar("ErrorProneClasspath", &ErrorProneClasspath, ":") - errorProneVar("ErrorProneChecksError", &ErrorProneChecksError, " ") - errorProneVar("ErrorProneChecksWarning", &ErrorProneChecksWarning, " ") - errorProneVar("ErrorProneChecksDefaultDisabled", &ErrorProneChecksDefaultDisabled, " ") - errorProneVar("ErrorProneChecksOff", &ErrorProneChecksOff, " ") - errorProneVar("ErrorProneFlags", &ErrorProneFlags, " ") - pctx.StaticVariable("ErrorProneChecks", strings.Join([]string{ + exportedVars.ExportVariableFuncVariable("ErrorProneClasspath", errorProneVar(&ErrorProneClasspath, ":")) + exportedVars.ExportVariableFuncVariable("ErrorProneChecksError", errorProneVar(&ErrorProneChecksError, " ")) + exportedVars.ExportVariableFuncVariable("ErrorProneChecksWarning", errorProneVar(&ErrorProneChecksWarning, " ")) + exportedVars.ExportVariableFuncVariable("ErrorProneChecksDefaultDisabled", errorProneVar(&ErrorProneChecksDefaultDisabled, " ")) + exportedVars.ExportVariableFuncVariable("ErrorProneChecksOff", errorProneVar(&ErrorProneChecksOff, " ")) + exportedVars.ExportVariableFuncVariable("ErrorProneFlags", errorProneVar(&ErrorProneFlags, " ")) + exportedVars.ExportStringListStaticVariable("ErrorProneChecks", []string{ "${ErrorProneChecksOff}", "${ErrorProneChecksError}", "${ErrorProneChecksWarning}", "${ErrorProneChecksDefaultDisabled}", - }, " ")) + }) } diff --git a/java/config/kotlin.go b/java/config/kotlin.go index 6cb61f306..fc63f4dfb 100644 --- a/java/config/kotlin.go +++ b/java/config/kotlin.go @@ -34,6 +34,7 @@ func init() { pctx.SourcePathVariable("KotlinKaptJar", "external/kotlinc/lib/kotlin-annotation-processing.jar") pctx.SourcePathVariable("KotlinAnnotationJar", "external/kotlinc/lib/annotations-13.0.jar") pctx.SourcePathVariable("KotlinStdlibJar", KotlinStdlibJar) + pctx.SourcePathVariable("KotlinAbiGenPluginJar", "external/kotlinc/lib/jvm-abi-gen.jar") // These flags silence "Illegal reflective access" warnings when running kapt in OpenJDK9+ pctx.StaticVariable("KaptSuppressJDK9Warnings", strings.Join([]string{ @@ -47,4 +48,9 @@ func init() { pctx.StaticVariable("KotlincSuppressJDK9Warnings", strings.Join([]string{ "-J--add-opens=java.base/java.util=ALL-UNNAMED", // https://youtrack.jetbrains.com/issue/KT-43704 }, " ")) + + pctx.StaticVariable("KotlincGlobalFlags", strings.Join([]string{ + // b/222162908: prevent kotlinc from reading /tmp/build.txt + "-Didea.plugins.compatible.build=999.SNAPSHOT", + }, " ")) } diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp new file mode 100644 index 000000000..cf3974601 --- /dev/null +++ b/java/core-libraries/Android.bp @@ -0,0 +1,225 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// 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. + +// +// Definitions for building the Android core libraries, i.e. ART, I18n and +// Conscrypt. +// +// These are here as the definitions are used by the build itself and include +// parts from all three of those modules. +// + +// A stubs target containing the parts of the public SDK API provided by the +// core libraries. +// +// Don't use this directly, use "sdk_version: core_current". +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +dist_targets = [ + "sdk", + "win_sdk", +] + +java_library { + name: "core.current.stubs", + visibility: ["//visibility:public"], + static_libs: [ + "art.module.public.api.stubs", + "conscrypt.module.public.api.stubs", + "i18n.module.public.api.stubs", + ], + sdk_version: "none", + system_modules: "none", + + dist: { + targets: dist_targets, + }, +} + +// Distributed with the SDK for turning into system modules to compile apps +// against. +// +// Also, produces dist files that are used by the +// prebuilts/sdk/update_prebuilts.py script to update the prebuilts/sdk +// directory. +java_library { + name: "core-current-stubs-for-system-modules", + visibility: ["//development/sdk"], + static_libs: [ + "core.current.stubs", + // This one is not on device but it's needed when javac compiles code + // containing lambdas. + "core-lambda-stubs-for-system-modules", + // This one is not on device but it's needed when javac compiles code + // containing @Generated annotations produced by some code generation + // tools. + // See http://b/123891440. + "core-generated-annotation-stubs", + ], + sdk_version: "none", + system_modules: "none", + dists: [ + { + // Legacy dist location for the public file. + dest: "core-for-system-modules.jar", + targets: dist_targets, + }, + { + dest: "system-modules/public/core-for-system-modules.jar", + targets: dist_targets, + }, + ], +} + +// Used when compiling higher-level code against core.current.stubs. +java_system_modules { + name: "core-public-stubs-system-modules", + visibility: ["//visibility:public"], + libs: [ + "core-current-stubs-for-system-modules", + ], +} + +// A stubs target containing the parts of the public SDK & @SystemApi(MODULE_LIBRARIES) API +// provided by the core libraries. +// +// Don't use this directly, use "sdk_version: module_current". +java_library { + name: "core.module_lib.stubs", + static_libs: [ + "art.module.public.api.stubs.module_lib", + + // Replace the following with the module-lib correspondence when Conscrypt or i18N module + // provides @SystemApi(MODULE_LIBRARIES). Currently, assume that only ART module provides + // @SystemApi(MODULE_LIBRARIES). + "conscrypt.module.public.api.stubs", + "i18n.module.public.api.stubs", + ], + sdk_version: "none", + system_modules: "none", + visibility: ["//visibility:private"], +} + +// Produces a dist file that is used by the +// prebuilts/sdk/update_prebuilts.py script to update the prebuilts/sdk +// directory. +java_library { + name: "core-module-lib-stubs-for-system-modules", + visibility: ["//visibility:private"], + static_libs: [ + "core.module_lib.stubs", + // This one is not on device but it's needed when javac compiles code + // containing lambdas. + "core-lambda-stubs-for-system-modules", + // This one is not on device but it's needed when javac compiles code + // containing @Generated annotations produced by some code generation + // tools. + // See http://b/123891440. + "core-generated-annotation-stubs", + ], + sdk_version: "none", + system_modules: "none", + dist: { + dest: "system-modules/module-lib/core-for-system-modules.jar", + targets: dist_targets, + }, +} + +// Used when compiling higher-level code with sdk_version "module_current" +java_system_modules { + name: "core-module-lib-stubs-system-modules", + libs: [ + "core-module-lib-stubs-for-system-modules", + ], + visibility: ["//visibility:public"], +} + +// Ideally this should be a restricted allowlist but there are hundreds of modules that depend on +// this. +// TODO(http://b/134561230) - limit the number of dependents on this. +core_platform_visibility = ["//visibility:public"] + +// Libraries containing the core platform API stubs for the core libraries. +// +// Although this stubs library is primarily used by the Java compiler / build to indicate +// the core platform API surface area, compile_dex: true is used so that the Core Platform +// API annotations are available to the dex tools that enable enforcement of runtime +// accessibility. b/119068555 +java_library { + name: "legacy.core.platform.api.stubs", + visibility: core_platform_visibility, + hostdex: true, + compile_dex: true, + + sdk_version: "none", + system_modules: "none", + static_libs: [ + "art.module.public.api.stubs.module_lib", + "conscrypt.module.platform.api.stubs", + "legacy.i18n.module.platform.api.stubs", + ], + patch_module: "java.base", +} + +java_library { + name: "stable.core.platform.api.stubs", + visibility: core_platform_visibility, + hostdex: true, + compile_dex: true, + + sdk_version: "none", + system_modules: "none", + static_libs: [ + "art.module.public.api.stubs.module_lib", + // conscrypt only has a stable version, so it is okay to depend on it here: + "conscrypt.module.platform.api.stubs", + "stable.i18n.module.platform.api.stubs", + ], + patch_module: "java.base", +} + +// Used when compiling higher-level code against *.core.platform.api.stubs. +java_system_modules { + name: "legacy-core-platform-api-stubs-system-modules", + visibility: core_platform_visibility, + libs: [ + "legacy.core.platform.api.stubs", + // This one is not on device but it's needed when javac compiles code + // containing lambdas. + "core-lambda-stubs-for-system-modules", + // This one is not on device but it's needed when javac compiles code + // containing @Generated annotations produced by some code generation + // tools. + // See http://b/123891440. + "core-generated-annotation-stubs", + ], +} + +java_system_modules { + name: "stable-core-platform-api-stubs-system-modules", + visibility: core_platform_visibility, + libs: [ + "stable.core.platform.api.stubs", + // This one is not on device but it's needed when javac compiles code + // containing lambdas. + "core-lambda-stubs-for-system-modules", + // This one is not on device but it's needed when javac compiles code + // containing @Generated annotations produced by some code generation + // tools. + // See http://b/123891440. + "core-generated-annotation-stubs", + ], +} diff --git a/java/core-libraries/OWNERS b/java/core-libraries/OWNERS new file mode 100644 index 000000000..bb3546abd --- /dev/null +++ b/java/core-libraries/OWNERS @@ -0,0 +1,3 @@ +include platform/external/icu:/OWNERS +include platform/external/conscrypt:/OWNERS +include platform/libcore:/OWNERS diff --git a/java/device_host_converter.go b/java/device_host_converter.go index 39fb04a8e..4abdcc6e9 100644 --- a/java/device_host_converter.go +++ b/java/device_host_converter.go @@ -118,7 +118,7 @@ func (d *DeviceHostConverter) GenerateAndroidBuildActions(ctx android.ModuleCont TransformJarsToJar(ctx, outputFile, "combine", d.implementationAndResourceJars, android.OptionalPath{}, false, nil, nil) d.combinedImplementationJar = outputFile - } else { + } else if len(d.implementationAndResourceJars) == 1 { d.combinedImplementationJar = d.implementationAndResourceJars[0] } @@ -127,7 +127,7 @@ func (d *DeviceHostConverter) GenerateAndroidBuildActions(ctx android.ModuleCont TransformJarsToJar(ctx, outputFile, "turbine combine", d.headerJars, android.OptionalPath{}, false, nil, []string{"META-INF/TRANSITIVE"}) d.combinedHeaderJar = outputFile - } else { + } else if len(d.headerJars) == 1 { d.combinedHeaderJar = d.headerJars[0] } @@ -174,7 +174,9 @@ func (d *DeviceHostConverter) AndroidMk() android.AndroidMkData { return android.AndroidMkData{ Class: "JAVA_LIBRARIES", OutputFile: android.OptionalPathForPath(d.combinedImplementationJar), - Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk", + // Make does not support Windows Java modules + Disabled: d.Os() == android.Windows, + Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk", Extra: []android.AndroidMkExtraFunc{ func(w io.Writer, outputFile android.Path) { fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true") diff --git a/java/dex.go b/java/dex.go index 7898e9dff..84665e7b5 100644 --- a/java/dex.go +++ b/java/dex.go @@ -32,6 +32,9 @@ type DexProperties struct { // list of module-specific flags that will be used for dex compiles Dxflags []string `android:"arch_variant"` + // A list of files containing rules that specify the classes to keep in the main dex file. + Main_dex_rules []string `android:"path"` + Optimize struct { // If false, disable all optimization. Defaults to true for android_app and android_test // modules, false for java_library and java_test modules. @@ -69,6 +72,9 @@ type DexProperties struct { // This defaults to reasonable value based on module and should not be set. // It exists only to support ART tests. Uncompress_dex *bool + + // Exclude kotlinc generate files: *.kotlin_module, *.kotlin_builtins. Defaults to false. + Exclude_kotlinc_generated_files *bool } type dexer struct { @@ -87,11 +93,14 @@ func (d *dexer) effectiveOptimizeEnabled() bool { var d8, d8RE = pctx.MultiCommandRemoteStaticRules("d8", blueprint.RuleParams{ Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` + - `$d8Template${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $in && ` + + `mkdir -p $$(dirname $tmpJar) && ` + + `${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` + + `$d8Template${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $tmpJar && ` + `$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` + - `${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`, + `${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in`, CommandDeps: []string{ "${config.D8Cmd}", + "${config.Zip2ZipCmd}", "${config.SoongZipCmd}", "${config.MergeZipsCmd}", }, @@ -110,25 +119,31 @@ var d8, d8RE = pctx.MultiCommandRemoteStaticRules("d8", ExecStrategy: "${config.RED8ExecStrategy}", Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"}, }, - }, []string{"outDir", "d8Flags", "zipFlags"}, nil) + }, []string{"outDir", "d8Flags", "zipFlags", "tmpJar", "mergeZipsFlags"}, nil) var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8", blueprint.RuleParams{ Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` + `rm -f "$outDict" && rm -rf "${outUsageDir}" && ` + `mkdir -p $$(dirname ${outUsage}) && ` + - `$r8Template${config.R8Cmd} ${config.DexFlags} -injars $in --output $outDir ` + + `mkdir -p $$(dirname $tmpJar) && ` + + `${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` + + `$r8Template${config.R8Cmd} ${config.DexFlags} -injars $tmpJar --output $outDir ` + `--no-data-resources ` + `-printmapping ${outDict} ` + `-printusage ${outUsage} ` + + `--deps-file ${out}.d ` + `$r8Flags && ` + `touch "${outDict}" "${outUsage}" && ` + `${config.SoongZipCmd} -o ${outUsageZip} -C ${outUsageDir} -f ${outUsage} && ` + `rm -rf ${outUsageDir} && ` + `$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` + - `${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`, + `${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in`, + Depfile: "${out}.d", + Deps: blueprint.DepsGCC, CommandDeps: []string{ "${config.R8Cmd}", + "${config.Zip2ZipCmd}", "${config.SoongZipCmd}", "${config.MergeZipsCmd}", }, @@ -156,15 +171,22 @@ var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8", Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"}, }, }, []string{"outDir", "outDict", "outUsage", "outUsageZip", "outUsageDir", - "r8Flags", "zipFlags"}, []string{"implicits"}) + "r8Flags", "zipFlags", "tmpJar", "mergeZipsFlags"}, []string{"implicits"}) + +func (d *dexer) dexCommonFlags(ctx android.ModuleContext, + minSdkVersion android.SdkSpec) (flags []string, deps android.Paths) { -func (d *dexer) dexCommonFlags(ctx android.ModuleContext, minSdkVersion android.SdkSpec) []string { - flags := d.dexProperties.Dxflags + flags = d.dexProperties.Dxflags // Translate all the DX flags to D8 ones until all the build files have been migrated // to D8 flags. See: b/69377755 flags = android.RemoveListFromList(flags, []string{"--core-library", "--dex", "--multi-dex"}) + for _, f := range android.PathsForModuleSrc(ctx, d.dexProperties.Main_dex_rules) { + flags = append(flags, "--main-dex-rules", f.String()) + deps = append(deps, f) + } + if ctx.Config().Getenv("NO_OPTIMIZE_DX") != "" { flags = append(flags, "--debug") } @@ -181,15 +203,15 @@ func (d *dexer) dexCommonFlags(ctx android.ModuleContext, minSdkVersion android. } flags = append(flags, "--min-api "+strconv.Itoa(effectiveVersion.FinalOrFutureInt())) - return flags + return flags, deps } func d8Flags(flags javaBuilderFlags) (d8Flags []string, d8Deps android.Paths) { d8Flags = append(d8Flags, flags.bootClasspath.FormRepeatedClassPath("--lib ")...) - d8Flags = append(d8Flags, flags.classpath.FormRepeatedClassPath("--lib ")...) + d8Flags = append(d8Flags, flags.dexClasspath.FormRepeatedClassPath("--lib ")...) d8Deps = append(d8Deps, flags.bootClasspath...) - d8Deps = append(d8Deps, flags.classpath...) + d8Deps = append(d8Deps, flags.dexClasspath...) return d8Flags, d8Deps } @@ -212,11 +234,11 @@ func (d *dexer) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Fl r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars")) r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars")) - r8Flags = append(r8Flags, flags.classpath.FormJavaClassPath("-libraryjars")) + r8Flags = append(r8Flags, flags.dexClasspath.FormJavaClassPath("-libraryjars")) r8Deps = append(r8Deps, proguardRaiseDeps...) r8Deps = append(r8Deps, flags.bootClasspath...) - r8Deps = append(r8Deps, flags.classpath...) + r8Deps = append(r8Deps, flags.dexClasspath...) flagFiles := android.Paths{ android.PathForSource(ctx, "build/make/core/proguard.flags"), @@ -238,6 +260,15 @@ func (d *dexer) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Fl if BoolDefault(opt.Proguard_compatibility, true) { r8Flags = append(r8Flags, "--force-proguard-compatibility") + } else { + // TODO(b/213833843): Allow configuration of the prefix via a build variable. + var sourceFilePrefix = "go/retraceme " + var sourceFileTemplate = "\"" + sourceFilePrefix + "%MAP_ID\"" + // TODO(b/200967150): Also tag the source file in compat builds. + if Bool(opt.Optimize) || Bool(opt.Obfuscate) { + r8Flags = append(r8Flags, "--map-id-template", "%MAP_HASH") + r8Flags = append(r8Flags, "--source-file-template", sourceFileTemplate) + } } // TODO(ccross): Don't shrink app instrumentation tests by default. @@ -273,13 +304,20 @@ func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, mi // Compile classes.jar into classes.dex and then javalib.jar javalibJar := android.PathForModuleOut(ctx, "dex", jarName).OutputPath outDir := android.PathForModuleOut(ctx, "dex") + tmpJar := android.PathForModuleOut(ctx, "withres-withoutdex", jarName) zipFlags := "--ignore_missing_files" if proptools.Bool(d.dexProperties.Uncompress_dex) { zipFlags += " -L 0" } - commonFlags := d.dexCommonFlags(ctx, minSdkVersion) + commonFlags, commonDeps := d.dexCommonFlags(ctx, minSdkVersion) + + // Exclude kotlinc generated files when "exclude_kotlinc_generated_files" is set to true. + mergeZipsFlags := "" + if proptools.BoolDefault(d.dexProperties.Exclude_kotlinc_generated_files, false) { + mergeZipsFlags = "-stripFile META-INF/*.kotlin_module -stripFile **/*.kotlin_builtins" + } useR8 := d.effectiveOptimizeEnabled() if useR8 { @@ -291,15 +329,18 @@ func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, mi proguardUsageZip := android.PathForModuleOut(ctx, "proguard_usage.zip") d.proguardUsageZip = android.OptionalPathForPath(proguardUsageZip) r8Flags, r8Deps := d.r8Flags(ctx, flags) + r8Deps = append(r8Deps, commonDeps...) rule := r8 args := map[string]string{ - "r8Flags": strings.Join(append(commonFlags, r8Flags...), " "), - "zipFlags": zipFlags, - "outDict": proguardDictionary.String(), - "outUsageDir": proguardUsageDir.String(), - "outUsage": proguardUsage.String(), - "outUsageZip": proguardUsageZip.String(), - "outDir": outDir.String(), + "r8Flags": strings.Join(append(commonFlags, r8Flags...), " "), + "zipFlags": zipFlags, + "outDict": proguardDictionary.String(), + "outUsageDir": proguardUsageDir.String(), + "outUsage": proguardUsage.String(), + "outUsageZip": proguardUsageZip.String(), + "outDir": outDir.String(), + "tmpJar": tmpJar.String(), + "mergeZipsFlags": mergeZipsFlags, } if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_R8") { rule = r8RE @@ -316,6 +357,7 @@ func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, mi }) } else { d8Flags, d8Deps := d8Flags(flags) + d8Deps = append(d8Deps, commonDeps...) rule := d8 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8") { rule = d8RE @@ -327,9 +369,11 @@ func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, mi Input: classesJar, Implicits: d8Deps, Args: map[string]string{ - "d8Flags": strings.Join(append(commonFlags, d8Flags...), " "), - "zipFlags": zipFlags, - "outDir": outDir.String(), + "d8Flags": strings.Join(append(commonFlags, d8Flags...), " "), + "zipFlags": zipFlags, + "outDir": outDir.String(), + "tmpJar": tmpJar.String(), + "mergeZipsFlags": mergeZipsFlags, }, }) } diff --git a/java/dex_test.go b/java/dex_test.go new file mode 100644 index 000000000..fbdccb65d --- /dev/null +++ b/java/dex_test.go @@ -0,0 +1,103 @@ +// Copyright 2022 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 ( + "testing" + + "android/soong/android" +) + +func TestR8(t *testing.T) { + result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, ` + android_app { + name: "app", + srcs: ["foo.java"], + libs: ["lib"], + static_libs: ["static_lib"], + platform_apis: true, + } + + java_library { + name: "lib", + srcs: ["foo.java"], + } + + java_library { + name: "static_lib", + srcs: ["foo.java"], + } + `) + + app := result.ModuleForTests("app", "android_common") + lib := result.ModuleForTests("lib", "android_common") + staticLib := result.ModuleForTests("static_lib", "android_common") + + appJavac := app.Rule("javac") + appR8 := app.Rule("r8") + libHeader := lib.Output("turbine-combined/lib.jar").Output + staticLibHeader := staticLib.Output("turbine-combined/static_lib.jar").Output + + android.AssertStringDoesContain(t, "expected lib header jar in app javac classpath", + appJavac.Args["classpath"], libHeader.String()) + android.AssertStringDoesContain(t, "expected static_lib header jar in app javac classpath", + appJavac.Args["classpath"], staticLibHeader.String()) + + android.AssertStringDoesContain(t, "expected lib header jar in app r8 classpath", + appR8.Args["r8Flags"], libHeader.String()) + android.AssertStringDoesNotContain(t, "expected no static_lib header jar in app javac classpath", + appR8.Args["r8Flags"], staticLibHeader.String()) +} + +func TestD8(t *testing.T) { + result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, ` + java_library { + name: "foo", + srcs: ["foo.java"], + libs: ["lib"], + static_libs: ["static_lib"], + installable: true, + } + + java_library { + name: "lib", + srcs: ["foo.java"], + } + + java_library { + name: "static_lib", + srcs: ["foo.java"], + } + `) + + foo := result.ModuleForTests("foo", "android_common") + lib := result.ModuleForTests("lib", "android_common") + staticLib := result.ModuleForTests("static_lib", "android_common") + + fooJavac := foo.Rule("javac") + fooD8 := foo.Rule("d8") + libHeader := lib.Output("turbine-combined/lib.jar").Output + staticLibHeader := staticLib.Output("turbine-combined/static_lib.jar").Output + + android.AssertStringDoesContain(t, "expected lib header jar in foo javac classpath", + fooJavac.Args["classpath"], libHeader.String()) + android.AssertStringDoesContain(t, "expected static_lib header jar in foo javac classpath", + fooJavac.Args["classpath"], staticLibHeader.String()) + + android.AssertStringDoesContain(t, "expected lib header jar in foo d8 classpath", + fooD8.Args["d8Flags"], libHeader.String()) + android.AssertStringDoesNotContain(t, "expected no static_lib header jar in foo javac classpath", + fooD8.Args["d8Flags"], staticLibHeader.String()) +} diff --git a/java/dexpreopt.go b/java/dexpreopt.go index 2e46d74fa..7c5f055e6 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -15,13 +15,65 @@ package java import ( + "path/filepath" + "strings" + "android/soong/android" "android/soong/dexpreopt" ) -type dexpreopterInterface interface { +type DexpreopterInterface interface { IsInstallable() bool // Structs that embed dexpreopter must implement this. dexpreoptDisabled(ctx android.BaseModuleContext) bool + DexpreoptBuiltInstalledForApex() []dexpreopterInstall + AndroidMkEntriesForApex() []android.AndroidMkEntries +} + +type dexpreopterInstall struct { + // A unique name to distinguish an output from others for the same java library module. Usually in + // the form of `<arch>-<encoded-path>.odex/vdex/art`. + name string + + // The name of the input java module. + moduleName string + + // The path to the dexpreopt output on host. + outputPathOnHost android.Path + + // The directory on the device for the output to install to. + installDirOnDevice android.InstallPath + + // The basename (the last segment of the path) for the output to install as. + installFileOnDevice string +} + +// The full module name of the output in the makefile. +func (install *dexpreopterInstall) FullModuleName() string { + return install.moduleName + install.SubModuleName() +} + +// The sub-module name of the output in the makefile (the name excluding the java module name). +func (install *dexpreopterInstall) SubModuleName() string { + return "-dexpreopt-" + install.name +} + +// 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 { @@ -33,13 +85,16 @@ type dexpreopter struct { isApp bool isTest bool isPresignedPrebuilt bool + preventInstall bool manifestFile android.Path statusFile android.WritablePath enforceUsesLibs bool classLoaderContexts dexpreopt.ClassLoaderContextMap - builtInstalled string + // See the `dexpreopt` function for details. + builtInstalled string + builtInstalledForApex []dexpreopterInstall // The config is used for two purposes: // - Passing dexpreopt information about libraries from Soong to Make. This is needed when @@ -74,14 +129,24 @@ func init() { dexpreopt.DexpreoptRunningInSoong = true } -func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool { - global := dexpreopt.GetGlobalConfig(ctx) +func isApexVariant(ctx android.BaseModuleContext) bool { + apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + return !apexInfo.IsForPlatform() +} - if global.DisablePreopt { - return true - } +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()) +} - if inList(ctx.ModuleName(), global.DisablePreoptModules) { +func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool { + if !ctx.Device() { return true } @@ -93,36 +158,80 @@ func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool { return true } - if !ctx.Module().(dexpreopterInterface).IsInstallable() { + // 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 ctx.Host() { + if !android.IsModulePreferred(ctx.Module()) { return true } - // Don't preopt APEX variant module - if apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo); !apexInfo.IsForPlatform() { + 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) { + if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) { return } dexpreopt.RegisterToolDeps(ctx) } -func odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool { - return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx)) +func (d *dexpreopter) odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool { + return dexpreopt.OdexOnSystemOtherByName(moduleName(ctx), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx)) +} + +// Returns the install path of the dex jar of a module. +// +// Do not rely on `ApexInfo.ApexVariationName` because it can be something like "apex1000", rather +// than the `name` in the path `/apex/<name>` as suggested in its comment. +// +// This function is on a best-effort basis. It cannot handle the case where an APEX jar is not a +// system server jar, which is fine because we currently only preopt system server jars for APEXes. +func (d *dexpreopter) getInstallPath( + ctx android.ModuleContext, defaultInstallPath android.InstallPath) android.InstallPath { + global := dexpreopt.GetGlobalConfig(ctx) + if global.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 @@ -133,7 +242,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath) - providesUsesLib := ctx.ModuleName() + providesUsesLib := moduleName(ctx) if ulib, ok := ctx.Module().(ProvidesUsesLib); ok { name := ulib.ProvidesUsesLib() if name != nil { @@ -141,24 +250,20 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr } } - // If it is neither app nor test, make config files regardless of its dexpreopt setting. + // 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. - // TODO(b/158843648): The config for apps should be generated as well regardless of setting. - if (d.isApp || d.isTest) && d.dexpreoptDisabled(ctx) { + if d.isTest && d.dexpreoptDisabled(ctx) { return } - global := dexpreopt.GetGlobalConfig(ctx) - - isSystemServerJar := global.SystemServerJars.ContainsJar(ctx.ModuleName()) + isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(moduleName(ctx)) bootImage := defaultBootImageConfig(ctx) if global.UseArtImage { bootImage = artBootImageConfig(ctx) } - // System server jars are an exception: they are dexpreopted without updatable bootclasspath. - dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp && !isSystemServerJar) + dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp) targets := ctx.MultiTargets() if len(targets) == 0 { @@ -200,15 +305,15 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr profileIsTextListing = true } else if global.ProfileDir != "" { profileClassListing = android.ExistentPathForSource(ctx, - global.ProfileDir, ctx.ModuleName()+".prof") + global.ProfileDir, moduleName(ctx)+".prof") } } // Full dexpreopt config, used to create dexpreopt build rules. dexpreoptConfig := &dexpreopt.ModuleConfig{ - Name: ctx.ModuleName(), + Name: moduleName(ctx), DexLocation: dexLocation, - BuildPath: android.PathForModuleOut(ctx, "dexpreopt", ctx.ModuleName()+".jar").OutputPath, + BuildPath: android.PathForModuleOut(ctx, "dexpreopt", moduleName(ctx)+".jar").OutputPath, DexPath: dexJarFile, ManifestPath: android.OptionalPathForPath(d.manifestFile), UncompressedDex: d.uncompressedDex, @@ -257,5 +362,47 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr dexpreoptRule.Build("dexpreopt", "dexpreopt") - d.builtInstalled = dexpreoptRule.Installs().String() + 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 19c65cac7..3d91aec91 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -16,7 +16,6 @@ package java import ( "path/filepath" - "sort" "strings" "android/soong/android" @@ -154,12 +153,23 @@ import ( // PRODUCT_BOOT_JARS_EXTRA variables. The AOSP makefiles specify some common Framework libraries, // but more product-specific libraries can be added in the product makefiles. // -// Each component of the PRODUCT_BOOT_JARS and PRODUCT_BOOT_JARS_EXTRA variables is either a simple -// name (if the library is a part of the Platform), or a colon-separated pair <apex, name> (if the -// library is a part of a non-updatable APEX). +// 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. // -// A related variable PRODUCT_UPDATABLE_BOOT_JARS contains bootclasspath libraries that are in -// updatable APEXes. They are not included in the boot image. +// 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 @@ -246,6 +256,10 @@ type bootImageConfig struct { // 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 @@ -262,8 +276,17 @@ type bootImageConfig struct { // 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. @@ -296,10 +319,16 @@ type bootImageVariant struct { // 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. + // 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. @@ -371,6 +400,11 @@ func (image *bootImageConfig) apexVariants() []*bootImageVariant { 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 @@ -489,7 +523,21 @@ func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, srcBootDexJars dst := dstBootJarsByModule[name] if src == nil { - ctx.ModuleErrorf("module %s does not provide a dex boot jar", name) + // 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 { @@ -512,14 +560,14 @@ func buildBootImageVariantsForAndroidOs(ctx android.ModuleContext, image *bootIm } // buildBootImageVariantsForBuildOs generates rules to build the boot image variants for the -// android.BuildOs OsType, i.e. the type of OS on which the build is being running. +// 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, android.BuildOs) + buildBootImageForOsType(ctx, image, profile, ctx.Config().BuildOS) } // buildBootImageForOsType takes a bootImageConfig, a profile file and an android.OsType @@ -611,7 +659,6 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p Flag("--runtime-arg").FlagWithArg("-Xmx", global.Dex2oatImageXmx) if profile != nil { - cmd.FlagWithArg("--compiler-filter=", "speed-profile") cmd.FlagWithInput("--profile-file=", profile) } @@ -642,6 +689,13 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p 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). @@ -680,6 +734,7 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p var vdexInstalls android.RuleBuilderInstalls var unstrippedInstalls android.RuleBuilderInstalls + var deviceInstalls android.RuleBuilderInstalls for _, artOrOat := range image.moduleFiles(ctx, outputDir, ".art", ".oat") { cmd.ImplicitOutput(artOrOat) @@ -705,12 +760,22 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p 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. @@ -756,11 +821,15 @@ func bootImageProfileRule(ctx android.ModuleContext, image *bootImageConfig) and FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps). FlagWithOutput("--reference-profile-file=", profile) - rule.Install(profile, "/system/etc/boot-image.prof") + 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.profileInstalls = append(image.profileInstalls, rule.Installs()...) + image.profilePathOnHost = profile return profile } @@ -793,44 +862,11 @@ func bootFrameworkProfileRule(ctx android.ModuleContext, image *bootImageConfig) 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 } -// generateUpdatableBcpPackagesRule generates the rule to create the updatable-bcp-packages.txt file -// and returns a path to the generated file. -func generateUpdatableBcpPackagesRule(ctx android.ModuleContext, image *bootImageConfig, updatableModules []android.Module) android.WritablePath { - // Collect `permitted_packages` for updatable boot jars. - var updatablePackages []string - for _, module := range updatableModules { - if j, ok := module.(PermittedPackagesForUpdatableBootJars); ok { - pp := j.PermittedPackagesForUpdatableBootJars() - if len(pp) > 0 { - updatablePackages = append(updatablePackages, pp...) - } else { - ctx.OtherModuleErrorf(module, "Missing permitted_packages") - } - } - } - - // Sort updatable packages to ensure deterministic ordering. - sort.Strings(updatablePackages) - - updatableBcpPackagesName := "updatable-bcp-packages.txt" - updatableBcpPackages := image.dir.Join(ctx, updatableBcpPackagesName) - - // WriteFileRule automatically adds the last end-of-line. - android.WriteFileRule(ctx, updatableBcpPackages, strings.Join(updatablePackages, "\n")) - - rule := android.NewRuleBuilder(pctx, ctx) - rule.Install(updatableBcpPackages, "/system/etc/"+updatableBcpPackagesName) - // TODO: Rename `profileInstalls` to `extraInstalls`? - // Maybe even move the field out of the bootImageConfig into some higher level type? - image.profileInstalls = append(image.profileInstalls, rule.Installs()...) - - return updatableBcpPackages -} - func dumpOatRules(ctx android.ModuleContext, image *bootImageConfig) { var allPhonies android.Paths for _, image := range image.variants { @@ -892,6 +928,9 @@ func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) { image := d.defaultBootImage if image != nil { 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) @@ -917,6 +956,9 @@ func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) { 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 := current.getAnyAndroidVariant().imageLocations() ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_HOST"+current.name, strings.Join(imageLocationsOnHost, ":")) diff --git a/java/dexpreopt_check.go b/java/dexpreopt_check.go new file mode 100644 index 000000000..83c088cd4 --- /dev/null +++ b/java/dexpreopt_check.go @@ -0,0 +1,96 @@ +// Copyright 2021 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 ( + "strings" + + "android/soong/android" + "android/soong/dexpreopt" + + "github.com/google/blueprint/pathtools" +) + +func init() { + RegisterDexpreoptCheckBuildComponents(android.InitRegistrationContext) +} + +func RegisterDexpreoptCheckBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterSingletonModuleType("dexpreopt_systemserver_check", dexpreoptSystemserverCheckFactory) +} + +// A build-time check to verify if all compilation artifacts of system server jars are installed +// into the system image. When the check fails, it means that dexpreopting is not working for some +// system server jars and needs to be fixed. +// This singleton module generates a list of the paths to the artifacts based on +// PRODUCT_SYSTEM_SERVER_JARS and PRODUCT_APEX_SYSTEM_SERVER_JARS, and passes it to Make via a +// variable. Make will then do the actual check. +// Currently, it only checks artifacts of modules defined in Soong. Artifacts of modules defined in +// Makefile are generated by a script generated by dexpreopt_gen, and their existence is unknown to +// Make and Ninja. +type dexpreoptSystemserverCheck struct { + android.SingletonModuleBase + + // Mapping from the module name to the install paths to the compilation artifacts. + artifactsByModuleName map[string][]string + + // The install paths to the compilation artifacts. + artifacts []string +} + +func dexpreoptSystemserverCheckFactory() android.SingletonModule { + m := &dexpreoptSystemserverCheck{} + m.artifactsByModuleName = make(map[string][]string) + android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) + return m +} + +func getInstallPath(ctx android.ModuleContext, location string) android.InstallPath { + return android.PathForModuleInPartitionInstall( + ctx, "", strings.TrimPrefix(location, "/")) +} + +func (m *dexpreoptSystemserverCheck) GenerateAndroidBuildActions(ctx android.ModuleContext) { + global := dexpreopt.GetGlobalConfig(ctx) + targets := ctx.Config().Targets[android.Android] + + // The check should be skipped on unbundled builds because system server jars are not preopted on + // unbundled builds since the artifacts are installed into the system image, not the APEXes. + if global.DisablePreopt || len(targets) == 0 || ctx.Config().UnbundledBuild() { + return + } + + systemServerJars := global.AllSystemServerJars(ctx) + for _, jar := range systemServerJars.CopyOfJars() { + dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, jar) + odexLocation := dexpreopt.ToOdexPath(dexLocation, targets[0].Arch.ArchType) + odexPath := getInstallPath(ctx, odexLocation) + vdexPath := getInstallPath(ctx, pathtools.ReplaceExtension(odexLocation, "vdex")) + m.artifactsByModuleName[jar] = []string{odexPath.String(), vdexPath.String()} + } +} + +func (m *dexpreoptSystemserverCheck) GenerateSingletonBuildActions(ctx android.SingletonContext) { + // Only keep modules defined in Soong. + ctx.VisitAllModules(func(module android.Module) { + if artifacts, ok := m.artifactsByModuleName[module.Name()]; ok { + m.artifacts = append(m.artifacts, artifacts...) + } + }) +} + +func (m *dexpreoptSystemserverCheck) MakeVars(ctx android.MakeVarsContext) { + ctx.Strict("DEXPREOPT_SYSTEMSERVER_ARTIFACTS", strings.Join(m.artifacts, " ")) +} diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go index b13955fba..4d0bd09c6 100644 --- a/java/dexpreopt_config.go +++ b/java/dexpreopt_config.go @@ -32,7 +32,7 @@ func dexpreoptTargets(ctx android.PathContext) []android.Target { } } // We may also need the images on host in order to run host-based tests. - for _, target := range ctx.Config().Targets[android.BuildOs] { + for _, target := range ctx.Config().Targets[ctx.Config().BuildOS] { targets = append(targets, target) } @@ -41,50 +41,59 @@ func dexpreoptTargets(ctx android.PathContext) []android.Target { var ( bootImageConfigKey = android.NewOnceKey("bootImageConfig") + bootImageConfigRawKey = android.NewOnceKey("bootImageConfigRaw") artBootImageName = "art" frameworkBootImageName = "boot" ) -// Construct the global boot image configs. -func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig { - return ctx.Config().Once(bootImageConfigKey, func() interface{} { - +func genBootImageConfigRaw(ctx android.PathContext) map[string]*bootImageConfig { + return ctx.Config().Once(bootImageConfigRawKey, func() interface{} { global := dexpreopt.GetGlobalConfig(ctx) - targets := dexpreoptTargets(ctx) - deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName()) artModules := global.ArtApexJars frameworkModules := global.BootJars.RemoveList(artModules) - artDirOnHost := "apex/art_boot_images/javalib" - artDirOnDevice := "apex/com.android.art/javalib" - frameworkSubdir := "system/framework" - // ART config for the primary boot image in the ART apex. // It includes the Core Libraries. artCfg := bootImageConfig{ - name: artBootImageName, - stem: "boot", - installDirOnHost: artDirOnHost, - installDirOnDevice: artDirOnDevice, - modules: artModules, + 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, + extends: &artCfg, + name: frameworkBootImageName, + stem: "boot", + installDirOnHost: frameworkSubdir, + installDirOnDevice: frameworkSubdir, + modules: frameworkModules, + preloadedClassesFile: "frameworks/base/config/preloaded-classes", } - configs := map[string]*bootImageConfig{ + 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 { @@ -142,14 +151,14 @@ func defaultBootImageConfig(ctx android.PathContext) *bootImageConfig { return genBootImageConfigs(ctx)[frameworkBootImageName] } -// Updatable boot config allows to access build/install paths of updatable boot jars without going +// 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 updatableBootConfig struct { - // A list of updatable boot jars. +type apexBootConfig struct { + // A list of apex boot jars. modules android.ConfiguredJarList - // A list of predefined build paths to updatable boot jars. They are configured very early, + // 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 @@ -161,21 +170,21 @@ type updatableBootConfig struct { dexLocations []string } -var updatableBootConfigKey = android.NewOnceKey("updatableBootConfig") +var updatableBootConfigKey = android.NewOnceKey("apexBootConfig") -// Returns updatable boot config. -func GetUpdatableBootConfig(ctx android.PathContext) updatableBootConfig { +// Returns apex boot config. +func GetApexBootConfig(ctx android.PathContext) apexBootConfig { return ctx.Config().Once(updatableBootConfigKey, func() interface{} { - updatableBootJars := dexpreopt.GetGlobalConfig(ctx).UpdatableBootJars + apexBootJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars - dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "updatable_bootjars") - dexPaths := updatableBootJars.BuildPaths(ctx, dir) - dexPathsByModuleName := updatableBootJars.BuildPathsByModule(ctx, dir) + dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "apex_bootjars") + dexPaths := apexBootJars.BuildPaths(ctx, dir) + dexPathsByModuleName := apexBootJars.BuildPathsByModule(ctx, dir) - dexLocations := updatableBootJars.DevicePaths(ctx.Config(), android.Android) + dexLocations := apexBootJars.DevicePaths(ctx.Config(), android.Android) - return updatableBootConfig{updatableBootJars, dexPaths, dexPathsByModuleName, dexLocations} - }).(updatableBootConfig) + 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 @@ -188,10 +197,10 @@ func bcpForDexpreopt(ctx android.PathContext, withUpdatable bool) (android.Writa dexLocations := bootImage.getAnyAndroidVariant().dexLocationsDeps if withUpdatable { - // Updatable boot jars (they are used only in dexpreopt, but not in the boot image). - updBootConfig := GetUpdatableBootConfig(ctx) - dexPaths = append(dexPaths, updBootConfig.dexPaths...) - dexLocations = append(dexLocations, updBootConfig.dexLocations...) + // 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 diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go index b25deceac..1c1070add 100644 --- a/java/dexpreopt_test.go +++ b/java/dexpreopt_test.go @@ -16,6 +16,8 @@ package java import ( "fmt" + "runtime" + "strings" "testing" "android/soong/android" @@ -23,11 +25,17 @@ import ( "android/soong/dexpreopt" ) +func init() { + RegisterFakeRuntimeApexMutator() +} + func TestDexpreoptEnabled(t *testing.T) { tests := []struct { - name string - bp string - enabled bool + name string + bp string + moduleName string + apexVariant bool + enabled bool }{ { name: "app", @@ -147,13 +155,81 @@ func TestDexpreoptEnabled(t *testing.T) { }`, enabled: true, }, + { + name: "apex variant", + bp: ` + java_library { + name: "foo", + installable: true, + srcs: ["a.java"], + apex_available: ["com.android.apex1"], + }`, + apexVariant: true, + enabled: false, + }, + { + name: "apex variant of apex system server jar", + bp: ` + java_library { + name: "service-foo", + installable: true, + srcs: ["a.java"], + apex_available: ["com.android.apex1"], + }`, + moduleName: "service-foo", + apexVariant: true, + enabled: true, + }, + { + name: "apex variant of prebuilt apex system server jar", + bp: ` + java_library { + name: "prebuilt_service-foo", + installable: true, + srcs: ["a.java"], + apex_available: ["com.android.apex1"], + }`, + moduleName: "prebuilt_service-foo", + apexVariant: true, + enabled: true, + }, + { + name: "platform variant of apex system server jar", + bp: ` + java_library { + name: "service-foo", + installable: true, + srcs: ["a.java"], + apex_available: ["com.android.apex1"], + }`, + moduleName: "service-foo", + apexVariant: false, + enabled: false, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - ctx, _ := testJava(t, test.bp) + preparers := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + PrepareForTestWithFakeApexMutator, + dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"), + ) + + result := preparers.RunTestWithBp(t, test.bp) + ctx := result.TestContext + + moduleName := "foo" + if test.moduleName != "" { + moduleName = test.moduleName + } + + variant := "android_common" + if test.apexVariant { + variant += "_apex1000" + } - dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeRule("dexpreopt") + dexpreopt := ctx.ModuleForTests(moduleName, variant).MaybeRule("dexpreopt") enabled := dexpreopt.Rule != nil if enabled != test.enabled { @@ -173,9 +249,9 @@ func enabledString(enabled bool) string { } func TestDex2oatToolDeps(t *testing.T) { - if android.BuildOs != android.Linux { + if runtime.GOOS != "linux" { // The host binary paths checked below are build OS dependent. - t.Skipf("Unsupported build OS %s", android.BuildOs) + t.Skipf("Unsupported build OS %s", runtime.GOOS) } preparers := android.GroupFixturePreparers( @@ -219,3 +295,145 @@ func TestDex2oatToolDeps(t *testing.T) { testDex2oatToolDep(true, true, true, prebuiltDex2oatPath) testDex2oatToolDep(false, true, false, prebuiltDex2oatPath) } + +func TestDexpreoptBuiltInstalledForApex(t *testing.T) { + preparers := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + PrepareForTestWithFakeApexMutator, + dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"), + ) + + // An APEX system server jar. + result := preparers.RunTestWithBp(t, ` + java_library { + name: "service-foo", + installable: true, + srcs: ["a.java"], + apex_available: ["com.android.apex1"], + }`) + ctx := result.TestContext + module := ctx.ModuleForTests("service-foo", "android_common_apex1000") + library := module.Module().(*Library) + + installs := library.dexpreopter.DexpreoptBuiltInstalledForApex() + + android.AssertIntEquals(t, "install count", 2, len(installs)) + + android.AssertStringEquals(t, "installs[0] FullModuleName", + "service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex", + installs[0].FullModuleName()) + + android.AssertStringEquals(t, "installs[0] SubModuleName", + "-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex", + installs[0].SubModuleName()) + + android.AssertStringEquals(t, "installs[1] FullModuleName", + "service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex", + installs[1].FullModuleName()) + + android.AssertStringEquals(t, "installs[1] SubModuleName", + "-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex", + installs[1].SubModuleName()) + + // Not an APEX system server jar. + result = preparers.RunTestWithBp(t, ` + java_library { + name: "foo", + installable: true, + srcs: ["a.java"], + }`) + ctx = result.TestContext + module = ctx.ModuleForTests("foo", "android_common") + library = module.Module().(*Library) + + installs = library.dexpreopter.DexpreoptBuiltInstalledForApex() + + android.AssertIntEquals(t, "install count", 0, len(installs)) +} + +func filterDexpreoptEntriesList(entriesList []android.AndroidMkEntries) []android.AndroidMkEntries { + var results []android.AndroidMkEntries + for _, entries := range entriesList { + if strings.Contains(entries.EntryMap["LOCAL_MODULE"][0], "-dexpreopt-") { + results = append(results, entries) + } + } + return results +} + +func verifyEntries(t *testing.T, message string, expectedModule string, + expectedPrebuiltModuleFile string, expectedModulePath string, expectedInstalledModuleStem string, + entries android.AndroidMkEntries) { + android.AssertStringEquals(t, message+" LOCAL_MODULE", expectedModule, + entries.EntryMap["LOCAL_MODULE"][0]) + + android.AssertStringEquals(t, message+" LOCAL_MODULE_CLASS", "ETC", + entries.EntryMap["LOCAL_MODULE_CLASS"][0]) + + android.AssertStringDoesContain(t, message+" LOCAL_PREBUILT_MODULE_FILE", + entries.EntryMap["LOCAL_PREBUILT_MODULE_FILE"][0], expectedPrebuiltModuleFile) + + android.AssertStringDoesContain(t, message+" LOCAL_MODULE_PATH", + entries.EntryMap["LOCAL_MODULE_PATH"][0], expectedModulePath) + + android.AssertStringEquals(t, message+" LOCAL_INSTALLED_MODULE_STEM", + expectedInstalledModuleStem, entries.EntryMap["LOCAL_INSTALLED_MODULE_STEM"][0]) + + android.AssertStringEquals(t, message+" LOCAL_NOT_AVAILABLE_FOR_PLATFORM", + "false", entries.EntryMap["LOCAL_NOT_AVAILABLE_FOR_PLATFORM"][0]) +} + +func TestAndroidMkEntriesForApex(t *testing.T) { + preparers := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + PrepareForTestWithFakeApexMutator, + dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"), + ) + + // An APEX system server jar. + result := preparers.RunTestWithBp(t, ` + java_library { + name: "service-foo", + installable: true, + srcs: ["a.java"], + apex_available: ["com.android.apex1"], + }`) + ctx := result.TestContext + module := ctx.ModuleForTests("service-foo", "android_common_apex1000") + + entriesList := android.AndroidMkEntriesForTest(t, ctx, module.Module()) + entriesList = filterDexpreoptEntriesList(entriesList) + + android.AssertIntEquals(t, "entries count", 2, len(entriesList)) + + verifyEntries(t, + "entriesList[0]", + "service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex", + "/dexpreopt/oat/arm64/javalib.odex", + "/system/framework/oat/arm64", + "apex@com.android.apex1@javalib@service-foo.jar@classes.odex", + entriesList[0]) + + verifyEntries(t, + "entriesList[1]", + "service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex", + "/dexpreopt/oat/arm64/javalib.vdex", + "/system/framework/oat/arm64", + "apex@com.android.apex1@javalib@service-foo.jar@classes.vdex", + entriesList[1]) + + // Not an APEX system server jar. + result = preparers.RunTestWithBp(t, ` + java_library { + name: "foo", + installable: true, + srcs: ["a.java"], + }`) + ctx = result.TestContext + module = ctx.ModuleForTests("foo", "android_common") + + entriesList = android.AndroidMkEntriesForTest(t, ctx, module.Module()) + entriesList = filterDexpreoptEntriesList(entriesList) + + android.AssertIntEquals(t, "entries count", 0, len(entriesList)) +} diff --git a/java/droiddoc.go b/java/droiddoc.go index 869a5982d..023d61912 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -330,7 +330,7 @@ func (j *Javadoc) genSources(ctx android.ModuleContext, srcFiles android.Paths, // Process all aidl files together to support sharding them into one or more rules that produce srcjars. if len(aidlSrcs) > 0 { - srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, flags.aidlDeps) + srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, nil, flags.aidlDeps) outSrcFiles = append(outSrcFiles, srcJarFiles...) } @@ -769,8 +769,8 @@ func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) { d.Javadoc.docZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"docs.zip") - jsilver := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "jsilver.jar") - doclava := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "doclava.jar") + jsilver := ctx.Config().HostJavaToolPath(ctx, "jsilver.jar") + doclava := ctx.Config().HostJavaToolPath(ctx, "doclava.jar") outDir := android.PathForModuleOut(ctx, "out") srcJarDir := android.PathForModuleOut(ctx, "srcjars") diff --git a/java/droidstubs.go b/java/droidstubs.go index c756815c6..3b1f7c041 100644 --- a/java/droidstubs.go +++ b/java/droidstubs.go @@ -16,6 +16,7 @@ package java import ( "fmt" + "path/filepath" "strings" "github.com/google/blueprint/proptools" @@ -25,6 +26,9 @@ import ( "android/soong/remoteexec" ) +// The values allowed for Droidstubs' Api_levels_sdk_type +var allowedApiLevelSdkTypes = []string{"public", "system", "module-lib"} + func init() { RegisterStubsBuildComponents(android.InitRegistrationContext) } @@ -128,12 +132,15 @@ type DroidstubsProperties struct { // whicih can be used for scheduling purposes High_mem *bool - // is set to true, Metalava will allow framework SDK to contain API levels annotations. + // if set to true, Metalava will allow framework SDK to contain API levels annotations. Api_levels_annotations_enabled *bool // the dirs which Metalava extracts API levels annotations from. Api_levels_annotations_dirs []string + // the sdk kind which Metalava extracts API levels annotations from. Supports 'public', 'system' and 'module-lib' for now; defaults to public. + Api_levels_sdk_type *string + // the filename which Metalava extracts API levels annotations from. Defaults to android.jar. Api_levels_jar_filename *string @@ -153,6 +160,7 @@ type ApiStubsSrcProvider interface { // Provider of information about API stubs, used by java_sdk_library. type ApiStubsProvider interface { + AnnotationsZip() android.Path ApiFilePath RemovedApiFilePath() android.Path @@ -207,6 +215,10 @@ func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) { } } +func (d *Droidstubs) AnnotationsZip() android.Path { + return d.annotationsZip +} + func (d *Droidstubs) ApiFilePath() android.Path { return d.apiFilePath } @@ -322,7 +334,11 @@ func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.Ru // TODO(tnorbye): find owners to fix these warnings when annotation was enabled. cmd.FlagWithArg("--hide ", "HiddenTypedefConstant"). FlagWithArg("--hide ", "SuperfluousPrefix"). - FlagWithArg("--hide ", "AnnotationExtraction") + FlagWithArg("--hide ", "AnnotationExtraction"). + // b/222738070 + FlagWithArg("--hide ", "BannedThrow"). + // b/223382732 + FlagWithArg("--hide ", "ChangedDefault") } } @@ -367,6 +383,7 @@ func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *a filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar") + var dirs []string ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) { if t, ok := m.(*ExportedDroiddocDir); ok { for _, dep := range t.deps { @@ -383,12 +400,41 @@ func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *a cmd.Implicit(dep) } } - cmd.FlagWithArg("--android-jar-pattern ", t.dir.String()+"/%/public/"+filename) + + dirs = append(dirs, t.dir.String()) } else { ctx.PropertyErrorf("api_levels_annotations_dirs", "module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m)) } }) + + // Add all relevant --android-jar-pattern patterns for Metalava. + // When parsing a stub jar for a specific version, Metalava picks the first pattern that defines + // an actual file present on disk (in the order the patterns were passed). For system APIs for + // privileged apps that are only defined since API level 21 (Lollipop), fallback to public stubs + // for older releases. Similarly, module-lib falls back to system API. + var sdkDirs []string + switch proptools.StringDefault(d.properties.Api_levels_sdk_type, "public") { + case "module-lib": + sdkDirs = []string{"module-lib", "system", "public"} + case "system": + sdkDirs = []string{"system", "public"} + case "public": + sdkDirs = []string{"public"} + default: + ctx.PropertyErrorf("api_levels_sdk_type", "needs to be one of %v", allowedApiLevelSdkTypes) + return + } + + for _, sdkDir := range sdkDirs { + for _, dir := range dirs { + cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, filename)) + } + } +} + +func metalavaUseRbe(ctx android.ModuleContext) bool { + return ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA") } func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths, @@ -399,7 +445,7 @@ func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersi cmd := rule.Command() cmd.FlagWithArg("ANDROID_PREFS_ROOT=", homeDir.String()) - if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA") { + if metalavaUseRbe(ctx) { rule.Remoteable(android.RemoteRuleSupports{RBE: true}) execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy) labels := map[string]string{"type": "tool", "name": "metalava"} @@ -434,7 +480,10 @@ func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersi Flag("--quiet"). Flag("--format=v2"). FlagWithArg("--repeat-errors-max ", "10"). - FlagWithArg("--hide ", "UnresolvedImport") + FlagWithArg("--hide ", "UnresolvedImport"). + FlagWithArg("--hide ", "InvalidNullabilityOverride"). + // b/223382732 + FlagWithArg("--hide ", "ChangedDefault") return cmd } @@ -530,7 +579,8 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { `\n` + `If it is not possible to do so, there are workarounds:\n` + `\n` + - `1. You can suppress the errors with @SuppressLint("<id>")\n` + `1. You can suppress the errors with @SuppressLint("<id>")\n` + + ` where the <id> is given in brackets in the error message above.\n` if baselineFile.Valid() { cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path()) @@ -619,7 +669,9 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { } // TODO(b/183630617): rewrapper doesn't support restat rules - // rule.Restat() + if !metalavaUseRbe(ctx) { + rule.Restat() + } zipSyncCleanupCmd(rule, srcJarDir) @@ -767,7 +819,7 @@ type PrebuiltStubsSources struct { properties PrebuiltStubsSourcesProperties - stubsSrcJar android.ModuleOutPath + stubsSrcJar android.Path } func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) { @@ -784,35 +836,39 @@ func (d *PrebuiltStubsSources) StubsSrcJar() android.Path { } func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) { - p.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar") - if len(p.properties.Srcs) != 1 { - ctx.PropertyErrorf("srcs", "must only specify one directory path, contains %d paths", len(p.properties.Srcs)) + ctx.PropertyErrorf("srcs", "must only specify one directory path or srcjar, contains %d paths", len(p.properties.Srcs)) return } - localSrcDir := p.properties.Srcs[0] - // Although PathForModuleSrc can return nil if either the path doesn't exist or - // the path components are invalid it won't in this case because no components - // are specified and the module directory must exist in order to get this far. - srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, localSrcDir) - - // Glob the contents of the directory just in case the directory does not exist. - srcGlob := localSrcDir + "/**/*" - srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob}) + src := p.properties.Srcs[0] + if filepath.Ext(src) == ".srcjar" { + // This is a srcjar. We can use it directly. + p.stubsSrcJar = android.PathForModuleSrc(ctx, src) + } else { + outPath := android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar") - rule := android.NewRuleBuilder(pctx, ctx) - rule.Command(). - BuiltTool("soong_zip"). - Flag("-write_if_changed"). - Flag("-jar"). - FlagWithOutput("-o ", p.stubsSrcJar). - FlagWithArg("-C ", srcDir.String()). - FlagWithRspFileInputList("-r ", p.stubsSrcJar.ReplaceExtension(ctx, "rsp"), srcPaths) + // This is a directory. Glob the contents just in case the directory does not exist. + srcGlob := src + "/**/*" + srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob}) - rule.Restat() + // Although PathForModuleSrc can return nil if either the path doesn't exist or + // the path components are invalid it won't in this case because no components + // are specified and the module directory must exist in order to get this far. + srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, src) - rule.Build("zip src", "Create srcjar from prebuilt source") + rule := android.NewRuleBuilder(pctx, ctx) + rule.Command(). + BuiltTool("soong_zip"). + Flag("-write_if_changed"). + Flag("-jar"). + FlagWithOutput("-o ", outPath). + FlagWithArg("-C ", srcDir.String()). + FlagWithRspFileInputList("-r ", outPath.ReplaceExtension(ctx, "rsp"), srcPaths) + rule.Restat() + rule.Build("zip src", "Create srcjar from prebuilt source") + p.stubsSrcJar = outPath + } } func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt { diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go index db664c15e..10d99f3a5 100644 --- a/java/droidstubs_test.go +++ b/java/droidstubs_test.go @@ -15,7 +15,9 @@ package java import ( + "fmt" "reflect" + "regexp" "strings" "testing" @@ -81,6 +83,75 @@ func TestDroidstubs(t *testing.T) { } } +// runs a test for droidstubs with a customizable sdkType argument and returns +// the list of jar patterns that is passed as `--android-jar-pattern` +func getAndroidJarPatternsForDroidstubs(t *testing.T, sdkType string) []string { + ctx, _ := testJavaWithFS(t, fmt.Sprintf(` + droiddoc_exported_dir { + name: "some-exported-dir", + path: "somedir", + } + + droiddoc_exported_dir { + name: "some-other-exported-dir", + path: "someotherdir", + } + + droidstubs { + name: "foo-stubs", + srcs: ["foo-doc/a.java"], + api_levels_annotations_dirs: [ + "some-exported-dir", + "some-other-exported-dir", + ], + api_levels_annotations_enabled: true, + api_levels_sdk_type: "%s", + } + `, sdkType), + map[string][]byte{ + "foo-doc/a.java": nil, + }) + + m := ctx.ModuleForTests("foo-stubs", "android_common") + manifest := m.Output("metalava.sbox.textproto") + cmd := String(android.RuleBuilderSboxProtoForTests(t, manifest).Commands[0].Command) + r := regexp.MustCompile(`--android-jar-pattern [^ ]+/android.jar`) + return r.FindAllString(cmd, -1) +} + +func TestPublicDroidstubs(t *testing.T) { + patterns := getAndroidJarPatternsForDroidstubs(t, "public") + + android.AssertArrayString(t, "order of patterns", []string{ + "--android-jar-pattern somedir/%/public/android.jar", + "--android-jar-pattern someotherdir/%/public/android.jar", + }, patterns) +} + +func TestSystemDroidstubs(t *testing.T) { + patterns := getAndroidJarPatternsForDroidstubs(t, "system") + + android.AssertArrayString(t, "order of patterns", []string{ + "--android-jar-pattern somedir/%/system/android.jar", + "--android-jar-pattern someotherdir/%/system/android.jar", + "--android-jar-pattern somedir/%/public/android.jar", + "--android-jar-pattern someotherdir/%/public/android.jar", + }, patterns) +} + +func TestModuleLibDroidstubs(t *testing.T) { + patterns := getAndroidJarPatternsForDroidstubs(t, "module-lib") + + android.AssertArrayString(t, "order of patterns", []string{ + "--android-jar-pattern somedir/%/module-lib/android.jar", + "--android-jar-pattern someotherdir/%/module-lib/android.jar", + "--android-jar-pattern somedir/%/system/android.jar", + "--android-jar-pattern someotherdir/%/system/android.jar", + "--android-jar-pattern somedir/%/public/android.jar", + "--android-jar-pattern someotherdir/%/public/android.jar", + }, patterns) +} + func TestDroidstubsSandbox(t *testing.T) { ctx, _ := testJavaWithFS(t, ` genrule { diff --git a/java/fuzz.go b/java/fuzz.go new file mode 100644 index 000000000..257f34356 --- /dev/null +++ b/java/fuzz.go @@ -0,0 +1,159 @@ +// Copyright 2021 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 ( + "github.com/google/blueprint/proptools" + "sort" + "strings" + + "android/soong/android" + "android/soong/fuzz" +) + +func init() { + RegisterJavaFuzzBuildComponents(android.InitRegistrationContext) +} + +func RegisterJavaFuzzBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("java_fuzz_host", FuzzFactory) + ctx.RegisterSingletonType("java_fuzz_packaging", javaFuzzPackagingFactory) +} + +type JavaFuzzLibrary struct { + Library + fuzzPackagedModule fuzz.FuzzPackagedModule +} + +func (j *JavaFuzzLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { + j.Library.GenerateAndroidBuildActions(ctx) + + if j.fuzzPackagedModule.FuzzProperties.Corpus != nil { + j.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Corpus) + } + if j.fuzzPackagedModule.FuzzProperties.Data != nil { + j.fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Data) + } + if j.fuzzPackagedModule.FuzzProperties.Dictionary != nil { + j.fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *j.fuzzPackagedModule.FuzzProperties.Dictionary) + } + + if j.fuzzPackagedModule.FuzzProperties.Fuzz_config != nil { + configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json") + android.WriteFileRule(ctx, configPath, j.fuzzPackagedModule.FuzzProperties.Fuzz_config.String()) + j.fuzzPackagedModule.Config = configPath + } +} + +// java_fuzz builds and links sources into a `.jar` file for the host. +// +// By default, a java_fuzz produces a `.jar` file containing `.class` files. +// This jar is not suitable for installing on a device. +func FuzzFactory() android.Module { + module := &JavaFuzzLibrary{} + + module.addHostProperties() + module.Module.properties.Installable = proptools.BoolPtr(false) + module.AddProperties(&module.fuzzPackagedModule.FuzzProperties) + + // java_fuzz packaging rules collide when both linux_glibc and linux_bionic are enabled, disable the linux_bionic variants. + android.AddLoadHook(module, func(ctx android.LoadHookContext) { + disableLinuxBionic := struct { + Target struct { + Linux_bionic struct { + Enabled *bool + } + } + }{} + disableLinuxBionic.Target.Linux_bionic.Enabled = proptools.BoolPtr(false) + ctx.AppendProperties(&disableLinuxBionic) + }) + + module.initModuleAndImport(module) + android.InitSdkAwareModule(module) + InitJavaModule(module, android.HostSupported) + return module +} + +// Responsible for generating rules that package fuzz targets into +// their architecture & target/host specific zip file. +type javaFuzzPackager struct { + fuzz.FuzzPackager +} + +func javaFuzzPackagingFactory() android.Singleton { + return &javaFuzzPackager{} +} + +func (s *javaFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) { + // Map between each architecture + host/device combination. + archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip) + + // List of individual fuzz targets. + s.FuzzTargets = make(map[string]bool) + + ctx.VisitAllModules(func(module android.Module) { + // Discard non-fuzz targets. + javaModule, ok := module.(*JavaFuzzLibrary) + if !ok { + return + } + + fuzzModuleValidator := fuzz.FuzzModule{ + javaModule.ModuleBase, + javaModule.DefaultableModuleBase, + javaModule.ApexModuleBase, + } + + if ok := fuzz.IsValid(fuzzModuleValidator); !ok || *javaModule.Module.properties.Installable { + return + } + + hostOrTargetString := "target" + if javaModule.Host() { + hostOrTargetString = "host" + } + archString := javaModule.Arch().ArchType.String() + + archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString) + archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()} + + var files []fuzz.FileToZip + 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) + + // Add .jar + files = append(files, fuzz.FileToZip{javaModule.outputFile, ""}) + + archDirs[archOs], ok = s.BuildZipFile(ctx, module, javaModule.fuzzPackagedModule, files, builder, archDir, archString, "host", archOs, archDirs) + if !ok { + return + } + + }) + s.CreateFuzzPackage(ctx, archDirs, fuzz.Java, pctx) +} + +func (s *javaFuzzPackager) MakeVars(ctx android.MakeVarsContext) { + packages := s.Packages.Strings() + sort.Strings(packages) + + ctx.Strict("SOONG_JAVA_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " ")) + + // Preallocate the slice of fuzz targets to minimize memory allocations. + s.PreallocateSlice(ctx, "ALL_JAVA_FUZZ_TARGETS") +} diff --git a/java/fuzz_test.go b/java/fuzz_test.go new file mode 100644 index 000000000..cf063ebe2 --- /dev/null +++ b/java/fuzz_test.go @@ -0,0 +1,65 @@ +// Copyright 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package java + +import ( + "android/soong/android" + "path/filepath" + "testing" +) + +var prepForJavaFuzzTest = android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + android.FixtureRegisterWithContext(RegisterJavaFuzzBuildComponents), +) + +func TestJavaFuzz(t *testing.T) { + result := prepForJavaFuzzTest.RunTestWithBp(t, ` + java_fuzz_host { + name: "foo", + srcs: ["a.java"], + libs: ["bar"], + static_libs: ["baz"], + } + + java_library_host { + name: "bar", + srcs: ["b.java"], + } + + java_library_host { + name: "baz", + srcs: ["c.java"], + }`) + + osCommonTarget := result.Config.BuildOSCommonTarget.String() + javac := result.ModuleForTests("foo", osCommonTarget).Rule("javac") + combineJar := result.ModuleForTests("foo", osCommonTarget).Description("for javac") + + if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" { + t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs) + } + + baz := result.ModuleForTests("baz", osCommonTarget).Rule("javac").Output.String() + barOut := filepath.Join("out", "soong", ".intermediates", "bar", osCommonTarget, "javac", "bar.jar") + bazOut := filepath.Join("out", "soong", ".intermediates", "baz", osCommonTarget, "javac", "baz.jar") + + android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], barOut) + android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], bazOut) + + if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != baz { + t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, baz) + } +} diff --git a/java/gen.go b/java/gen.go index 445a2d8a3..1572bf00a 100644 --- a/java/gen.go +++ b/java/gen.go @@ -44,7 +44,7 @@ var ( }) ) -func genAidl(ctx android.ModuleContext, aidlFiles android.Paths, aidlFlags string, deps android.Paths) android.Paths { +func genAidl(ctx android.ModuleContext, aidlFiles android.Paths, aidlGlobalFlags string, aidlIndividualFlags map[string]string, deps android.Paths) android.Paths { // Shard aidl files into groups of 50 to avoid having to recompile all of them if one changes and to avoid // hitting command line length limits. shards := android.ShardPaths(aidlFiles, 50) @@ -61,15 +61,17 @@ func genAidl(ctx android.ModuleContext, aidlFiles android.Paths, aidlFlags strin rule.Command().Text("rm -rf").Flag(outDir.String()) rule.Command().Text("mkdir -p").Flag(outDir.String()) - rule.Command().Text("FLAGS=' " + aidlFlags + "'") + rule.Command().Text("FLAGS=' " + aidlGlobalFlags + "'") for _, aidlFile := range shard { + localFlag := aidlIndividualFlags[aidlFile.String()] depFile := srcJarFile.InSameDir(ctx, aidlFile.String()+".d") javaFile := outDir.Join(ctx, pathtools.ReplaceExtension(aidlFile.String(), "java")) rule.Command(). Tool(ctx.Config().HostToolPath(ctx, "aidl")). FlagWithDepFile("-d", depFile). Flag("$FLAGS"). + Flag(localFlag). Input(aidlFile). Output(javaFile). Implicits(deps) @@ -159,7 +161,14 @@ func (j *Module) genSources(ctx android.ModuleContext, srcFiles android.Paths, // Process all aidl files together to support sharding them into one or more rules that produce srcjars. if len(aidlSrcs) > 0 { - srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, flags.aidlDeps) + individualFlags := make(map[string]string) + for _, aidlSrc := range aidlSrcs { + flags := j.individualAidlFlags(ctx, aidlSrc) + if flags != "" { + individualFlags[aidlSrc.String()] = flags + } + } + srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, individualFlags, flags.aidlDeps) outSrcFiles = append(outSrcFiles, srcJarFiles...) } diff --git a/java/genrule.go b/java/genrule.go index e0a9c8faf..5047c412f 100644 --- a/java/genrule.go +++ b/java/genrule.go @@ -24,8 +24,8 @@ func init() { } func RegisterGenRuleBuildComponents(ctx android.RegistrationContext) { - ctx.RegisterModuleType("java_genrule", genRuleFactory) - ctx.RegisterModuleType("java_genrule_host", genRuleFactoryHost) + ctx.RegisterModuleType("java_genrule", GenRuleFactory) + ctx.RegisterModuleType("java_genrule_host", GenRuleFactoryHost) } // java_genrule is a genrule that can depend on other java_* objects. @@ -33,7 +33,7 @@ func RegisterGenRuleBuildComponents(ctx android.RegistrationContext) { // By default a java_genrule has a single variant that will run against the device variant of its dependencies and // produce an output that can be used as an input to a device java rule. // -// Specifying `host_supported: true` will produce two variants, one that uses device dependencie sand one that uses +// Specifying `host_supported: true` will produce two variants, one that uses device dependencies and one that uses // host dependencies. Each variant will run the command. // // Use a java_genrule instead of a genrule when it needs to depend on or be depended on by other java modules, unless @@ -44,7 +44,7 @@ func RegisterGenRuleBuildComponents(ctx android.RegistrationContext) { // Use a java_genrule to package generated java resources: // // java_genrule { -// name: "generated_resources", +// name: "generated_resources", // tools: [ // "generator", // "soong_zip", @@ -60,10 +60,12 @@ func RegisterGenRuleBuildComponents(ctx android.RegistrationContext) { // srcs: ["src/**/*.java"], // static_libs: ["generated_resources"], // } -func genRuleFactory() android.Module { +func GenRuleFactory() android.Module { module := genrule.NewGenRule() android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon) + android.InitDefaultableModule(module) + android.InitBazelModule(module) return module } @@ -72,10 +74,12 @@ func genRuleFactory() android.Module { // // A java_genrule_host has a single variant that will run against the host variant of its dependencies and // produce an output that can be used as an input to a host java rule. -func genRuleFactoryHost() android.Module { +func GenRuleFactoryHost() android.Module { module := genrule.NewGenRule() android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon) + android.InitDefaultableModule(module) + android.InitBazelModule(module) return module } diff --git a/java/genrule_test.go b/java/genrule_test.go new file mode 100644 index 000000000..1c294b286 --- /dev/null +++ b/java/genrule_test.go @@ -0,0 +1,118 @@ +// 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 ( + "reflect" + "strings" + "testing" + + "android/soong/android" +) + +func testGenruleContext(config android.Config) *android.TestContext { + ctx := android.NewTestArchContext(config) + ctx.RegisterModuleType("java_genrule", GenRuleFactory) + ctx.Register() + + return ctx +} + +func TestGenruleCmd(t *testing.T) { + fs := map[string][]byte{ + "tool": nil, + "foo": nil, + } + bp := ` + java_genrule { + name: "gen", + tool_files: ["tool"], + cmd: "$(location tool) $(in) $(out)", + srcs: ["foo"], + out: ["out"], + } + ` + config := android.TestArchConfig(t.TempDir(), nil, bp, fs) + + ctx := testGenruleContext(config) + + _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) + if errs == nil { + _, errs = ctx.PrepareBuildActions(config) + } + if errs != nil { + t.Fatal(errs) + } + + gen := ctx.ModuleForTests("gen", "android_common").Output("out") + expected := []string{"foo"} + if !reflect.DeepEqual(expected, gen.Implicits.Strings()[:len(expected)]) { + t.Errorf(`want arm inputs %v, got %v`, expected, gen.Implicits.Strings()) + } +} + +func TestJarGenrules(t *testing.T) { + ctx, _ := testJava(t, ` + java_library { + name: "foo", + srcs: ["a.java"], + } + + java_genrule { + name: "jargen", + tool_files: ["b.java"], + cmd: "$(location b.java) $(in) $(out)", + out: ["jargen.jar"], + srcs: [":foo"], + } + + java_library { + name: "bar", + static_libs: ["jargen"], + srcs: ["c.java"], + } + + java_library { + name: "baz", + libs: ["jargen"], + srcs: ["c.java"], + } + `) + + foo := ctx.ModuleForTests("foo", "android_common").Output("javac/foo.jar") + jargen := ctx.ModuleForTests("jargen", "android_common").Output("jargen.jar") + bar := ctx.ModuleForTests("bar", "android_common").Output("javac/bar.jar") + baz := ctx.ModuleForTests("baz", "android_common").Output("javac/baz.jar") + barCombined := ctx.ModuleForTests("bar", "android_common").Output("combined/bar.jar") + + if g, w := jargen.Implicits.Strings(), foo.Output.String(); !android.InList(w, g) { + t.Errorf("expected jargen inputs [%q], got %q", w, g) + } + + if !strings.Contains(bar.Args["classpath"], jargen.Output.String()) { + t.Errorf("bar classpath %v does not contain %q", bar.Args["classpath"], jargen.Output.String()) + } + + if !strings.Contains(baz.Args["classpath"], jargen.Output.String()) { + t.Errorf("baz classpath %v does not contain %q", baz.Args["classpath"], jargen.Output.String()) + } + + if len(barCombined.Inputs) != 2 || + barCombined.Inputs[0].String() != bar.Output.String() || + barCombined.Inputs[1].String() != jargen.Output.String() { + t.Errorf("bar combined jar inputs %v is not [%q, %q]", + barCombined.Inputs.Strings(), bar.Output.String(), jargen.Output.String()) + } +} diff --git a/java/hiddenapi.go b/java/hiddenapi.go index f901434a0..cf9c7ad7a 100644 --- a/java/hiddenapi.go +++ b/java/hiddenapi.go @@ -30,14 +30,14 @@ type hiddenAPI struct { // that information encoded within it. active bool - // The path to the dex jar that is in the boot class path. If this is nil then the associated + // The path to the dex jar that is in the boot class path. If this is unset then the associated // module is not a boot jar, but could be one of the <x>-hiddenapi modules that provide additional // annotations for the <x> boot dex jar but which do not actually provide a boot dex jar // themselves. // // This must be the path to the unencoded dex jar as the encoded dex jar indirectly depends on // this file so using the encoded dex jar here would result in a cycle in the ninja rules. - bootDexJarPath android.Path + bootDexJarPath OptionalDexJarPath // The paths to the classes jars that contain classes and class members annotated with // the UnsupportedAppUsage annotation that need to be extracted as part of the hidden API @@ -49,7 +49,7 @@ type hiddenAPI struct { uncompressDexState *bool } -func (h *hiddenAPI) bootDexJar() android.Path { +func (h *hiddenAPI) bootDexJar() OptionalDexJarPath { return h.bootDexJarPath } @@ -65,10 +65,12 @@ func (h *hiddenAPI) uncompressDex() *bool { type hiddenAPIModule interface { android.Module hiddenAPIIntf + + MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec } type hiddenAPIIntf interface { - bootDexJar() android.Path + bootDexJar() OptionalDexJarPath classesJars() android.Paths uncompressDex() *bool } @@ -79,7 +81,7 @@ var _ hiddenAPIIntf = (*hiddenAPI)(nil) // // uncompressedDexState should be nil when the module is a prebuilt and so does not require hidden // API encoding. -func (h *hiddenAPI) initHiddenAPI(ctx android.ModuleContext, dexJar, classesJar android.Path, uncompressedDexState *bool) { +func (h *hiddenAPI) initHiddenAPI(ctx android.ModuleContext, dexJar OptionalDexJarPath, classesJar android.Path, uncompressedDexState *bool) { // Save the classes jars even if this is not active as they may be used by modular hidden API // processing. @@ -118,11 +120,11 @@ func (h *hiddenAPI) initHiddenAPI(ctx android.ModuleContext, dexJar, classesJar } func isModuleInBootClassPath(ctx android.BaseModuleContext, module android.Module) bool { - // Get the configured non-updatable and updatable boot jars. - nonUpdatableBootJars := ctx.Config().NonUpdatableBootJars() - updatableBootJars := ctx.Config().UpdatableBootJars() - active := isModuleInConfiguredList(ctx, module, nonUpdatableBootJars) || - isModuleInConfiguredList(ctx, module, updatableBootJars) + // Get the configured platform and apex boot jars. + nonApexBootJars := ctx.Config().NonApexBootJars() + apexBootJars := ctx.Config().ApexBootJars() + active := isModuleInConfiguredList(ctx, module, nonApexBootJars) || + isModuleInConfiguredList(ctx, module, apexBootJars) return active } @@ -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", @@ -297,6 +308,7 @@ func hiddenAPIEncodeDex(ctx android.ModuleContext, dexInput, flagsCSV android.Pa type hiddenApiAnnotationsDependencyTag struct { blueprint.BaseDependencyTag + android.LicenseAnnotationSharedDependencyTag } // Tag used to mark dependencies on java_library instances that contains Java source files whose diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go index 654ebb743..c90b2ff97 100644 --- a/java/hiddenapi_modular.go +++ b/java/hiddenapi_modular.go @@ -19,6 +19,7 @@ import ( "strings" "android/soong/android" + "github.com/google/blueprint" ) @@ -218,7 +219,7 @@ func (b hiddenAPIStubsDependencyTag) ExcludeFromVisibilityEnforcement() { var _ android.ExcludeFromVisibilityEnforcementTag = hiddenAPIStubsDependencyTag{} var _ android.ReplaceSourceWithPrebuilt = hiddenAPIStubsDependencyTag{} var _ android.ExcludeFromApexContentsTag = hiddenAPIStubsDependencyTag{} -var _ android.SdkMemberTypeDependencyTag = hiddenAPIStubsDependencyTag{} +var _ android.SdkMemberDependencyTag = hiddenAPIStubsDependencyTag{} // hiddenAPIComputeMonolithicStubLibModules computes the set of module names that provide stubs // needed to produce the hidden API monolithic stub flags file. @@ -277,7 +278,7 @@ func hiddenAPIAddStubLibDependencies(ctx android.BottomUpMutatorContext, apiScop // hiddenAPIRetrieveDexJarBuildPath retrieves the DexJarBuildPath from the specified module, if // available, or reports an error. func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android.Module, kind android.SdkKind) android.Path { - var dexJar android.Path + var dexJar OptionalDexJarPath if sdkLibrary, ok := module.(SdkLibraryDependency); ok { dexJar = sdkLibrary.SdkApiStubDexJar(ctx, kind) } else if j, ok := module.(UsesLibraryDependency); ok { @@ -287,17 +288,24 @@ func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android. return nil } - if dexJar == nil { - ctx.ModuleErrorf("dependency %s does not provide a dex jar, consider setting compile_dex: true", module) + if !dexJar.Valid() { + ctx.ModuleErrorf("dependency %s does not provide a dex jar: %s", module, dexJar.InvalidReason()) + return nil } - return dexJar + return dexJar.Path() } +// HIDDENAPI_STUB_FLAGS_IMPL_FLAGS is the set of flags that identify implementation only signatures, +// i.e. those signatures that are not part of any API (including the hidden API). +var HIDDENAPI_STUB_FLAGS_IMPL_FLAGS = []string{} + +var HIDDENAPI_FLAGS_CSV_IMPL_FLAGS = []string{"blocked"} + // buildRuleToGenerateHiddenAPIStubFlagsFile creates a rule to create a hidden API stub flags file. // // The rule is initialized but not built so that the caller can modify it and select an appropriate // name. -func buildRuleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, name, desc string, outputPath android.WritablePath, bootDexJars android.Paths, input HiddenAPIFlagInput, moduleStubFlagsPaths android.Paths) { +func buildRuleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, name, desc string, outputPath android.WritablePath, bootDexJars android.Paths, input HiddenAPIFlagInput, stubFlagSubsets SignatureCsvSubsets) { // Singleton rule which applies hiddenapi on all boot class path dex files. rule := android.NewRuleBuilder(pctx, ctx) @@ -317,7 +325,7 @@ func buildRuleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, name, // If no module stub flags paths are provided then this must be being called for a // bootclasspath_fragment and not the whole platform_bootclasspath. - if moduleStubFlagsPaths == nil { + if stubFlagSubsets == nil { // This is being run on a fragment of the bootclasspath. command.Flag("--fragment") } @@ -342,8 +350,9 @@ func buildRuleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, name, // If there are stub flag files that have been generated by fragments on which this depends then // use them to validate the stub flag file generated by the rules created by this method. - if len(moduleStubFlagsPaths) > 0 { - validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, moduleStubFlagsPaths) + if len(stubFlagSubsets) > 0 { + validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, stubFlagSubsets, + HIDDENAPI_STUB_FLAGS_IMPL_FLAGS) // Add the file that indicates that the file generated by this is valid. // @@ -510,14 +519,6 @@ func (s FlagFilesByCategory) append(other FlagFilesByCategory) { } } -// dedup removes duplicates in the flag files, while maintaining the order in which they were -// appended. -func (s FlagFilesByCategory) dedup() { - for category, paths := range s { - s[category] = android.FirstUniquePaths(paths) - } -} - // HiddenAPIInfo contains information provided by the hidden API processing. // // That includes paths resolved from HiddenAPIFlagFileProperties and also generated by hidden API @@ -554,6 +555,20 @@ func (i *HiddenAPIInfo) mergeFromFragmentDeps(ctx android.ModuleContext, fragmen } } +// StubFlagSubset returns a SignatureCsvSubset that contains a path to a filtered-stub-flags.csv +// file and a path to a signature-patterns.csv file that defines a subset of the monolithic stub +// flags file, i.e. out/soong/hiddenapi/hiddenapi-stub-flags.txt, against which it will be compared. +func (i *HiddenAPIInfo) StubFlagSubset() SignatureCsvSubset { + return SignatureCsvSubset{i.FilteredStubFlagsPath, i.SignaturePatternsPath} +} + +// FlagSubset returns a SignatureCsvSubset that contains a path to a filtered-flags.csv file and a +// path to a signature-patterns.csv file that defines a subset of the monolithic flags file, i.e. +// out/soong/hiddenapi/hiddenapi-flags.csv, against which it will be compared. +func (i *HiddenAPIInfo) FlagSubset() SignatureCsvSubset { + return SignatureCsvSubset{i.FilteredFlagsPath, i.SignaturePatternsPath} +} + var HiddenAPIInfoProvider = blueprint.NewProvider(HiddenAPIInfo{}) // ModuleStubDexJars contains the stub dex jars provided by a single module. @@ -776,9 +791,6 @@ func (i *HiddenAPIFlagInput) transitiveStubDexJarsByScope() StubDexJarsByModule // HiddenAPIFlagOutput contains paths to output files from the hidden API flag generation for a // bootclasspath_fragment module. type HiddenAPIFlagOutput struct { - // The path to the generated stub-flags.csv file. - StubFlagsPath android.Path - // The path to the generated annotation-flags.csv file. AnnotationFlagsPath android.Path @@ -788,8 +800,21 @@ type HiddenAPIFlagOutput struct { // The path to the generated index.csv file. IndexPath android.Path + // The path to the generated stub-flags.csv file. + StubFlagsPath android.Path + // The path to the generated all-flags.csv file. AllFlagsPath android.Path + + // The path to the generated signature-patterns.txt file which defines the subset of the + // monolithic hidden API files provided in this. + SignaturePatternsPath android.Path + + // The path to the generated filtered-stub-flags.csv file. + FilteredStubFlagsPath android.Path + + // The path to the generated filtered-flags.csv file. + FilteredFlagsPath android.Path } // bootDexJarByModule is a map from base module name (without prebuilt_ prefix) to the boot dex @@ -856,7 +881,7 @@ func pathForValidation(ctx android.PathContext, path android.WritablePath) andro // the annotationFlags. func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc string, outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlagPaths android.Paths, - flagFilesByCategory FlagFilesByCategory, allFlagsPaths android.Paths, generatedRemovedDexSignatures android.OptionalPath) { + flagFilesByCategory FlagFilesByCategory, flagSubsets SignatureCsvSubsets, generatedRemovedDexSignatures android.OptionalPath) { // Create the rule that will generate the flag files. tempPath := tempPathForRestat(ctx, outputPath) @@ -885,8 +910,9 @@ func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc st // If there are flag files that have been generated by fragments on which this depends then use // them to validate the flag file generated by the rules created by this method. - if len(allFlagsPaths) > 0 { - validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, allFlagsPaths) + if len(flagSubsets) > 0 { + validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, flagSubsets, + HIDDENAPI_FLAGS_CSV_IMPL_FLAGS) // Add the file that indicates that the file generated by this is valid. // @@ -898,20 +924,122 @@ func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc st rule.Build(name, desc) } +// SignatureCsvSubset describes a subset of a monolithic flags file, i.e. either +// out/soong/hiddenapi/hiddenapi-stub-flags.txt or out/soong/hiddenapi/hiddenapi-flags.csv +type SignatureCsvSubset struct { + // The path to the CSV file containing hidden API flags. + // + // It has the dex member signature as the first column, with flags, one per column, in the + // subsequent columns. + CsvFile android.Path + + // The path to the CSV file containing the signature patterns. + // + // It is a single column CSV file with the column containing a signature pattern. + SignaturePatternsFile android.Path +} + +type SignatureCsvSubsets []SignatureCsvSubset + +func (s SignatureCsvSubsets) RelativeToTop() []string { + result := []string{} + for _, subset := range s { + result = append(result, fmt.Sprintf("%s:%s", subset.CsvFile.RelativeToTop(), subset.SignaturePatternsFile.RelativeToTop())) + } + return result +} + +// buildRuleSignaturePatternsFile creates a rule to generate a file containing the set of signature +// patterns that will select a subset of the monolithic flags. +func buildRuleSignaturePatternsFile( + ctx android.ModuleContext, flagsPath android.Path, + splitPackages []string, packagePrefixes []string, singlePackages []string) android.Path { + patternsFile := android.PathForModuleOut(ctx, "modular-hiddenapi", "signature-patterns.csv") + // Create a rule to validate the output from the following rule. + rule := android.NewRuleBuilder(pctx, ctx) + + // Quote any * in the packages to avoid them being expanded by the shell. + quotedSplitPackages := []string{} + for _, pkg := range splitPackages { + quotedSplitPackages = append(quotedSplitPackages, strings.ReplaceAll(pkg, "*", "\\*")) + } + + rule.Command(). + BuiltTool("signature_patterns"). + FlagWithInput("--flags ", flagsPath). + FlagForEachArg("--split-package ", quotedSplitPackages). + FlagForEachArg("--package-prefix ", packagePrefixes). + FlagForEachArg("--single-package ", singlePackages). + FlagWithOutput("--output ", patternsFile) + rule.Build("hiddenAPISignaturePatterns", "hidden API signature patterns") + + return patternsFile +} + +// buildRuleRemoveSignaturesWithImplementationFlags creates a rule that will remove signatures from +// the input flags file which have only the implementation flags, i.e. are not part of an API. +// +// The implementationFlags specifies the set of default flags that identifies the signature of a +// private, implementation only, member. Signatures that match those flags are removed from the +// flags as they are implementation only. +// +// This is used to remove implementation only signatures from the signature files that are persisted +// in the sdk snapshot as the sdk snapshots should not include implementation details. The +// signatures generated by this method will be compared by the buildRuleValidateOverlappingCsvFiles +// method which treats any missing signatures as if they were implementation only signatures. +func buildRuleRemoveSignaturesWithImplementationFlags(ctx android.BuilderContext, + name string, desc string, inputPath android.Path, filteredPath android.WritablePath, + implementationFlags []string) { + + rule := android.NewRuleBuilder(pctx, ctx) + implementationFlagPattern := "" + for _, implementationFlag := range implementationFlags { + implementationFlagPattern = implementationFlagPattern + "," + implementationFlag + } + rule.Command(). + Text(`grep -vE "^[^,]+` + implementationFlagPattern + `$"`).Input(inputPath). + Text(">").Output(filteredPath). + // Grep's exit code depends on whether it finds anything. It is 0 (build success) when it finds + // something and 1 (build failure) when it does not and 2 (when it encounters an error). + // However, while it is unlikely it is not an error if this does not find any matches. The + // following will only run if the grep does not find something and in that case it will treat + // an exit code of 1 as success and anything else as failure. + Text("|| test $? -eq 1") + rule.Build(name, desc) +} + // buildRuleValidateOverlappingCsvFiles checks that the modular CSV files, i.e. the files generated // by the individual bootclasspath_fragment modules are subsets of the monolithic CSV file. -func buildRuleValidateOverlappingCsvFiles(ctx android.BuilderContext, name string, desc string, monolithicFilePath android.WritablePath, modularFilePaths android.Paths) android.WritablePath { +// +// The implementationFlags specifies the set of default flags that identifies the signature of a +// private, implementation only, member. A signature which is present in a monolithic flags subset +// defined by SignatureCsvSubset but which is not present in the flags file from the corresponding +// module is assumed to be an implementation only member and so must have these flags. +func buildRuleValidateOverlappingCsvFiles(ctx android.BuilderContext, name string, desc string, + monolithicFilePath android.WritablePath, csvSubsets SignatureCsvSubsets, + implementationFlags []string) android.WritablePath { // The file which is used to record that the flags file is valid. validFile := pathForValidation(ctx, monolithicFilePath) // Create a rule to validate the output from the following rule. rule := android.NewRuleBuilder(pctx, ctx) - rule.Command(). + command := rule.Command(). BuiltTool("verify_overlaps"). - Input(monolithicFilePath). - Inputs(modularFilePaths). - // If validation passes then update the file that records that. - Text("&& touch").Output(validFile) + FlagWithInput("--monolithic-flags ", monolithicFilePath) + + for _, subset := range csvSubsets { + command. + Flag("--module-flags "). + Textf("%s:%s", subset.CsvFile, subset.SignaturePatternsFile). + Implicit(subset.CsvFile).Implicit(subset.SignaturePatternsFile) + } + + for _, implementationFlag := range implementationFlags { + command.FlagWithArg("--implementation-flag ", implementationFlag) + } + + // If validation passes then update the file that records that. + command.Text("&& touch").Output(validFile) rule.Build(name+"Validation", desc+" validation") return validFile @@ -976,18 +1104,34 @@ 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 } + // Generate the filtered-stub-flags.csv file which contains the filtered stub flags that will be + // compared against the monolithic stub flags. + filteredStubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "filtered-stub-flags.csv") + buildRuleRemoveSignaturesWithImplementationFlags(ctx, "modularHiddenApiFilteredStubFlags", + "modular hiddenapi filtered stub flags", stubFlagsCSV, filteredStubFlagsCSV, + HIDDENAPI_STUB_FLAGS_IMPL_FLAGS) + + // Generate the filtered-flags.csv file which contains the filtered flags that will be compared + // against the monolithic flags. + filteredFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "filtered-flags.csv") + buildRuleRemoveSignaturesWithImplementationFlags(ctx, "modularHiddenApiFilteredFlags", + "modular hiddenapi filtered flags", allFlagsCSV, filteredFlagsCSV, + HIDDENAPI_FLAGS_CSV_IMPL_FLAGS) + // Store the paths in the info for use by other modules and sdk snapshot generation. output := HiddenAPIOutput{ HiddenAPIFlagOutput: HiddenAPIFlagOutput{ - StubFlagsPath: stubFlagsCSV, - AnnotationFlagsPath: annotationFlagsCSV, - MetadataPath: metadataCSV, - IndexPath: indexCSV, - AllFlagsPath: allFlagsCSV, + AnnotationFlagsPath: annotationFlagsCSV, + MetadataPath: metadataCSV, + IndexPath: indexCSV, + StubFlagsPath: stubFlagsCSV, + AllFlagsPath: allFlagsCSV, + FilteredStubFlagsPath: filteredStubFlagsCSV, + FilteredFlagsPath: filteredFlagsCSV, }, EncodedBootDexFilesByModule: encodedBootDexJarsByModule, } @@ -1044,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 @@ -1069,6 +1216,7 @@ func extractBootDexInfoFromModules(ctx android.ModuleContext, contents []android bootDexJarsByModule[module.Name()] = bootDexInfo{ path: bootDexJar, uncompressDex: *hiddenAPIModule.uncompressDex(), + minSdkVersion: hiddenAPIModule.MinSdkVersion(ctx), } } @@ -1077,18 +1225,17 @@ func extractBootDexInfoFromModules(ctx android.ModuleContext, contents []android // retrieveBootDexJarFromHiddenAPIModule retrieves the boot dex jar from the hiddenAPIModule. // -// If the module does not provide a boot dex jar, i.e. the returned boot dex jar is nil, then that -// create a fake path and either report an error immediately or defer reporting of the error until -// the path is actually used. +// If the module does not provide a boot dex jar, i.e. the returned boot dex jar is unset or +// invalid, then create a fake path and either report an error immediately or defer reporting of the +// error until the path is actually used. func retrieveBootDexJarFromHiddenAPIModule(ctx android.ModuleContext, module hiddenAPIModule) android.Path { bootDexJar := module.bootDexJar() - if bootDexJar == nil { + if !bootDexJar.Valid() { fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/boot-dex/%s.jar", module.Name())) - bootDexJar = fake - - handleMissingDexBootFile(ctx, module, fake) + handleMissingDexBootFile(ctx, module, fake, bootDexJar.InvalidReason()) + return fake } - return bootDexJar + return bootDexJar.Path() } // extractClassesJarsFromModules extracts the class jars from the supplied modules. @@ -1112,13 +1259,6 @@ func retrieveClassesJarsFromModule(module android.Module) android.Paths { // deferReportingMissingBootDexJar returns true if a missing boot dex jar should not be reported by // Soong but should instead only be reported in ninja if the file is actually built. func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.Module) bool { - // TODO(b/179354495): Remove this workaround when it is unnecessary. - // Prebuilt modules like framework-wifi do not yet provide dex implementation jars. So, - // create a fake one that will cause a build error only if it is used. - if ctx.Config().AlwaysUsePrebuiltSdks() { - return true - } - // Any missing dependency should be allowed. if ctx.Config().AllowMissingDependencies() { return true @@ -1189,7 +1329,7 @@ func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.M // handleMissingDexBootFile will either log a warning or create an error rule to create the fake // file depending on the value returned from deferReportingMissingBootDexJar. -func handleMissingDexBootFile(ctx android.ModuleContext, module android.Module, fake android.WritablePath) { +func handleMissingDexBootFile(ctx android.ModuleContext, module android.Module, fake android.WritablePath, reason string) { if deferReportingMissingBootDexJar(ctx, module) { // Create an error rule that pretends to create the output file but will actually fail if it // is run. @@ -1197,11 +1337,11 @@ func handleMissingDexBootFile(ctx android.ModuleContext, module android.Module, Rule: android.ErrorRule, Output: fake, Args: map[string]string{ - "error": fmt.Sprintf("missing dependencies: boot dex jar for %s", module), + "error": fmt.Sprintf("missing boot dex jar dependency for %s: %s", module, reason), }, }) } else { - ctx.ModuleErrorf("module %s does not provide a dex jar", module) + ctx.ModuleErrorf("module %s does not provide a dex jar: %s", module, reason) } } @@ -1212,14 +1352,13 @@ func handleMissingDexBootFile(ctx android.ModuleContext, module android.Module, // However, under certain conditions, e.g. errors, or special build configurations it will return // a path to a fake file. func retrieveEncodedBootDexJarFromModule(ctx android.ModuleContext, module android.Module) android.Path { - bootDexJar := module.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath() - if bootDexJar == nil { + bootDexJar := module.(interface{ DexJarBuildPath() OptionalDexJarPath }).DexJarBuildPath() + if !bootDexJar.Valid() { fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/encoded-dex/%s.jar", module.Name())) - bootDexJar = fake - - handleMissingDexBootFile(ctx, module, fake) + handleMissingDexBootFile(ctx, module, fake, bootDexJar.InvalidReason()) + return fake } - return bootDexJar + return bootDexJar.Path() } // extractEncodedDexJarsFromModules extracts the encoded dex jars from the supplied modules. diff --git a/java/hiddenapi_monolithic.go b/java/hiddenapi_monolithic.go index 52f0770f3..5956e3c6c 100644 --- a/java/hiddenapi_monolithic.go +++ b/java/hiddenapi_monolithic.go @@ -29,9 +29,6 @@ type MonolithicHiddenAPIInfo struct { // that category. FlagsFilesByCategory FlagFilesByCategory - // The paths to the generated stub-flags.csv files. - StubFlagsPaths android.Paths - // The paths to the generated annotation-flags.csv files. AnnotationFlagsPaths android.Paths @@ -41,8 +38,13 @@ type MonolithicHiddenAPIInfo struct { // The paths to the generated index.csv files. IndexPaths android.Paths - // The paths to the generated all-flags.csv files. - AllFlagsPaths android.Paths + // The subsets of the monolithic hiddenapi-stubs-flags.txt file that are provided by each + // bootclasspath_fragment modules. + StubFlagSubsets SignatureCsvSubsets + + // The subsets of the monolithic hiddenapi-flags.csv file that are provided by each + // bootclasspath_fragment modules. + FlagSubsets SignatureCsvSubsets // The classes jars from the libraries on the platform bootclasspath. ClassesJars android.Paths @@ -58,68 +60,34 @@ func newMonolithicHiddenAPIInfo(ctx android.ModuleContext, flagFilesByCategory F // Merge all the information from the classpathElements. The fragments form a DAG so it is possible that // this will introduce duplicates so they will be resolved after processing all the classpathElements. for _, element := range classpathElements { - var classesJars android.Paths switch e := element.(type) { case *ClasspathLibraryElement: - classesJars = retrieveClassesJarsFromModule(e.Module()) + classesJars := retrieveClassesJarsFromModule(e.Module()) + monolithicInfo.ClassesJars = append(monolithicInfo.ClassesJars, classesJars...) case *ClasspathFragmentElement: fragment := e.Module() if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) { info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo) monolithicInfo.append(&info) - - // If the bootclasspath fragment actually perform hidden API processing itself then use the - // CSV files it provides and do not bother processing the classesJars files. This ensures - // consistent behavior between source and prebuilt as prebuilt modules do not provide - // classesJars. - if info.AllFlagsPath != nil { - continue - } + } else { + ctx.ModuleErrorf("%s does not provide hidden API information", fragment) } - - classesJars = extractClassesJarsFromModules(e.Contents) } - - monolithicInfo.ClassesJars = append(monolithicInfo.ClassesJars, classesJars...) } - // Dedup paths. - monolithicInfo.dedup() - return monolithicInfo } // append appends all the files from the supplied info to the corresponding files in this struct. func (i *MonolithicHiddenAPIInfo) append(other *HiddenAPIInfo) { i.FlagsFilesByCategory.append(other.FlagFilesByCategory) + i.AnnotationFlagsPaths = append(i.AnnotationFlagsPaths, other.AnnotationFlagsPath) + i.MetadataPaths = append(i.MetadataPaths, other.MetadataPath) + i.IndexPaths = append(i.IndexPaths, other.IndexPath) - // The output may not be set if the bootclasspath_fragment has not yet been updated to support - // hidden API processing. - // TODO(b/179354495): Switch back to append once all bootclasspath_fragment modules have been - // updated to support hidden API processing properly. - appendIfNotNil := func(paths android.Paths, path android.Path) android.Paths { - if path == nil { - return paths - } - return append(paths, path) - } - i.StubFlagsPaths = appendIfNotNil(i.StubFlagsPaths, other.StubFlagsPath) - i.AnnotationFlagsPaths = appendIfNotNil(i.AnnotationFlagsPaths, other.AnnotationFlagsPath) - i.MetadataPaths = appendIfNotNil(i.MetadataPaths, other.MetadataPath) - i.IndexPaths = appendIfNotNil(i.IndexPaths, other.IndexPath) - i.AllFlagsPaths = appendIfNotNil(i.AllFlagsPaths, other.AllFlagsPath) -} - -// dedup removes duplicates in all the paths, while maintaining the order in which they were -// appended. -func (i *MonolithicHiddenAPIInfo) dedup() { - i.FlagsFilesByCategory.dedup() - i.StubFlagsPaths = android.FirstUniquePaths(i.StubFlagsPaths) - i.AnnotationFlagsPaths = android.FirstUniquePaths(i.AnnotationFlagsPaths) - i.MetadataPaths = android.FirstUniquePaths(i.MetadataPaths) - i.IndexPaths = android.FirstUniquePaths(i.IndexPaths) - i.AllFlagsPaths = android.FirstUniquePaths(i.AllFlagsPaths) + i.StubFlagSubsets = append(i.StubFlagSubsets, other.StubFlagSubset()) + i.FlagSubsets = append(i.FlagSubsets, other.FlagSubset()) } var MonolithicHiddenAPIInfoProvider = blueprint.NewProvider(MonolithicHiddenAPIInfo{}) diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go index dcd363c2c..75b7bb7c8 100644 --- a/java/hiddenapi_singleton_test.go +++ b/java/hiddenapi_singleton_test.go @@ -20,6 +20,7 @@ import ( "testing" "android/soong/android" + "github.com/google/blueprint/proptools" ) @@ -306,7 +307,7 @@ func TestHiddenAPIEncoding_JavaSdkLibrary(t *testing.T) { android.AssertStringEquals(t, "encode embedded java_library", unencodedDexJar, actualUnencodedDexJar.String()) // Make sure that the encoded dex jar is the exported one. - exportedDexJar := moduleForTests.Module().(UsesLibraryDependency).DexJarBuildPath() + exportedDexJar := moduleForTests.Module().(UsesLibraryDependency).DexJarBuildPath().Path() android.AssertPathRelativeToTopEquals(t, "encode embedded java_library", encodedDexJar, exportedDexJar) } diff --git a/java/jacoco.go b/java/jacoco.go index 9162161d3..e11c2ce69 100644 --- a/java/jacoco.go +++ b/java/jacoco.go @@ -94,7 +94,7 @@ func jacocoFiltersToZipCommand(includes, excludes []string) string { if len(includes) > 0 { specs += strings.Join(includes, " ") } else { - specs += "**/*.class" + specs += "'**/*.class'" } return specs } diff --git a/java/jacoco_test.go b/java/jacoco_test.go index 91f05535a..1882908ca 100644 --- a/java/jacoco_test.go +++ b/java/jacoco_test.go @@ -74,7 +74,7 @@ func TestJacocoFiltersToZipCommand(t *testing.T) { { name: "implicit wildcard", includes: []string{}, - out: "**/*.class", + out: "'**/*.class'", }, { name: "only include", diff --git a/java/java.go b/java/java.go index bbed42def..c8fb93cca 100644 --- a/java/java.go +++ b/java/java.go @@ -23,6 +23,8 @@ import ( "path/filepath" "strings" + "android/soong/bazel" + "github.com/google/blueprint" "github.com/google/blueprint/proptools" @@ -74,6 +76,7 @@ func RegisterJavaSdkMemberTypes() { android.RegisterSdkMemberType(javaHeaderLibsSdkMemberType) android.RegisterSdkMemberType(javaLibsSdkMemberType) android.RegisterSdkMemberType(javaBootLibsSdkMemberType) + android.RegisterSdkMemberType(javaSystemserverLibsSdkMemberType) android.RegisterSdkMemberType(javaTestSdkMemberType) } @@ -131,11 +134,50 @@ var ( PropertyName: "java_boot_libs", SupportsSdk: true, }, - // Temporarily export implementation classes jar for java_boot_libs as it is required for the - // hiddenapi processing. - // TODO(b/179354495): Revert once hiddenapi processing has been modularized. - exportImplementationClassesJar, - sdkSnapshotFilePathForJar, + func(ctx android.SdkMemberContext, j *Library) android.Path { + // Java boot libs are only provided in the SDK to provide access to their dex implementation + // jar for use by dexpreopting and boot jars package check. They do not need to provide an + // actual implementation jar but the java_import will need a file that exists so just copy an + // empty file. Any attempt to use that file as a jar will cause a build error. + return ctx.SnapshotBuilder().EmptyFile() + }, + func(osPrefix, name string) string { + // Create a special name for the implementation jar to try and provide some useful information + // to a developer that attempts to compile against this. + // TODO(b/175714559): Provide a proper error message in Soong not ninja. + return filepath.Join(osPrefix, "java_boot_libs", "snapshot", "jars", "are", "invalid", name+jarFileSuffix) + }, + onlyCopyJarToSnapshot, + } + + // Supports adding java systemserver libraries to module_exports and sdk. + // + // The build has some implicit dependencies (via the systemserver jars configuration) on a number + // of modules that are part of the java systemserver classpath and which are provided by mainline + // modules but which are not otherwise used outside those mainline modules. + // + // As they are not needed outside the mainline modules adding them to the sdk/module-exports as + // either java_libs, or java_header_libs would end up exporting more information than was strictly + // necessary. The java_systemserver_libs property to allow those modules to be exported as part of + // the sdk/module_exports without exposing any unnecessary information. + javaSystemserverLibsSdkMemberType = &librarySdkMemberType{ + android.SdkMemberTypeBase{ + PropertyName: "java_systemserver_libs", + SupportsSdk: true, + }, + func(ctx android.SdkMemberContext, j *Library) android.Path { + // Java systemserver libs are only provided in the SDK to provide access to their dex + // implementation jar for use by dexpreopting. They do not need to provide an actual + // implementation jar but the java_import will need a file that exists so just copy an empty + // file. Any attempt to use that file as a jar will cause a build error. + return ctx.SnapshotBuilder().EmptyFile() + }, + func(osPrefix, name string) string { + // Create a special name for the implementation jar to try and provide some useful information + // to a developer that attempts to compile against this. + // TODO(b/175714559): Provide a proper error message in Soong not ninja. + return filepath.Join(osPrefix, "java_systemserver_libs", "snapshot", "jars", "are", "invalid", name+jarFileSuffix) + }, onlyCopyJarToSnapshot, } @@ -212,7 +254,7 @@ type ApexDependency interface { // Provides build path and install path to DEX jars. type UsesLibraryDependency interface { - DexJarBuildPath() android.Path + DexJarBuildPath() OptionalDexJarPath DexJarInstallPath() android.Path ClassLoaderContexts() dexpreopt.ClassLoaderContextMap } @@ -229,6 +271,12 @@ func (j *Module) XrefJavaFiles() android.Paths { type dependencyTag struct { blueprint.BaseDependencyTag name string + + // True if the dependency is relinked at runtime. + runtimeLinked bool + + // True if the dependency is a toolchain, for example an annotation processor. + toolchain bool } // installDependencyTag is a dependency tag that is annotated to cause the installed files of the @@ -239,15 +287,40 @@ type installDependencyTag struct { name string } +func (d dependencyTag) LicenseAnnotations() []android.LicenseAnnotation { + if d.runtimeLinked { + return []android.LicenseAnnotation{android.LicenseAnnotationSharedDependency} + } else if d.toolchain { + return []android.LicenseAnnotation{android.LicenseAnnotationToolchain} + } + return nil +} + +var _ android.LicenseAnnotationsDependencyTag = dependencyTag{} + type usesLibraryDependencyTag struct { dependencyTag - sdkVersion int // SDK version in which the library appared as a standalone library. + + // 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 } -func makeUsesLibraryDependencyTag(sdkVersion int) usesLibraryDependencyTag { +func makeUsesLibraryDependencyTag(sdkVersion int, optional bool, implicit bool) usesLibraryDependencyTag { return usesLibraryDependencyTag{ - dependencyTag: dependencyTag{name: fmt.Sprintf("uses-library-%d", sdkVersion)}, - sdkVersion: sdkVersion, + dependencyTag: dependencyTag{ + name: fmt.Sprintf("uses-library-%d", sdkVersion), + runtimeLinked: true, + }, + sdkVersion: sdkVersion, + optional: optional, + implicit: implicit, } } @@ -257,29 +330,27 @@ func IsJniDepTag(depTag blueprint.DependencyTag) bool { var ( dataNativeBinsTag = dependencyTag{name: "dataNativeBins"} + dataDeviceBinsTag = dependencyTag{name: "dataDeviceBins"} staticLibTag = dependencyTag{name: "staticlib"} - libTag = dependencyTag{name: "javalib"} - java9LibTag = dependencyTag{name: "java9lib"} - pluginTag = dependencyTag{name: "plugin"} - errorpronePluginTag = dependencyTag{name: "errorprone-plugin"} - exportedPluginTag = dependencyTag{name: "exported-plugin"} - bootClasspathTag = dependencyTag{name: "bootclasspath"} - systemModulesTag = dependencyTag{name: "system modules"} + libTag = dependencyTag{name: "javalib", runtimeLinked: true} + java9LibTag = dependencyTag{name: "java9lib", runtimeLinked: true} + pluginTag = dependencyTag{name: "plugin", toolchain: true} + errorpronePluginTag = dependencyTag{name: "errorprone-plugin", toolchain: true} + exportedPluginTag = dependencyTag{name: "exported-plugin", toolchain: true} + bootClasspathTag = dependencyTag{name: "bootclasspath", runtimeLinked: true} + systemModulesTag = dependencyTag{name: "system modules", runtimeLinked: true} frameworkResTag = dependencyTag{name: "framework-res"} - kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib"} - kotlinAnnotationsTag = dependencyTag{name: "kotlin-annotations"} + kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib", runtimeLinked: true} + kotlinAnnotationsTag = dependencyTag{name: "kotlin-annotations", runtimeLinked: true} + kotlinPluginTag = dependencyTag{name: "kotlin-plugin", toolchain: true} proguardRaiseTag = dependencyTag{name: "proguard-raise"} certificateTag = dependencyTag{name: "certificate"} instrumentationForTag = dependencyTag{name: "instrumentation_for"} - extraLintCheckTag = dependencyTag{name: "extra-lint-check"} - jniLibTag = dependencyTag{name: "jnilib"} + extraLintCheckTag = dependencyTag{name: "extra-lint-check", toolchain: true} + jniLibTag = dependencyTag{name: "jnilib", runtimeLinked: true} syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"} jniInstallTag = installDependencyTag{name: "jni install"} binaryInstallTag = installDependencyTag{name: "binary install"} - usesLibTag = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion) - usesLibCompat28Tag = makeUsesLibraryDependencyTag(28) - usesLibCompat29Tag = makeUsesLibraryDependencyTag(29) - usesLibCompat30Tag = makeUsesLibraryDependencyTag(30) ) func IsLibDepTag(depTag blueprint.DependencyTag) bool { @@ -350,9 +421,25 @@ func sdkDeps(ctx android.BottomUpMutatorContext, sdkContext android.SdkContext, } type deps struct { - classpath classpath - java9Classpath classpath - bootClasspath classpath + // bootClasspath is the list of jars that form the boot classpath (generally the java.* and + // android.* classes) for tools that still use it. javac targeting 1.9 or higher uses + // systemModules and java9Classpath instead. + bootClasspath classpath + + // classpath is the list of jars that form the classpath for javac and kotlinc rules. It + // contains header jars for all static and non-static dependencies. + classpath classpath + + // dexClasspath is the list of jars that form the classpath for d8 and r8 rules. It contains + // header jars for all non-static dependencies. Static dependencies have already been + // combined into the program jar. + dexClasspath classpath + + // java9Classpath is the list of jars that will be added to the classpath when targeting + // 1.9 or higher. It generally contains the android.* classes, while the java.* classes + // are provided by systemModules. + java9Classpath classpath + processorPath classpath errorProneProcessorPath classpath processorClasses []string @@ -366,6 +453,7 @@ type deps struct { aidlPreprocess android.OptionalPath kotlinStdlib android.Paths kotlinAnnotations android.Paths + kotlinPlugins android.Paths disableTurbine bool } @@ -385,7 +473,7 @@ func getJavaVersion(ctx android.ModuleContext, javaVersion string, sdkContext an } else if ctx.Device() { return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion(ctx)) } else { - return JAVA_VERSION_9 + return JAVA_VERSION_11 } } @@ -397,6 +485,7 @@ const ( JAVA_VERSION_7 = 7 JAVA_VERSION_8 = 8 JAVA_VERSION_9 = 9 + JAVA_VERSION_11 = 11 ) func (v javaVersion) String() string { @@ -409,6 +498,8 @@ func (v javaVersion) String() string { return "1.8" case JAVA_VERSION_9: return "1.9" + case JAVA_VERSION_11: + return "11" default: return "unsupported" } @@ -429,8 +520,10 @@ func normalizeJavaVersion(ctx android.BaseModuleContext, javaVersion string) jav return JAVA_VERSION_8 case "1.9", "9": return JAVA_VERSION_9 - case "10", "11": - ctx.PropertyErrorf("java_version", "Java language levels above 9 are not supported") + case "11": + return JAVA_VERSION_11 + case "10": + ctx.PropertyErrorf("java_version", "Java language levels 10 is not supported") return JAVA_VERSION_UNSUPPORTED default: ctx.PropertyErrorf("java_version", "Unrecognized Java language level") @@ -450,7 +543,7 @@ type Library struct { var _ android.ApexModule = (*Library)(nil) -// Provides access to the list of permitted packages from updatable boot jars. +// Provides access to the list of permitted packages from apex boot jars. type PermittedPackagesForUpdatableBootJars interface { PermittedPackagesForUpdatableBootJars() []string } @@ -473,7 +566,7 @@ func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bo } // Store uncompressed dex files that are preopted on /system. - if !dexpreopter.dexpreoptDisabled(ctx) && (ctx.Host() || !odexOnSystemOther(ctx, dexpreopter.installPath)) { + if !dexpreopter.dexpreoptDisabled(ctx) && (ctx.Host() || !dexpreopter.odexOnSystemOther(ctx, dexpreopter.installPath)) { return true } if ctx.Config().UncompressPrivAppDex() && @@ -484,9 +577,18 @@ func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bo return false } +// Sets `dexer.dexProperties.Uncompress_dex` to the proper value. +func setUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter, dexer *dexer) { + if dexer.dexProperties.Uncompress_dex == nil { + // If the value was not force-set by the user, use reasonable default based on the module. + dexer.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, dexpreopter)) + } +} + func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.sdkVersion = j.SdkVersion(ctx) j.minSdkVersion = j.MinSdkVersion(ctx) + j.maxSdkVersion = j.MaxSdkVersion(ctx) apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) if !apexInfo.IsForPlatform() { @@ -494,14 +596,12 @@ func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { } j.checkSdkVersions(ctx) - j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar") + j.dexpreopter.installPath = j.dexpreopter.getInstallPath( + ctx, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")) j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary - if j.dexProperties.Uncompress_dex == nil { - // If the value was not force-set by the user, use reasonable default based on the module. - j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter)) - } + setUncompressDex(ctx, &j.dexpreopter, &j.dexer) j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex - j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap) + j.classLoaderContexts = j.usesLibrary.classLoaderContextForUsesLibDeps(ctx) j.compile(ctx, nil) // Collect the module directory for IDE info in java/jdeps.go. @@ -513,13 +613,29 @@ func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { if j.InstallMixin != nil { extraInstallDeps = j.InstallMixin(ctx, j.outputFile) } - j.installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"), - j.Stem()+".jar", j.outputFile, extraInstallDeps...) + hostDexNeeded := Bool(j.deviceProperties.Hostdex) && !ctx.Host() + if hostDexNeeded { + j.hostdexInstallFile = ctx.InstallFile( + android.PathForHostDexInstall(ctx, "framework"), + j.Stem()+"-hostdex.jar", j.outputFile) + } + var installDir android.InstallPath + if ctx.InstallInTestcases() { + var archDir string + if !ctx.Host() { + archDir = ctx.DeviceConfig().DeviceArch() + } + installDir = android.PathForModuleInstall(ctx, ctx.ModuleName(), archDir) + } else { + installDir = android.PathForModuleInstall(ctx, "framework") + } + j.installFile = ctx.InstallFile(installDir, j.Stem()+".jar", j.outputFile, extraInstallDeps...) } } func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) { j.deps(ctx) + j.usesLibrary.deps(ctx, false) } const ( @@ -559,8 +675,8 @@ const ( copyEverythingToSnapshot = false ) -func (mt *librarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) { - mctx.AddVariationDependencies(nil, dependencyTag, names...) +func (mt *librarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) { + ctx.AddVariationDependencies(nil, dependencyTag, names...) } func (mt *librarySdkMemberType) IsInstance(module android.Module) bool { @@ -657,6 +773,7 @@ func LibraryFactory() android.Module { android.InitApexModule(module) android.InitSdkAwareModule(module) + android.InitBazelModule(module) InitJavaModule(module, android.HostAndDeviceSupported) return module } @@ -679,6 +796,7 @@ func LibraryHostFactory() android.Module { android.InitApexModule(module) android.InitSdkAwareModule(module) + android.InitBazelModule(module) InitJavaModule(module, android.HostSupported) return module } @@ -727,17 +845,44 @@ type testProperties struct { // Names of modules containing JNI libraries that should be installed alongside the test. Jni_libs []string + + // Install the test into a folder named for the module in all test suites. + Per_testcase_directory *bool } type hostTestProperties struct { // list of native binary modules that should be installed alongside the test Data_native_bins []string `android:"arch_variant"` + + // list of device binary modules that should be installed alongside the test + // This property only adds the first variant of the dependency + Data_device_bins_first []string `android:"arch_variant"` + + // list of device binary modules that should be installed alongside the test + // This property adds 64bit AND 32bit variants of the dependency + Data_device_bins_both []string `android:"arch_variant"` + + // list of device binary modules that should be installed alongside the test + // This property only adds 64bit variants of the dependency + Data_device_bins_64 []string `android:"arch_variant"` + + // list of device binary modules that should be installed alongside the test + // This property adds 32bit variants of the dependency if available, or else + // defaults to the 64bit variant + Data_device_bins_prefer32 []string `android:"arch_variant"` + + // list of device binary modules that should be installed alongside the test + // This property only adds 32bit variants of the dependency + Data_device_bins_32 []string `android:"arch_variant"` } type testHelperLibraryProperties struct { // list of compatibility suites (for example "cts", "vts") that the module should be // installed into. Test_suites []string `android:"arch_variant"` + + // Install the test into a folder named for the module in all test suites. + Per_testcase_directory *bool } type prebuiltTestProperties struct { @@ -781,6 +926,97 @@ type JavaTestImport struct { dexJarFile android.Path } +func (j *Test) InstallInTestcases() bool { + // Host java tests install into $(HOST_OUT_JAVA_LIBRARIES), and then are copied into + // testcases by base_rules.mk. + return !j.Host() +} + +func (j *TestHelperLibrary) InstallInTestcases() bool { + return true +} + +func (j *JavaTestImport) InstallInTestcases() bool { + return true +} + +func (j *TestHost) addDataDeviceBinsDeps(ctx android.BottomUpMutatorContext) { + if len(j.testHostProperties.Data_device_bins_first) > 0 { + deviceVariations := ctx.Config().AndroidFirstDeviceTarget.Variations() + ctx.AddFarVariationDependencies(deviceVariations, dataDeviceBinsTag, j.testHostProperties.Data_device_bins_first...) + } + + var maybeAndroid32Target *android.Target + var maybeAndroid64Target *android.Target + android32TargetList := android.FirstTarget(ctx.Config().Targets[android.Android], "lib32") + android64TargetList := android.FirstTarget(ctx.Config().Targets[android.Android], "lib64") + if len(android32TargetList) > 0 { + maybeAndroid32Target = &android32TargetList[0] + } + if len(android64TargetList) > 0 { + maybeAndroid64Target = &android64TargetList[0] + } + + if len(j.testHostProperties.Data_device_bins_both) > 0 { + if maybeAndroid32Target == nil && maybeAndroid64Target == nil { + ctx.PropertyErrorf("data_device_bins_both", "no device targets available. Targets: %q", ctx.Config().Targets) + return + } + if maybeAndroid32Target != nil { + ctx.AddFarVariationDependencies( + maybeAndroid32Target.Variations(), + dataDeviceBinsTag, + j.testHostProperties.Data_device_bins_both..., + ) + } + if maybeAndroid64Target != nil { + ctx.AddFarVariationDependencies( + maybeAndroid64Target.Variations(), + dataDeviceBinsTag, + j.testHostProperties.Data_device_bins_both..., + ) + } + } + + if len(j.testHostProperties.Data_device_bins_prefer32) > 0 { + if maybeAndroid32Target != nil { + ctx.AddFarVariationDependencies( + maybeAndroid32Target.Variations(), + dataDeviceBinsTag, + j.testHostProperties.Data_device_bins_prefer32..., + ) + } else { + if maybeAndroid64Target == nil { + ctx.PropertyErrorf("data_device_bins_prefer32", "no device targets available. Targets: %q", ctx.Config().Targets) + return + } + ctx.AddFarVariationDependencies( + maybeAndroid64Target.Variations(), + dataDeviceBinsTag, + j.testHostProperties.Data_device_bins_prefer32..., + ) + } + } + + if len(j.testHostProperties.Data_device_bins_32) > 0 { + if maybeAndroid32Target == nil { + ctx.PropertyErrorf("data_device_bins_32", "cannot find 32bit device target. Targets: %q", ctx.Config().Targets) + return + } + deviceVariations := maybeAndroid32Target.Variations() + ctx.AddFarVariationDependencies(deviceVariations, dataDeviceBinsTag, j.testHostProperties.Data_device_bins_32...) + } + + if len(j.testHostProperties.Data_device_bins_64) > 0 { + if maybeAndroid64Target == nil { + ctx.PropertyErrorf("data_device_bins_64", "cannot find 64bit device target. Targets: %q", ctx.Config().Targets) + return + } + deviceVariations := maybeAndroid64Target.Variations() + ctx.AddFarVariationDependencies(deviceVariations, dataDeviceBinsTag, j.testHostProperties.Data_device_bins_64...) + } +} + func (j *TestHost) DepsMutator(ctx android.BottomUpMutatorContext) { if len(j.testHostProperties.Data_native_bins) > 0 { for _, target := range ctx.MultiTargets() { @@ -795,6 +1031,8 @@ func (j *TestHost) DepsMutator(ctx android.BottomUpMutatorContext) { } } + j.addDataDeviceBinsDeps(ctx) + j.deps(ctx) } @@ -802,14 +1040,58 @@ func (j *TestHost) AddExtraResource(p android.Path) { j.extraResources = append(j.extraResources, p) } +func (j *TestHost) dataDeviceBins() []string { + ret := make([]string, 0, + len(j.testHostProperties.Data_device_bins_first)+ + len(j.testHostProperties.Data_device_bins_both)+ + len(j.testHostProperties.Data_device_bins_prefer32)+ + len(j.testHostProperties.Data_device_bins_32)+ + len(j.testHostProperties.Data_device_bins_64), + ) + + ret = append(ret, j.testHostProperties.Data_device_bins_first...) + ret = append(ret, j.testHostProperties.Data_device_bins_both...) + ret = append(ret, j.testHostProperties.Data_device_bins_prefer32...) + ret = append(ret, j.testHostProperties.Data_device_bins_32...) + ret = append(ret, j.testHostProperties.Data_device_bins_64...) + + return ret +} + +func (j *TestHost) GenerateAndroidBuildActions(ctx android.ModuleContext) { + var configs []tradefed.Config + dataDeviceBins := j.dataDeviceBins() + if len(dataDeviceBins) > 0 { + // add Tradefed configuration to push device bins to device for testing + remoteDir := filepath.Join("/data/local/tests/unrestricted/", j.Name()) + options := []tradefed.Option{{Name: "cleanup", Value: "true"}} + for _, bin := range dataDeviceBins { + fullPath := filepath.Join(remoteDir, bin) + options = append(options, tradefed.Option{Name: "push-file", Key: bin, Value: fullPath}) + } + configs = append(configs, tradefed.Object{ + Type: "target_preparer", + Class: "com.android.tradefed.targetprep.PushFilePreparer", + Options: options, + }) + } + + j.Test.generateAndroidBuildActionsWithConfig(ctx, configs) +} + func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) { + j.generateAndroidBuildActionsWithConfig(ctx, nil) +} + +func (j *Test) generateAndroidBuildActionsWithConfig(ctx android.ModuleContext, configs []tradefed.Config) { if j.testProperties.Test_options.Unit_test == nil && ctx.Host() { // TODO(b/): Clean temporary heuristic to avoid unexpected onboarding. defaultUnitTest := !inList("tradefed", j.properties.Libs) && !inList("cts", j.testProperties.Test_suites) j.testProperties.Test_options.Unit_test = proptools.BoolPtr(defaultUnitTest) } + j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template, - j.testProperties.Test_suites, j.testProperties.Auto_gen_config, j.testProperties.Test_options.Unit_test) + j.testProperties.Test_suites, configs, j.testProperties.Auto_gen_config, j.testProperties.Test_options.Unit_test) j.data = android.PathsForModuleSrc(ctx, j.testProperties.Data) @@ -819,6 +1101,10 @@ func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.data = append(j.data, android.OutputFileForModule(ctx, dep, "")) }) + ctx.VisitDirectDepsWithTag(dataDeviceBinsTag, func(dep android.Module) { + j.data = append(j.data, android.OutputFileForModule(ctx, dep, "")) + }) + ctx.VisitDirectDepsWithTag(jniLibTag, func(dep android.Module) { sharedLibInfo := ctx.OtherModuleProvider(dep, cc.SharedLibraryInfoProvider).(cc.SharedLibraryInfo) if sharedLibInfo.SharedLibrary != nil { @@ -851,7 +1137,7 @@ func (j *TestHelperLibrary) GenerateAndroidBuildActions(ctx android.ModuleContex func (j *JavaTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.prebuiltTestProperties.Test_config, nil, - j.prebuiltTestProperties.Test_suites, nil, nil) + j.prebuiltTestProperties.Test_suites, nil, nil, nil) j.Import.GenerateAndroidBuildActions(ctx) } @@ -860,8 +1146,8 @@ type testSdkMemberType struct { android.SdkMemberTypeBase } -func (mt *testSdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) { - mctx.AddVariationDependencies(nil, dependencyTag, names...) +func (mt *testSdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) { + ctx.AddVariationDependencies(nil, dependencyTag, names...) } func (mt *testSdkMemberType) IsInstance(module android.Module) bool { @@ -1012,14 +1298,14 @@ func InitTestHost(th *TestHost, installable *bool, testSuites []string, autoGenC type binaryProperties struct { // installable script to execute the resulting jar - Wrapper *string `android:"path"` + Wrapper *string `android:"path,arch_variant"` // Name of the class containing main to be inserted into the manifest as Main-Class. Main_class *string // Names of modules containing JNI libraries that should be installed alongside the host // variant of the binary. - Jni_libs []string + Jni_libs []string `android:"arch_variant"` } type Binary struct { @@ -1057,14 +1343,23 @@ func (j *Binary) GenerateAndroidBuildActions(ctx android.ModuleContext) { if j.binaryProperties.Wrapper != nil { j.wrapperFile = android.PathForModuleSrc(ctx, *j.binaryProperties.Wrapper) } else { + if ctx.Windows() { + ctx.PropertyErrorf("wrapper", "wrapper is required for Windows") + } + j.wrapperFile = android.PathForSource(ctx, "build/soong/scripts/jar-wrapper.sh") } + ext := "" + if ctx.Windows() { + ext = ".bat" + } + // The host installation rules make the installed wrapper depend on all the dependencies // of the wrapper variant, which will include the common variant's jar file and any JNI // libraries. This is verified by TestBinary. j.binaryFile = ctx.InstallExecutable(android.PathForModuleInstall(ctx, "bin"), - ctx.ModuleName(), j.wrapperFile) + ctx.ModuleName()+ext, j.wrapperFile) } } @@ -1100,6 +1395,8 @@ func BinaryFactory() android.Module { android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommonFirst) android.InitDefaultableModule(module) + android.InitBazelModule(module) + return module } @@ -1117,6 +1414,7 @@ func BinaryHostFactory() android.Module { android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommonFirst) android.InitDefaultableModule(module) + android.InitBazelModule(module) return module } @@ -1138,7 +1436,6 @@ type ImportProperties struct { Installable *bool // If not empty, classes are restricted to the specified packages and their sub-packages. - // This information is used to generate the updatable-bcp-packages.txt file. Permitted_packages []string // List of shared java libs that this module has dependencies to @@ -1167,6 +1464,7 @@ type Import struct { android.ModuleBase android.DefaultableModuleBase android.ApexModuleBase + android.BazelModuleBase prebuilt android.Prebuilt android.SdkBase @@ -1180,7 +1478,8 @@ type Import struct { properties ImportProperties // output file containing classes.dex and resources - dexJarFile android.Path + dexJarFile OptionalDexJarPath + dexJarInstallFile android.Path combinedClasspathFile android.Path classLoaderContexts dexpreopt.ClassLoaderContextMap @@ -1264,6 +1563,10 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.hideApexVariantFromMake = true } + if ctx.Windows() { + j.HideFromMake() + } + jars := android.PathsForModuleSrc(ctx, j.properties.Jars) jarName := j.Stem() + ".jar" @@ -1279,7 +1582,6 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap) var flags javaBuilderFlags - var deapexerModule android.Module ctx.VisitDirectDeps(func(module android.Module) { tag := ctx.OtherModuleDependencyTag(module) @@ -1287,7 +1589,10 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { if ctx.OtherModuleHasProvider(module, JavaInfoProvider) { dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo) switch tag { - case libTag, staticLibTag: + case libTag: + flags.classpath = append(flags.classpath, dep.HeaderJars...) + flags.dexClasspath = append(flags.dexClasspath, dep.HeaderJars...) + case staticLibTag: flags.classpath = append(flags.classpath, dep.HeaderJars...) case bootClasspathTag: flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars...) @@ -1300,20 +1605,20 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { } addCLCFromDep(ctx, module, j.classLoaderContexts) - - // Save away the `deapexer` module on which this depends, if any. - if tag == android.DeapexerTag { - if deapexerModule != nil { - ctx.ModuleErrorf("Ambiguous duplicate deapexer module dependencies %q and %q", - deapexerModule.Name(), module.Name()) - } - deapexerModule = module - } }) if Bool(j.properties.Installable) { - ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"), - jarName, outputFile) + var installDir android.InstallPath + if ctx.InstallInTestcases() { + var archDir string + if !ctx.Host() { + archDir = ctx.DeviceConfig().DeviceArch() + } + installDir = android.PathForModuleInstall(ctx, ctx.ModuleName(), archDir) + } else { + installDir = android.PathForModuleInstall(ctx, "framework") + } + ctx.InstallFile(installDir, jarName, outputFile) } j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs) @@ -1323,25 +1628,28 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { // obtained from the associated deapexer module. ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) if ai.ForPrebuiltApex { - if deapexerModule == nil { - // This should never happen as a variant for a prebuilt_apex is only created if the - // deapexer module has been configured to export the dex implementation jar for this module. - ctx.ModuleErrorf("internal error: module %q does not depend on a `deapexer` module for prebuilt_apex %q", - j.Name(), ai.ApexVariationName) - return - } - // Get the path of the dex implementation jar from the `deapexer` module. - di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo) + di := android.FindDeapexerProviderForModule(ctx) + if di == nil { + return // An error has been reported by FindDeapexerProviderForModule. + } if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(j.BaseModuleName())); dexOutputPath != nil { - j.dexJarFile = dexOutputPath + dexJarFile := makeDexJarPathFromPath(dexOutputPath) + j.dexJarFile = dexJarFile + installPath := android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(j.BaseModuleName())) + j.dexJarInstallFile = installPath + + j.dexpreopter.installPath = j.dexpreopter.getInstallPath(ctx, installPath) + setUncompressDex(ctx, &j.dexpreopter, &j.dexer) + j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex + j.dexpreopt(ctx, dexOutputPath) // Initialize the hiddenapi structure. - j.initHiddenAPI(ctx, dexOutputPath, outputFile, nil) + j.initHiddenAPI(ctx, dexJarFile, outputFile, j.dexProperties.Uncompress_dex) } else { // This should never happen as a variant for a prebuilt_apex is only created if the // prebuilt_apex has been configured to export the java library dex file. - ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt_apex %q", deapexerModule.Name()) + ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt APEX %s", di.ApexModuleName()) } } else if Bool(j.dexProperties.Compile_dex) { sdkDep := decodeSdkDep(ctx, android.SdkContext(j)) @@ -1355,11 +1663,9 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { // Dex compilation - j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", jarName) - if j.dexProperties.Uncompress_dex == nil { - // If the value was not force-set by the user, use reasonable default based on the module. - j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter)) - } + j.dexpreopter.installPath = j.dexpreopter.getInstallPath( + ctx, android.PathForModuleInstall(ctx, "framework", jarName)) + setUncompressDex(ctx, &j.dexpreopter, &j.dexer) j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex var dexOutputFile android.OutputPath @@ -1369,12 +1675,13 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { } // Initialize the hiddenapi structure. - j.initHiddenAPI(ctx, dexOutputFile, outputFile, j.dexProperties.Uncompress_dex) + j.initHiddenAPI(ctx, makeDexJarPathFromPath(dexOutputFile), outputFile, j.dexProperties.Uncompress_dex) // Encode hidden API flags in dex file. dexOutputFile = j.hiddenAPIEncodeDex(ctx, dexOutputFile) - j.dexJarFile = dexOutputFile + j.dexJarFile = makeDexJarPathFromPath(dexOutputFile) + j.dexJarInstallFile = android.PathForModuleInstall(ctx, "framework", jarName) } } @@ -1411,12 +1718,12 @@ func (j *Import) ImplementationAndResourcesJars() android.Paths { return android.Paths{j.combinedClasspathFile} } -func (j *Import) DexJarBuildPath() android.Path { +func (j *Import) DexJarBuildPath() OptionalDexJarPath { return j.dexJarFile } func (j *Import) DexJarInstallPath() android.Path { - return nil + return j.dexJarInstallFile } func (j *Import) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap { @@ -1440,12 +1747,8 @@ func (j *Import) ShouldSupportSdkVersion(ctx android.BaseModuleContext, if sdkSpec.Kind == android.SdkCore { return nil } - ver, err := sdkSpec.EffectiveVersion(ctx) - if err != nil { - return err - } - if ver.GreaterThan(sdkVersion) { - return fmt.Errorf("newer SDK(%v)", ver) + if sdkSpec.ApiLevel.GreaterThan(sdkVersion) { + return fmt.Errorf("newer SDK(%v)", sdkSpec.ApiLevel) } return nil } @@ -1478,9 +1781,6 @@ var _ android.IDEInfo = (*Import)(nil) var _ android.IDECustomizedModuleName = (*Import)(nil) // Collect information for opening IDE project files in java/jdeps.go. -const ( - removedPrefix = "prebuilt_" -) func (j *Import) IDEInfo(dpInfo *android.IdeInfo) { dpInfo.Jars = append(dpInfo.Jars, j.PrebuiltSrcs()...) @@ -1490,11 +1790,7 @@ func (j *Import) IDECustomizedModuleName() string { // TODO(b/113562217): Extract the base module name from the Import name, often the Import name // has a prefix "prebuilt_". Remove the prefix explicitly if needed until we find a better // solution to get the Import name. - name := j.Name() - if strings.HasPrefix(name, removedPrefix) { - name = strings.TrimPrefix(name, removedPrefix) - } - return name + return android.RemoveOptionalPrebuiltPrefix(j.Name()) } var _ android.PrebuiltInterface = (*Import)(nil) @@ -1503,7 +1799,7 @@ func (j *Import) IsInstallable() bool { return Bool(j.properties.Installable) } -var _ dexpreopterInterface = (*Import)(nil) +var _ DexpreopterInterface = (*Import)(nil) // java_import imports one or more `.jar` files into the build graph as if they were built by a java_library module. // @@ -1527,6 +1823,7 @@ func ImportFactory() android.Module { android.InitPrebuiltModule(module, &module.properties.Jars) android.InitApexModule(module) android.InitSdkAwareModule(module) + android.InitBazelModule(module) InitJavaModule(module, android.HostAndDeviceSupported) return module } @@ -1543,6 +1840,7 @@ func ImportFactoryHost() android.Module { android.InitPrebuiltModule(module, &module.properties.Jars) android.InitApexModule(module) + android.InitBazelModule(module) InitJavaModule(module, android.HostSupported) return module } @@ -1564,7 +1862,7 @@ type DexImport struct { properties DexImportProperties - dexJarFile android.Path + dexJarFile OptionalDexJarPath dexpreopter @@ -1616,7 +1914,8 @@ func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.hideApexVariantFromMake = true } - j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar") + j.dexpreopter.installPath = j.dexpreopter.getInstallPath( + ctx, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")) j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter) inputJar := ctx.ExpandSource(j.properties.Jars[0], "jars") @@ -1654,7 +1953,7 @@ func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { }) } - j.dexJarFile = dexOutputFile + j.dexJarFile = makeDexJarPathFromPath(dexOutputFile) j.dexpreopt(ctx, dexOutputFile) @@ -1664,7 +1963,7 @@ func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { } } -func (j *DexImport) DexJarBuildPath() android.Path { +func (j *DexImport) DexJarBuildPath() OptionalDexJarPath { return j.dexJarFile } @@ -1738,6 +2037,7 @@ func DefaultsFactory() android.Module { module.AddProperties( &CommonProperties{}, &DeviceProperties{}, + &OverridableDeviceProperties{}, &DexProperties{}, &DexpreoptProperties{}, &android.ProtoProperties{}, @@ -1796,32 +2096,30 @@ func addCLCFromDep(ctx android.ModuleContext, depModule android.Module, return } - // Find out if the dependency is either an SDK library or an ordinary library that is disguised - // as an SDK library by the means of `provides_uses_lib` property. If yes, the library is itself - // a <uses-library> and should be added as a node in the CLC tree, and its CLC should be added - // as subtree of that node. Otherwise the library is not a <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. - var implicitSdkLib *string - comp, isComp := depModule.(SdkLibraryComponentDependency) - if isComp { - implicitSdkLib = comp.OptionalImplicitSdkLibrary() - // OptionalImplicitSdkLibrary() may be nil so need to fall through to ProvidesUsesLib(). - } - if implicitSdkLib == nil { - if ulib, ok := depModule.(ProvidesUsesLib); ok { - implicitSdkLib = ulib.ProvidesUsesLib() - } + depName := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(depModule)) + + var sdkLib *string + if lib, ok := depModule.(SdkLibraryDependency); ok && lib.sharedLibrary() { + // A shared SDK library. This should be added as a top-level CLC element. + sdkLib = &depName + } else if ulib, ok := depModule.(ProvidesUsesLib); ok { + // A non-SDK library disguised as an SDK library by the means of `provides_uses_lib` + // property. This should be handled in the same way as a shared SDK library. + sdkLib = ulib.ProvidesUsesLib() } depTag := ctx.OtherModuleDependencyTag(depModule) - if depTag == libTag || depTag == usesLibTag { + 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 depTag == staticLibTag { // Propagate <uses-library> through static library dependencies, unless it is a component // library (such as stubs). Component libraries have a dependency on their SDK library, // which should not be pulled just because of a static component library. - if implicitSdkLib != nil { + if sdkLib != nil { return } } else { @@ -1829,11 +2127,260 @@ func addCLCFromDep(ctx android.ModuleContext, depModule android.Module, return } - if implicitSdkLib != nil { - clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *implicitSdkLib, - dep.DexJarBuildPath(), dep.DexJarInstallPath(), dep.ClassLoaderContexts()) + // If this is an SDK (or SDK-like) library, then it should be added as a node in the CLC tree, + // and its CLC should be added as subtree of that node. Otherwise the library is not a + // <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, + dep.DexJarBuildPath().PathOrNil(), dep.DexJarInstallPath(), dep.ClassLoaderContexts()) } else { - depName := ctx.OtherModuleName(depModule) clcMap.AddContextMap(dep.ClassLoaderContexts(), depName) } } + +type javaCommonAttributes struct { + Srcs bazel.LabelListAttribute + Plugins bazel.LabelListAttribute + Javacopts bazel.StringListAttribute +} + +type javaDependencyLabels struct { + // Dependencies which DO NOT contribute to the API visible to upstream dependencies. + Deps bazel.LabelListAttribute + // Dependencies which DO contribute to the API visible to upstream dependencies. + StaticDeps bazel.LabelListAttribute +} + +// convertLibraryAttrsBp2Build converts a few shared attributes from java_* modules +// and also separates dependencies into dynamic dependencies and static dependencies. +// Each corresponding Bazel target type, can have a different method for handling +// dynamic vs. static dependencies, and so these are returned to the calling function. +type eventLogTagsAttributes struct { + Srcs bazel.LabelListAttribute +} + +func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) (*javaCommonAttributes, *javaDependencyLabels) { + var srcs bazel.LabelListAttribute + archVariantProps := m.GetArchVariantProperties(ctx, &CommonProperties{}) + for axis, configToProps := range archVariantProps { + for config, _props := range configToProps { + if archProps, ok := _props.(*CommonProperties); ok { + archSrcs := android.BazelLabelForModuleSrcExcludes(ctx, archProps.Srcs, archProps.Exclude_srcs) + srcs.SetSelectValue(axis, config, archSrcs) + } + } + } + + javaSrcPartition := "java" + protoSrcPartition := "proto" + logtagSrcPartition := "logtag" + srcPartitions := bazel.PartitionLabelListAttribute(ctx, &srcs, bazel.LabelPartitions{ + javaSrcPartition: bazel.LabelPartition{Extensions: []string{".java"}, Keep_remainder: true}, + logtagSrcPartition: bazel.LabelPartition{Extensions: []string{".logtags", ".logtag"}}, + protoSrcPartition: android.ProtoSrcLabelPartition, + }) + + javaSrcs := srcPartitions[javaSrcPartition] + + var logtagsSrcs bazel.LabelList + if !srcPartitions[logtagSrcPartition].IsEmpty() { + logtagsLibName := m.Name() + "_logtags" + logtagsSrcs = bazel.MakeLabelList([]bazel.Label{{Label: ":" + logtagsLibName}}) + ctx.CreateBazelTargetModule( + bazel.BazelTargetModuleProperties{ + Rule_class: "event_log_tags", + Bzl_load_location: "//build/make/tools:event_log_tags.bzl", + }, + android.CommonAttributes{Name: logtagsLibName}, + &eventLogTagsAttributes{ + Srcs: srcPartitions[logtagSrcPartition], + }, + ) + } + javaSrcs.Append(bazel.MakeLabelListAttribute(logtagsSrcs)) + + var javacopts []string + if m.properties.Javacflags != nil { + javacopts = append(javacopts, m.properties.Javacflags...) + } + epEnabled := m.properties.Errorprone.Enabled + //TODO(b/227504307) add configuration that depends on RUN_ERROR_PRONE environment variable + if Bool(epEnabled) { + javacopts = append(javacopts, m.properties.Errorprone.Javacflags...) + } + + commonAttrs := &javaCommonAttributes{ + Srcs: javaSrcs, + Plugins: bazel.MakeLabelListAttribute( + android.BazelLabelForModuleDeps(ctx, m.properties.Plugins), + ), + Javacopts: bazel.MakeStringListAttribute(javacopts), + } + + depLabels := &javaDependencyLabels{} + + var deps bazel.LabelList + if m.properties.Libs != nil { + deps.Append(android.BazelLabelForModuleDeps(ctx, m.properties.Libs)) + } + + var staticDeps bazel.LabelList + if m.properties.Static_libs != nil { + staticDeps.Append(android.BazelLabelForModuleDeps(ctx, m.properties.Static_libs)) + } + + protoDepLabel := bp2buildProto(ctx, &m.Module, srcPartitions[protoSrcPartition]) + // Soong does not differentiate between a java_library and the Bazel equivalent of + // a java_proto_library + proto_library pair. Instead, in Soong proto sources are + // listed directly in the srcs of a java_library, and the classes produced + // by protoc are included directly in the resulting JAR. Thus upstream dependencies + // that depend on a java_library with proto sources can link directly to the protobuf API, + // and so this should be a static dependency. + staticDeps.Add(protoDepLabel) + + depLabels.Deps = bazel.MakeLabelListAttribute(deps) + depLabels.StaticDeps = bazel.MakeLabelListAttribute(staticDeps) + + return commonAttrs, depLabels +} + +type javaLibraryAttributes struct { + *javaCommonAttributes + Deps bazel.LabelListAttribute + Exports bazel.LabelListAttribute +} + +func javaLibraryBp2Build(ctx android.TopDownMutatorContext, m *Library) { + commonAttrs, depLabels := m.convertLibraryAttrsBp2Build(ctx) + + deps := depLabels.Deps + if !commonAttrs.Srcs.IsEmpty() { + deps.Append(depLabels.StaticDeps) // we should only append these if there are sources to use them + + sdkVersion := m.SdkVersion(ctx) + if sdkVersion.Kind == android.SdkPublic && sdkVersion.ApiLevel == android.FutureApiLevel { + // TODO(b/220869005) remove forced dependency on current public android.jar + deps.Add(bazel.MakeLabelAttribute("//prebuilts/sdk:public_current_android_sdk_java_import")) + } + } else if !depLabels.Deps.IsEmpty() { + ctx.ModuleErrorf("Module has direct dependencies but no sources. Bazel will not allow this.") + } + + attrs := &javaLibraryAttributes{ + javaCommonAttributes: commonAttrs, + Deps: deps, + Exports: depLabels.StaticDeps, + } + + props := bazel.BazelTargetModuleProperties{ + Rule_class: "java_library", + Bzl_load_location: "//build/bazel/rules/java:library.bzl", + } + + ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs) +} + +type javaBinaryHostAttributes struct { + *javaCommonAttributes + Deps bazel.LabelListAttribute + Runtime_deps bazel.LabelListAttribute + Main_class string + Jvm_flags bazel.StringListAttribute +} + +// JavaBinaryHostBp2Build is for java_binary_host bp2build. +func javaBinaryHostBp2Build(ctx android.TopDownMutatorContext, m *Binary) { + commonAttrs, depLabels := m.convertLibraryAttrsBp2Build(ctx) + + deps := depLabels.Deps + deps.Append(depLabels.StaticDeps) + if m.binaryProperties.Jni_libs != nil { + deps.Append(bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, m.binaryProperties.Jni_libs))) + } + + var runtimeDeps bazel.LabelListAttribute + if commonAttrs.Srcs.IsEmpty() { + // if there are no sources, then the dependencies can only be used at runtime + runtimeDeps = deps + deps = bazel.LabelListAttribute{} + } + + mainClass := "" + if m.binaryProperties.Main_class != nil { + mainClass = *m.binaryProperties.Main_class + } + if m.properties.Manifest != nil { + mainClassInManifest, err := android.GetMainClassInManifest(ctx.Config(), android.PathForModuleSrc(ctx, *m.properties.Manifest).String()) + if err != nil { + return + } + mainClass = mainClassInManifest + } + + attrs := &javaBinaryHostAttributes{ + javaCommonAttributes: commonAttrs, + Deps: deps, + Runtime_deps: runtimeDeps, + Main_class: mainClass, + } + + // Attribute jvm_flags + if m.binaryProperties.Jni_libs != nil { + jniLibPackages := map[string]bool{} + for _, jniLibLabel := range android.BazelLabelForModuleDeps(ctx, m.binaryProperties.Jni_libs).Includes { + jniLibPackage := jniLibLabel.Label + indexOfColon := strings.Index(jniLibLabel.Label, ":") + if indexOfColon > 0 { + // JNI lib from other package + jniLibPackage = jniLibLabel.Label[2:indexOfColon] + } else if indexOfColon == 0 { + // JNI lib in the same package of java_binary + packageOfCurrentModule := m.GetBazelLabel(ctx, m) + jniLibPackage = packageOfCurrentModule[2:strings.Index(packageOfCurrentModule, ":")] + } + if _, inMap := jniLibPackages[jniLibPackage]; !inMap { + jniLibPackages[jniLibPackage] = true + } + } + jniLibPaths := []string{} + for jniLibPackage, _ := range jniLibPackages { + // See cs/f:.*/third_party/bazel/.*java_stub_template.txt for the use of RUNPATH + jniLibPaths = append(jniLibPaths, "$${RUNPATH}"+jniLibPackage) + } + attrs.Jvm_flags = bazel.MakeStringListAttribute([]string{"-Djava.library.path=" + strings.Join(jniLibPaths, ":")}) + } + + props := bazel.BazelTargetModuleProperties{ + Rule_class: "java_binary", + } + + // Create the BazelTargetModule. + ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs) +} + +type bazelJavaImportAttributes struct { + Jars bazel.LabelListAttribute +} + +// java_import bp2Build converter. +func (i *Import) ConvertWithBp2build(ctx android.TopDownMutatorContext) { + var jars bazel.LabelListAttribute + archVariantProps := i.GetArchVariantProperties(ctx, &ImportProperties{}) + for axis, configToProps := range archVariantProps { + for config, _props := range configToProps { + if archProps, ok := _props.(*ImportProperties); ok { + archJars := android.BazelLabelForModuleSrcExcludes(ctx, archProps.Jars, []string(nil)) + jars.SetSelectValue(axis, config, archJars) + } + } + } + + attrs := &bazelJavaImportAttributes{ + Jars: jars, + } + props := bazel.BazelTargetModuleProperties{Rule_class: "java_import"} + + ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: android.RemoveOptionalPrebuiltPrefix(i.Name())}, attrs) + +} diff --git a/java/java_test.go b/java/java_test.go index bd373c177..af889ccd4 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -441,7 +441,7 @@ func TestBinary(t *testing.T) { } `) - buildOS := android.BuildOs.String() + buildOS := ctx.Config().BuildOS.String() bar := ctx.ModuleForTests("bar", buildOS+"_common") barJar := bar.Output("bar.jar").Output.String() @@ -478,7 +478,7 @@ func TestTest(t *testing.T) { } `) - buildOS := android.BuildOs.String() + buildOS := ctx.Config().BuildOS.String() foo := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost) @@ -523,7 +523,7 @@ func TestHostBinaryNoJavaDebugInfoOverride(t *testing.T) { } // check that -g is not overridden for host modules - buildOS := android.BuildOs.String() + buildOS := result.Config.BuildOS.String() hostBinary := result.ModuleForTests("host_binary", buildOS+"_common") hostJavaFlags := hostBinary.Module().VariablesForTests()["javacFlags"] if strings.Contains(hostJavaFlags, "-g:source,lines") { @@ -600,8 +600,8 @@ func TestPrebuilts(t *testing.T) { } barDexJar := barModule.Module().(*Import).DexJarBuildPath() - if barDexJar != nil { - t.Errorf("bar dex jar build path expected to be nil, got %q", barDexJar) + if barDexJar.IsSet() { + t.Errorf("bar dex jar build path expected to be set, got %s", barDexJar) } if !strings.Contains(javac.Args["classpath"], sdklibStubsJar.String()) { @@ -612,7 +612,7 @@ func TestPrebuilts(t *testing.T) { t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, bazJar.String()) } - bazDexJar := bazModule.Module().(*Import).DexJarBuildPath() + bazDexJar := bazModule.Module().(*Import).DexJarBuildPath().Path() expectedDexJar := "out/soong/.intermediates/baz/android_common/dex/baz.jar" android.AssertPathRelativeToTopEquals(t, "baz dex jar build path", expectedDexJar, bazDexJar) @@ -973,7 +973,7 @@ func TestTurbine(t *testing.T) { fooHeaderJar := filepath.Join("out", "soong", ".intermediates", "foo", "android_common", "turbine-combined", "foo.jar") barTurbineJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine", "bar.jar") - android.AssertStringDoesContain(t, "bar turbine classpath", barTurbine.Args["classpath"], fooHeaderJar) + android.AssertStringDoesContain(t, "bar turbine classpath", barTurbine.Args["turbineFlags"], fooHeaderJar) android.AssertStringDoesContain(t, "bar javac classpath", barJavac.Args["classpath"], fooHeaderJar) android.AssertPathsRelativeToTopEquals(t, "bar turbine combineJar", []string{barTurbineJar, fooHeaderJar}, barTurbineCombined.Inputs) android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "prebuilts/sdk/14/public/android.jar") @@ -988,69 +988,15 @@ func TestSharding(t *testing.T) { } `) - barHeaderJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar") + barHeaderJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine", "bar.jar") for i := 0; i < 3; i++ { barJavac := ctx.ModuleForTests("bar", "android_common").Description("javac" + strconv.Itoa(i)) - if !strings.Contains(barJavac.Args["classpath"], barHeaderJar) { - t.Errorf("bar javac classpath %v does not contain %q", barJavac.Args["classpath"], barHeaderJar) + if !strings.HasPrefix(barJavac.Args["classpath"], "-classpath "+barHeaderJar+":") { + t.Errorf("bar javac classpath %v does start with %q", barJavac.Args["classpath"], barHeaderJar) } } } -func TestJarGenrules(t *testing.T) { - ctx, _ := testJava(t, ` - java_library { - name: "foo", - srcs: ["a.java"], - } - - java_genrule { - name: "jargen", - tool_files: ["b.java"], - cmd: "$(location b.java) $(in) $(out)", - out: ["jargen.jar"], - srcs: [":foo"], - } - - java_library { - name: "bar", - static_libs: ["jargen"], - srcs: ["c.java"], - } - - java_library { - name: "baz", - libs: ["jargen"], - srcs: ["c.java"], - } - `) - - foo := ctx.ModuleForTests("foo", "android_common").Output("javac/foo.jar") - jargen := ctx.ModuleForTests("jargen", "android_common").Output("jargen.jar") - bar := ctx.ModuleForTests("bar", "android_common").Output("javac/bar.jar") - baz := ctx.ModuleForTests("baz", "android_common").Output("javac/baz.jar") - barCombined := ctx.ModuleForTests("bar", "android_common").Output("combined/bar.jar") - - if g, w := jargen.Implicits.Strings(), foo.Output.String(); !android.InList(w, g) { - t.Errorf("expected jargen inputs [%q], got %q", w, g) - } - - if !strings.Contains(bar.Args["classpath"], jargen.Output.String()) { - t.Errorf("bar classpath %v does not contain %q", bar.Args["classpath"], jargen.Output.String()) - } - - if !strings.Contains(baz.Args["classpath"], jargen.Output.String()) { - t.Errorf("baz classpath %v does not contain %q", baz.Args["classpath"], jargen.Output.String()) - } - - if len(barCombined.Inputs) != 2 || - barCombined.Inputs[0].String() != bar.Output.String() || - barCombined.Inputs[1].String() != jargen.Output.String() { - t.Errorf("bar combined jar inputs %v is not [%q, %q]", - barCombined.Inputs.Strings(), bar.Output.String(), jargen.Output.String()) - } -} - func TestExcludeFileGroupInSrcs(t *testing.T) { ctx, _ := testJava(t, ` java_library { @@ -1183,7 +1129,7 @@ func checkPatchModuleFlag(t *testing.T, ctx *android.TestContext, moduleName str break } } - if expected != android.StringPathRelativeToTop(ctx.Config().BuildDir(), got) { + if expected != android.StringPathRelativeToTop(ctx.Config().SoongOutDir(), got) { t.Errorf("Unexpected patch-module flag for module %q - expected %q, but got %q", moduleName, expected, got) } } @@ -1357,6 +1303,72 @@ func TestAidlFlagsArePassedToTheAidlCompiler(t *testing.T) { } } +func TestAidlFlagsWithMinSdkVersion(t *testing.T) { + fixture := android.GroupFixturePreparers( + prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{"14": {"foo"}})) + + for _, tc := range []struct { + name string + sdkVersion string + expected string + }{ + {"default is current", "", "current"}, + {"use sdk_version", `sdk_version: "14"`, "14"}, + {"system_current", `sdk_version: "system_current"`, "current"}, + } { + t.Run(tc.name, func(t *testing.T) { + ctx := fixture.RunTestWithBp(t, ` + java_library { + name: "foo", + srcs: ["aidl/foo/IFoo.aidl"], + `+tc.sdkVersion+` + } + `) + aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command + expectedAidlFlag := "--min_sdk_version=" + tc.expected + if !strings.Contains(aidlCommand, expectedAidlFlag) { + t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag) + } + }) + } +} + +func TestAidlEnforcePermissions(t *testing.T) { + ctx, _ := testJava(t, ` + java_library { + name: "foo", + srcs: ["aidl/foo/IFoo.aidl"], + aidl: { enforce_permissions: true }, + } + `) + + aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command + expectedAidlFlag := "-Wmissing-permission-annotation -Werror" + if !strings.Contains(aidlCommand, expectedAidlFlag) { + t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag) + } +} + +func TestAidlEnforcePermissionsException(t *testing.T) { + ctx, _ := testJava(t, ` + java_library { + name: "foo", + srcs: ["aidl/foo/IFoo.aidl", "aidl/foo/IFoo2.aidl"], + aidl: { enforce_permissions: true, enforce_permissions_exceptions: ["aidl/foo/IFoo2.aidl"] }, + } + `) + + aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command + expectedAidlFlag := "$$FLAGS -Wmissing-permission-annotation -Werror aidl/foo/IFoo.aidl" + if !strings.Contains(aidlCommand, expectedAidlFlag) { + t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag) + } + expectedAidlFlag = "$$FLAGS aidl/foo/IFoo2.aidl" + if !strings.Contains(aidlCommand, expectedAidlFlag) { + t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag) + } +} + func TestDataNativeBinaries(t *testing.T) { ctx, _ := testJava(t, ` java_test_host { @@ -1371,7 +1383,7 @@ func TestDataNativeBinaries(t *testing.T) { } `) - buildOS := android.BuildOs.String() + buildOS := ctx.Config().BuildOS.String() test := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost) entries := android.AndroidMkEntriesForTest(t, ctx, test)[0] @@ -1387,8 +1399,271 @@ func TestDefaultInstallable(t *testing.T) { } `) - buildOS := android.BuildOs.String() + buildOS := ctx.Config().BuildOS.String() module := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost) assertDeepEquals(t, "Default installable value should be true.", proptools.BoolPtr(true), module.properties.Installable) } + +func TestErrorproneEnabled(t *testing.T) { + ctx, _ := testJava(t, ` + java_library { + name: "foo", + srcs: ["a.java"], + errorprone: { + enabled: true, + }, + } + `) + + javac := ctx.ModuleForTests("foo", "android_common").Description("javac") + + // Test that the errorprone plugins are passed to javac + expectedSubstring := "-Xplugin:ErrorProne" + if !strings.Contains(javac.Args["javacFlags"], expectedSubstring) { + t.Errorf("expected javacFlags to contain %q, got %q", expectedSubstring, javac.Args["javacFlags"]) + } + + // Modules with errorprone { enabled: true } will include errorprone checks + // in the main javac build rule. Only when RUN_ERROR_PRONE is true will + // the explicit errorprone build rule be created. + errorprone := ctx.ModuleForTests("foo", "android_common").MaybeDescription("errorprone") + if errorprone.RuleParams.Description != "" { + t.Errorf("expected errorprone build rule to not exist, but it did") + } +} + +func TestErrorproneDisabled(t *testing.T) { + bp := ` + java_library { + name: "foo", + srcs: ["a.java"], + errorprone: { + enabled: false, + }, + } + ` + ctx := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + android.FixtureMergeEnv(map[string]string{ + "RUN_ERROR_PRONE": "true", + }), + ).RunTestWithBp(t, bp) + + javac := ctx.ModuleForTests("foo", "android_common").Description("javac") + + // Test that the errorprone plugins are not passed to javac, like they would + // be if enabled was true. + expectedSubstring := "-Xplugin:ErrorProne" + if strings.Contains(javac.Args["javacFlags"], expectedSubstring) { + t.Errorf("expected javacFlags to not contain %q, got %q", expectedSubstring, javac.Args["javacFlags"]) + } + + // Check that no errorprone build rule is created, like there would be + // if enabled was unset and RUN_ERROR_PRONE was true. + errorprone := ctx.ModuleForTests("foo", "android_common").MaybeDescription("errorprone") + if errorprone.RuleParams.Description != "" { + t.Errorf("expected errorprone build rule to not exist, but it did") + } +} + +func TestErrorproneEnabledOnlyByEnvironmentVariable(t *testing.T) { + bp := ` + java_library { + name: "foo", + srcs: ["a.java"], + } + ` + ctx := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + android.FixtureMergeEnv(map[string]string{ + "RUN_ERROR_PRONE": "true", + }), + ).RunTestWithBp(t, bp) + + javac := ctx.ModuleForTests("foo", "android_common").Description("javac") + errorprone := ctx.ModuleForTests("foo", "android_common").Description("errorprone") + + // Check that the errorprone plugins are not passed to javac, because they + // will instead be passed to the separate errorprone compilation + expectedSubstring := "-Xplugin:ErrorProne" + if strings.Contains(javac.Args["javacFlags"], expectedSubstring) { + t.Errorf("expected javacFlags to not contain %q, got %q", expectedSubstring, javac.Args["javacFlags"]) + } + + // Check that the errorprone plugin is enabled + if !strings.Contains(errorprone.Args["javacFlags"], expectedSubstring) { + t.Errorf("expected errorprone to contain %q, got %q", expectedSubstring, javac.Args["javacFlags"]) + } +} + +func TestDataDeviceBinsBuildsDeviceBinary(t *testing.T) { + testCases := []struct { + dataDeviceBinType string + depCompileMultilib string + variants []string + expectedError string + }{ + { + dataDeviceBinType: "first", + depCompileMultilib: "first", + variants: []string{"android_arm64_armv8-a"}, + }, + { + dataDeviceBinType: "first", + depCompileMultilib: "both", + variants: []string{"android_arm64_armv8-a"}, + }, + { + // this is true because our testing framework is set up with + // Targets ~ [<64bit target>, <32bit target>], where 64bit is "first" + dataDeviceBinType: "first", + depCompileMultilib: "32", + expectedError: `Android.bp:2:3: dependency "bar" of "foo" missing variant`, + }, + { + dataDeviceBinType: "first", + depCompileMultilib: "64", + variants: []string{"android_arm64_armv8-a"}, + }, + { + dataDeviceBinType: "both", + depCompileMultilib: "both", + variants: []string{ + "android_arm_armv7-a-neon", + "android_arm64_armv8-a", + }, + }, + { + dataDeviceBinType: "both", + depCompileMultilib: "32", + expectedError: `Android.bp:2:3: dependency "bar" of "foo" missing variant`, + }, + { + dataDeviceBinType: "both", + depCompileMultilib: "64", + expectedError: `Android.bp:2:3: dependency "bar" of "foo" missing variant`, + }, + { + dataDeviceBinType: "both", + depCompileMultilib: "first", + expectedError: `Android.bp:2:3: dependency "bar" of "foo" missing variant`, + }, + { + dataDeviceBinType: "32", + depCompileMultilib: "32", + variants: []string{"android_arm_armv7-a-neon"}, + }, + { + dataDeviceBinType: "32", + depCompileMultilib: "first", + expectedError: `Android.bp:2:3: dependency "bar" of "foo" missing variant`, + }, + { + dataDeviceBinType: "32", + depCompileMultilib: "both", + variants: []string{"android_arm_armv7-a-neon"}, + }, + { + dataDeviceBinType: "32", + depCompileMultilib: "64", + expectedError: `Android.bp:2:3: dependency "bar" of "foo" missing variant`, + }, + { + dataDeviceBinType: "64", + depCompileMultilib: "64", + variants: []string{"android_arm64_armv8-a"}, + }, + { + dataDeviceBinType: "64", + depCompileMultilib: "both", + variants: []string{"android_arm64_armv8-a"}, + }, + { + dataDeviceBinType: "64", + depCompileMultilib: "first", + variants: []string{"android_arm64_armv8-a"}, + }, + { + dataDeviceBinType: "64", + depCompileMultilib: "32", + expectedError: `Android.bp:2:3: dependency "bar" of "foo" missing variant`, + }, + { + dataDeviceBinType: "prefer32", + depCompileMultilib: "32", + variants: []string{"android_arm_armv7-a-neon"}, + }, + { + dataDeviceBinType: "prefer32", + depCompileMultilib: "both", + variants: []string{"android_arm_armv7-a-neon"}, + }, + { + dataDeviceBinType: "prefer32", + depCompileMultilib: "first", + expectedError: `Android.bp:2:3: dependency "bar" of "foo" missing variant`, + }, + { + dataDeviceBinType: "prefer32", + depCompileMultilib: "64", + expectedError: `Android.bp:2:3: dependency "bar" of "foo" missing variant`, + }, + } + + bpTemplate := ` + java_test_host { + name: "foo", + srcs: ["test.java"], + data_device_bins_%s: ["bar"], + } + + cc_binary { + name: "bar", + compile_multilib: "%s", + } + ` + + for _, tc := range testCases { + bp := fmt.Sprintf(bpTemplate, tc.dataDeviceBinType, tc.depCompileMultilib) + + errorHandler := android.FixtureExpectsNoErrors + if tc.expectedError != "" { + errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(tc.expectedError) + } + + testName := fmt.Sprintf(`data_device_bins_%s with compile_multilib:"%s"`, tc.dataDeviceBinType, tc.depCompileMultilib) + t.Run(testName, func(t *testing.T) { + ctx := android.GroupFixturePreparers(PrepareForIntegrationTestWithJava). + ExtendWithErrorHandler(errorHandler). + RunTestWithBp(t, bp) + if tc.expectedError != "" { + return + } + + buildOS := ctx.Config.BuildOS.String() + fooVariant := ctx.ModuleForTests("foo", buildOS+"_common") + fooMod := fooVariant.Module().(*TestHost) + entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, fooMod)[0] + + expectedAutogenConfig := `<option name="push-file" key="bar" value="/data/local/tests/unrestricted/foo/bar" />` + autogen := fooVariant.Rule("autogen") + if !strings.Contains(autogen.Args["extraConfigs"], expectedAutogenConfig) { + t.Errorf("foo extraConfigs %v does not contain %q", autogen.Args["extraConfigs"], expectedAutogenConfig) + } + + expectedData := []string{} + for _, variant := range tc.variants { + barVariant := ctx.ModuleForTests("bar", variant) + relocated := barVariant.Output("bar") + expectedInput := fmt.Sprintf("out/soong/.intermediates/bar/%s/unstripped/bar", variant) + android.AssertPathRelativeToTopEquals(t, "relocation input", expectedInput, relocated.Input) + + expectedData = append(expectedData, fmt.Sprintf("out/soong/.intermediates/bar/%s/bar:bar", variant)) + } + + actualData := entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"] + android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_TEST_DATA", ctx.Config, expectedData, actualData) + }) + } +} diff --git a/java/jdeps.go b/java/jdeps.go index 0ab2e422b..373433517 100644 --- a/java/jdeps.go +++ b/java/jdeps.go @@ -40,16 +40,11 @@ type jdepsGeneratorSingleton struct { var _ android.SingletonMakeVarsProvider = (*jdepsGeneratorSingleton)(nil) const ( - // Environment variables used to modify behavior of this singleton. - envVariableCollectJavaDeps = "SOONG_COLLECT_JAVA_DEPS" - jdepsJsonFileName = "module_bp_java_deps.json" + jdepsJsonFileName = "module_bp_java_deps.json" ) func (j *jdepsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) { - if !ctx.Config().IsEnvTrue(envVariableCollectJavaDeps) { - return - } - + // (b/204397180) Generate module_bp_java_deps.json by default. moduleInfos := make(map[string]android.IdeInfo) ctx.VisitAllModules(func(module android.Module) { @@ -76,6 +71,8 @@ func (j *jdepsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonCont dpInfo.Jars = android.FirstUniqueStrings(dpInfo.Jars) dpInfo.SrcJars = android.FirstUniqueStrings(dpInfo.SrcJars) dpInfo.Paths = android.FirstUniqueStrings(dpInfo.Paths) + dpInfo.Static_libs = android.FirstUniqueStrings(dpInfo.Static_libs) + dpInfo.Libs = android.FirstUniqueStrings(dpInfo.Libs) moduleInfos[name] = dpInfo mkProvider, ok := module.(android.AndroidMkDataProvider) diff --git a/java/kotlin.go b/java/kotlin.go index 3a6fc0f48..903c6249b 100644 --- a/java/kotlin.go +++ b/java/kotlin.go @@ -28,16 +28,20 @@ import ( var kotlinc = pctx.AndroidRemoteStaticRule("kotlinc", android.RemoteRuleSupports{Goma: true}, blueprint.RuleParams{ - Command: `rm -rf "$classesDir" "$srcJarDir" "$kotlinBuildFile" "$emptyDir" && ` + - `mkdir -p "$classesDir" "$srcJarDir" "$emptyDir" && ` + + Command: `rm -rf "$classesDir" "$headerClassesDir" "$srcJarDir" "$kotlinBuildFile" "$emptyDir" && ` + + `mkdir -p "$classesDir" "$headerClassesDir" "$srcJarDir" "$emptyDir" && ` + `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` + `${config.GenKotlinBuildFileCmd} --classpath "$classpath" --name "$name"` + ` --out_dir "$classesDir" --srcs "$out.rsp" --srcs "$srcJarDir/list"` + ` $commonSrcFilesArg --out "$kotlinBuildFile" && ` + - `${config.KotlincCmd} ${config.KotlincSuppressJDK9Warnings} ${config.JavacHeapFlags} ` + - `$kotlincFlags -jvm-target $kotlinJvmTarget -Xbuild-file=$kotlinBuildFile ` + - `-kotlin-home $emptyDir && ` + - `${config.SoongZipCmd} -jar -o $out -C $classesDir -D $classesDir && ` + + `${config.KotlincCmd} ${config.KotlincGlobalFlags} ` + + ` ${config.KotlincSuppressJDK9Warnings} ${config.JavacHeapFlags} ` + + ` $kotlincFlags -jvm-target $kotlinJvmTarget -Xbuild-file=$kotlinBuildFile ` + + ` -kotlin-home $emptyDir ` + + ` -Xplugin=${config.KotlinAbiGenPluginJar} ` + + ` -P plugin:org.jetbrains.kotlin.jvm.abi:outputDir=$headerClassesDir && ` + + `${config.SoongZipCmd} -jar -o $out -C $classesDir -D $classesDir -write_if_changed && ` + + `${config.SoongZipCmd} -jar -o $headerJar -C $headerClassesDir -D $headerClassesDir -write_if_changed && ` + `rm -rf "$srcJarDir"`, CommandDeps: []string{ "${config.KotlincCmd}", @@ -48,15 +52,17 @@ var kotlinc = pctx.AndroidRemoteStaticRule("kotlinc", android.RemoteRuleSupports "${config.KotlinStdlibJar}", "${config.KotlinTrove4jJar}", "${config.KotlinAnnotationJar}", + "${config.KotlinAbiGenPluginJar}", "${config.GenKotlinBuildFileCmd}", "${config.SoongZipCmd}", "${config.ZipSyncCmd}", }, Rspfile: "$out.rsp", RspfileContent: `$in`, + Restat: true, }, "kotlincFlags", "classpath", "srcJars", "commonSrcFilesArg", "srcJarDir", "classesDir", - "kotlinJvmTarget", "kotlinBuildFile", "emptyDir", "name") + "headerClassesDir", "headerJar", "kotlinJvmTarget", "kotlinBuildFile", "emptyDir", "name") func kotlinCommonSrcsList(ctx android.ModuleContext, commonSrcFiles android.Paths) android.OptionalPath { if len(commonSrcFiles) > 0 { @@ -75,12 +81,13 @@ func kotlinCommonSrcsList(ctx android.ModuleContext, commonSrcFiles android.Path } // kotlinCompile takes .java and .kt sources and srcJars, and compiles the .kt sources into a classes jar in outputFile. -func kotlinCompile(ctx android.ModuleContext, outputFile android.WritablePath, +func kotlinCompile(ctx android.ModuleContext, outputFile, headerOutputFile android.WritablePath, srcFiles, commonSrcFiles, srcJars android.Paths, flags javaBuilderFlags) { var deps android.Paths deps = append(deps, flags.kotlincClasspath...) + deps = append(deps, flags.kotlincDeps...) deps = append(deps, srcJars...) deps = append(deps, commonSrcFiles...) @@ -95,17 +102,20 @@ func kotlinCompile(ctx android.ModuleContext, outputFile android.WritablePath, } ctx.Build(pctx, android.BuildParams{ - Rule: kotlinc, - Description: "kotlinc", - Output: outputFile, - Inputs: srcFiles, - Implicits: deps, + Rule: kotlinc, + Description: "kotlinc", + Output: outputFile, + ImplicitOutput: headerOutputFile, + Inputs: srcFiles, + Implicits: deps, Args: map[string]string{ "classpath": flags.kotlincClasspath.FormJavaClassPath(""), "kotlincFlags": flags.kotlincFlags, "commonSrcFilesArg": commonSrcFilesArg, "srcJars": strings.Join(srcJars.Strings(), " "), "classesDir": android.PathForModuleOut(ctx, "kotlinc", "classes").String(), + "headerClassesDir": android.PathForModuleOut(ctx, "kotlinc", "header_classes").String(), + "headerJar": headerOutputFile.String(), "srcJarDir": android.PathForModuleOut(ctx, "kotlinc", "srcJars").String(), "kotlinBuildFile": android.PathForModuleOut(ctx, "kotlinc-build.xml").String(), "emptyDir": android.PathForModuleOut(ctx, "kotlinc", "empty").String(), @@ -116,7 +126,7 @@ func kotlinCompile(ctx android.ModuleContext, outputFile android.WritablePath, }) } -var kapt = pctx.AndroidRemoteStaticRule("kapt", android.RemoteRuleSupports{Goma: true}, +var kaptStubs = pctx.AndroidRemoteStaticRule("kaptStubs", android.RemoteRuleSupports{Goma: true}, blueprint.RuleParams{ Command: `rm -rf "$srcJarDir" "$kotlinBuildFile" "$kaptDir" && ` + `mkdir -p "$srcJarDir" "$kaptDir/sources" "$kaptDir/classes" && ` + @@ -124,19 +134,19 @@ var kapt = pctx.AndroidRemoteStaticRule("kapt", android.RemoteRuleSupports{Goma: `${config.GenKotlinBuildFileCmd} --classpath "$classpath" --name "$name"` + ` --srcs "$out.rsp" --srcs "$srcJarDir/list"` + ` $commonSrcFilesArg --out "$kotlinBuildFile" && ` + - `${config.KotlincCmd} ${config.KaptSuppressJDK9Warnings} ${config.KotlincSuppressJDK9Warnings} ` + + `${config.KotlincCmd} ${config.KotlincGlobalFlags} ` + + `${config.KaptSuppressJDK9Warnings} ${config.KotlincSuppressJDK9Warnings} ` + `${config.JavacHeapFlags} $kotlincFlags -Xplugin=${config.KotlinKaptJar} ` + `-P plugin:org.jetbrains.kotlin.kapt3:sources=$kaptDir/sources ` + `-P plugin:org.jetbrains.kotlin.kapt3:classes=$kaptDir/classes ` + `-P plugin:org.jetbrains.kotlin.kapt3:stubs=$kaptDir/stubs ` + `-P plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true ` + - `-P plugin:org.jetbrains.kotlin.kapt3:aptMode=stubsAndApt ` + + `-P plugin:org.jetbrains.kotlin.kapt3:aptMode=stubs ` + `-P plugin:org.jetbrains.kotlin.kapt3:javacArguments=$encodedJavacFlags ` + `$kaptProcessorPath ` + `$kaptProcessor ` + `-Xbuild-file=$kotlinBuildFile && ` + - `${config.SoongZipCmd} -jar -o $out -C $kaptDir/sources -D $kaptDir/sources && ` + - `${config.SoongZipCmd} -jar -o $classesJarOut -C $kaptDir/classes -D $kaptDir/classes && ` + + `${config.SoongZipCmd} -jar -o $out -C $kaptDir/stubs -D $kaptDir/stubs && ` + `rm -rf "$srcJarDir"`, CommandDeps: []string{ "${config.KotlincCmd}", @@ -165,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...) @@ -194,13 +205,14 @@ func kotlinKapt(ctx android.ModuleContext, srcJarOutputFile, resJarOutputFile an kotlinName := filepath.Join(ctx.ModuleDir(), ctx.ModuleSubDir(), ctx.ModuleName()) kotlinName = strings.ReplaceAll(kotlinName, "/", "__") + // First run kapt to generate .java stubs from .kt files + kaptStubsJar := android.PathForModuleOut(ctx, "kapt", "stubs.jar") ctx.Build(pctx, android.BuildParams{ - Rule: kapt, - Description: "kapt", - Output: srcJarOutputFile, - ImplicitOutput: resJarOutputFile, - Inputs: srcFiles, - Implicits: deps, + Rule: kaptStubs, + Description: "kapt stubs", + Output: kaptStubsJar, + Inputs: srcFiles, + Implicits: deps, Args: map[string]string{ "classpath": flags.kotlincClasspath.FormJavaClassPath(""), "kotlincFlags": flags.kotlincFlags, @@ -216,6 +228,11 @@ func kotlinKapt(ctx android.ModuleContext, srcJarOutputFile, resJarOutputFile an "classesJarOut": resJarOutputFile.String(), }, }) + + // Then run turbine to perform annotation processing on the stubs and any .java srcFiles. + javaSrcFiles := srcFiles.FilterByExt(".java") + turbineSrcJars := append(android.Paths{kaptStubsJar}, srcJars...) + TurbineApt(ctx, srcJarOutputFile, resJarOutputFile, javaSrcFiles, turbineSrcJars, flags) } // kapt converts a list of key, value pairs into a base64 encoded Java serialization, which is what kapt expects. diff --git a/java/kotlin_test.go b/java/kotlin_test.go index 1c146a192..435d78294 100644 --- a/java/kotlin_test.go +++ b/java/kotlin_test.go @@ -15,10 +15,11 @@ package java import ( - "android/soong/android" "strconv" "strings" "testing" + + "android/soong/android" ) func TestKotlin(t *testing.T) { @@ -44,6 +45,10 @@ func TestKotlin(t *testing.T) { fooKotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc") fooJavac := ctx.ModuleForTests("foo", "android_common").Rule("javac") fooJar := ctx.ModuleForTests("foo", "android_common").Output("combined/foo.jar") + fooHeaderJar := ctx.ModuleForTests("foo", "android_common").Output("turbine-combined/foo.jar") + + fooKotlincClasses := fooKotlinc.Output + fooKotlincHeaderClasses := fooKotlinc.ImplicitOutput if len(fooKotlinc.Inputs) != 2 || fooKotlinc.Inputs[0].String() != "a.java" || fooKotlinc.Inputs[1].String() != "b.kt" { @@ -54,17 +59,21 @@ func TestKotlin(t *testing.T) { t.Errorf(`foo inputs %v != ["a.java"]`, fooJavac.Inputs) } - if !strings.Contains(fooJavac.Args["classpath"], fooKotlinc.Output.String()) { + if !strings.Contains(fooJavac.Args["classpath"], fooKotlincHeaderClasses.String()) { t.Errorf("foo classpath %v does not contain %q", - fooJavac.Args["classpath"], fooKotlinc.Output.String()) + fooJavac.Args["classpath"], fooKotlincHeaderClasses.String()) } - if !inList(fooKotlinc.Output.String(), fooJar.Inputs.Strings()) { + if !inList(fooKotlincClasses.String(), fooJar.Inputs.Strings()) { t.Errorf("foo jar inputs %v does not contain %q", - fooJar.Inputs.Strings(), fooKotlinc.Output.String()) + fooJar.Inputs.Strings(), fooKotlincClasses.String()) + } + + if !inList(fooKotlincHeaderClasses.String(), fooHeaderJar.Inputs.Strings()) { + t.Errorf("foo header jar inputs %v does not contain %q", + fooHeaderJar.Inputs.Strings(), fooKotlincHeaderClasses.String()) } - fooHeaderJar := ctx.ModuleForTests("foo", "android_common").Output("turbine-combined/foo.jar") bazHeaderJar := ctx.ModuleForTests("baz", "android_common").Output("turbine-combined/baz.jar") barKotlinc := ctx.ModuleForTests("bar", "android_common").Rule("kotlinc") @@ -114,53 +123,73 @@ func TestKapt(t *testing.T) { t.Run("", func(t *testing.T) { ctx, _ := testJava(t, bp) - buildOS := android.BuildOs.String() + buildOS := ctx.Config().BuildOS.String() - kapt := ctx.ModuleForTests("foo", "android_common").Rule("kapt") - kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc") - javac := ctx.ModuleForTests("foo", "android_common").Rule("javac") + foo := ctx.ModuleForTests("foo", "android_common") + kaptStubs := foo.Rule("kapt") + turbineApt := foo.Description("turbine apt") + kotlinc := foo.Rule("kotlinc") + javac := foo.Rule("javac") bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String() baz := ctx.ModuleForTests("baz", buildOS+"_common").Rule("javac").Output.String() // Test that the kotlin and java sources are passed to kapt and kotlinc - if len(kapt.Inputs) != 2 || kapt.Inputs[0].String() != "a.java" || kapt.Inputs[1].String() != "b.kt" { - t.Errorf(`foo kapt inputs %v != ["a.java", "b.kt"]`, kapt.Inputs) + if len(kaptStubs.Inputs) != 2 || kaptStubs.Inputs[0].String() != "a.java" || kaptStubs.Inputs[1].String() != "b.kt" { + t.Errorf(`foo kapt inputs %v != ["a.java", "b.kt"]`, kaptStubs.Inputs) } if len(kotlinc.Inputs) != 2 || kotlinc.Inputs[0].String() != "a.java" || kotlinc.Inputs[1].String() != "b.kt" { t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, kotlinc.Inputs) } - // Test that only the java sources are passed to javac + // Test that only the java sources are passed to turbine-apt and javac + if len(turbineApt.Inputs) != 1 || turbineApt.Inputs[0].String() != "a.java" { + t.Errorf(`foo turbine apt inputs %v != ["a.java"]`, turbineApt.Inputs) + } if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" { t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs) } - // Test that the kapt srcjar is a dependency of kotlinc and javac rules - if !inList(kapt.Output.String(), kotlinc.Implicits.Strings()) { - t.Errorf("expected %q in kotlinc implicits %v", kapt.Output.String(), kotlinc.Implicits.Strings()) + // Test that the kapt stubs jar is a dependency of turbine-apt + if !inList(kaptStubs.Output.String(), turbineApt.Implicits.Strings()) { + t.Errorf("expected %q in turbine-apt implicits %v", kaptStubs.Output.String(), kotlinc.Implicits.Strings()) + } + + // Test that the turbine-apt srcjar is a dependency of kotlinc and javac rules + if !inList(turbineApt.Output.String(), kotlinc.Implicits.Strings()) { + t.Errorf("expected %q in kotlinc implicits %v", turbineApt.Output.String(), kotlinc.Implicits.Strings()) } - if !inList(kapt.Output.String(), javac.Implicits.Strings()) { - t.Errorf("expected %q in javac implicits %v", kapt.Output.String(), javac.Implicits.Strings()) + if !inList(turbineApt.Output.String(), javac.Implicits.Strings()) { + t.Errorf("expected %q in javac implicits %v", turbineApt.Output.String(), javac.Implicits.Strings()) } - // Test that the kapt srcjar is extracted by the kotlinc and javac rules - if kotlinc.Args["srcJars"] != kapt.Output.String() { - t.Errorf("expected %q in kotlinc srcjars %v", kapt.Output.String(), kotlinc.Args["srcJars"]) + // Test that the turbine-apt srcjar is extracted by the kotlinc and javac rules + if kotlinc.Args["srcJars"] != turbineApt.Output.String() { + t.Errorf("expected %q in kotlinc srcjars %v", turbineApt.Output.String(), kotlinc.Args["srcJars"]) } - if javac.Args["srcJars"] != kapt.Output.String() { - t.Errorf("expected %q in javac srcjars %v", kapt.Output.String(), kotlinc.Args["srcJars"]) + if javac.Args["srcJars"] != turbineApt.Output.String() { + t.Errorf("expected %q in javac srcjars %v", turbineApt.Output.String(), kotlinc.Args["srcJars"]) } // Test that the processors are passed to kapt expectedProcessorPath := "-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + bar + " -P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + baz - if kapt.Args["kaptProcessorPath"] != expectedProcessorPath { - t.Errorf("expected kaptProcessorPath %q, got %q", expectedProcessorPath, kapt.Args["kaptProcessorPath"]) + if kaptStubs.Args["kaptProcessorPath"] != expectedProcessorPath { + t.Errorf("expected kaptProcessorPath %q, got %q", expectedProcessorPath, kaptStubs.Args["kaptProcessorPath"]) } expectedProcessor := "-P plugin:org.jetbrains.kotlin.kapt3:processors=com.bar -P plugin:org.jetbrains.kotlin.kapt3:processors=com.baz" - if kapt.Args["kaptProcessor"] != expectedProcessor { - t.Errorf("expected kaptProcessor %q, got %q", expectedProcessor, kapt.Args["kaptProcessor"]) + if kaptStubs.Args["kaptProcessor"] != expectedProcessor { + t.Errorf("expected kaptProcessor %q, got %q", expectedProcessor, kaptStubs.Args["kaptProcessor"]) + } + + // Test that the processors are passed to turbine-apt + expectedProcessorPath = "--processorpath " + bar + " " + baz + if !strings.Contains(turbineApt.Args["turbineFlags"], expectedProcessorPath) { + t.Errorf("expected turbine-apt processorpath %q, got %q", expectedProcessorPath, turbineApt.Args["turbineFlags"]) + } + expectedProcessor = "--processors com.bar com.baz" + if !strings.Contains(turbineApt.Args["turbineFlags"], expectedProcessor) { + t.Errorf("expected turbine-apt processor %q, got %q", expectedProcessor, turbineApt.Args["turbineFlags"]) } // Test that the processors are not passed to javac @@ -182,10 +211,9 @@ func TestKapt(t *testing.T) { android.FixtureMergeEnv(env), ).RunTestWithBp(t, bp) - buildOS := android.BuildOs.String() + buildOS := result.Config.BuildOS.String() kapt := result.ModuleForTests("foo", "android_common").Rule("kapt") - //kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc") javac := result.ModuleForTests("foo", "android_common").Description("javac") errorprone := result.ModuleForTests("foo", "android_common").Description("errorprone") @@ -281,3 +309,54 @@ func TestKaptEncodeFlags(t *testing.T) { }) } } + +func TestKotlinCompose(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + ).RunTestWithBp(t, ` + java_library { + name: "androidx.compose.runtime_runtime", + } + + java_library_host { + name: "androidx.compose.compiler_compiler-hosted", + } + + java_library { + name: "withcompose", + srcs: ["a.kt"], + plugins: ["plugin"], + static_libs: ["androidx.compose.runtime_runtime"], + } + + java_library { + name: "nocompose", + srcs: ["a.kt"], + } + + java_plugin { + name: "plugin", + } + `) + + buildOS := result.Config.BuildOS.String() + + composeCompiler := result.ModuleForTests("androidx.compose.compiler_compiler-hosted", buildOS+"_common").Rule("combineJar").Output + withCompose := result.ModuleForTests("withcompose", "android_common") + noCompose := result.ModuleForTests("nocompose", "android_common") + + android.AssertStringListContains(t, "missing compose compiler dependency", + withCompose.Rule("kotlinc").Implicits.Strings(), composeCompiler.String()) + + 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()) + + android.AssertStringDoesNotContain(t, "unexpected compose compiler plugin", + noCompose.VariablesForTestsRelativeToTop()["kotlincFlags"], "-Xplugin="+composeCompiler.String()) +} diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go index 628a10048..8e224914d 100644 --- a/java/legacy_core_platform_api_usage.go +++ b/java/legacy_core_platform_api_usage.go @@ -81,7 +81,6 @@ var legacyCorePlatformApiModules = []string{ "ds-car-docs", // for AAOS API documentation only "DynamicSystemInstallationService", "EmergencyInfo-lib", - "ethernet-service", "EthernetServiceTests", "ExternalStorageProvider", "face-V1-0-javalib", @@ -228,17 +227,27 @@ func init() { } } -func useLegacyCorePlatformApi(ctx android.EarlyModuleContext) bool { - return useLegacyCorePlatformApiByName(ctx.ModuleName()) +var legacyCorePlatformApiLookupKey = android.NewOnceKey("legacyCorePlatformApiLookup") + +func getLegacyCorePlatformApiLookup(config android.Config) map[string]struct{} { + return config.Once(legacyCorePlatformApiLookupKey, func() interface{} { + return legacyCorePlatformApiLookup + }).(map[string]struct{}) } -func useLegacyCorePlatformApiByName(name string) bool { - _, found := legacyCorePlatformApiLookup[name] +// useLegacyCorePlatformApi checks to see whether the supplied module name is in the list of modules +// that are able to use the legacy core platform API and returns true if it does, false otherwise. +// +// This method takes the module name separately from the context as this may be being called for a +// module that is not the target of the supplied context. +func useLegacyCorePlatformApi(ctx android.EarlyModuleContext, moduleName string) bool { + lookup := getLegacyCorePlatformApiLookup(ctx.Config()) + _, found := lookup[moduleName] return found } func corePlatformSystemModules(ctx android.EarlyModuleContext) string { - if useLegacyCorePlatformApi(ctx) { + if useLegacyCorePlatformApi(ctx, ctx.ModuleName()) { return config.LegacyCorePlatformSystemModules } else { return config.StableCorePlatformSystemModules @@ -246,7 +255,7 @@ func corePlatformSystemModules(ctx android.EarlyModuleContext) string { } func corePlatformBootclasspathLibraries(ctx android.EarlyModuleContext) []string { - if useLegacyCorePlatformApi(ctx) { + if useLegacyCorePlatformApi(ctx, ctx.ModuleName()) { return config.LegacyCorePlatformBootclasspathLibraries } else { return config.StableCorePlatformBootclasspathLibraries diff --git a/java/lint.go b/java/lint.go index fe3218e90..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 @@ -102,12 +102,12 @@ type lintOutputsIntf interface { lintOutputs() *lintOutputs } -type lintDepSetsIntf interface { +type LintDepSetsIntf interface { LintDepSets() LintDepSets // Methods used to propagate strict_updatability_linting values. - getStrictUpdatabilityLinting() bool - setStrictUpdatabilityLinting(bool) + GetStrictUpdatabilityLinting() bool + SetStrictUpdatabilityLinting(bool) } type LintDepSets struct { @@ -158,15 +158,15 @@ func (l *linter) LintDepSets() LintDepSets { return l.outputs.depSets } -func (l *linter) getStrictUpdatabilityLinting() bool { +func (l *linter) GetStrictUpdatabilityLinting() bool { return BoolDefault(l.properties.Lint.Strict_updatability_linting, false) } -func (l *linter) setStrictUpdatabilityLinting(strictLinting bool) { +func (l *linter) SetStrictUpdatabilityLinting(strictLinting bool) { l.properties.Lint.Strict_updatability_linting = &strictLinting } -var _ lintDepSetsIntf = (*linter)(nil) +var _ LintDepSetsIntf = (*linter)(nil) var _ lintOutputsIntf = (*linter)(nil) @@ -273,7 +273,7 @@ func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.Ru cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks) cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks) - if l.getStrictUpdatabilityLinting() { + if l.GetStrictUpdatabilityLinting() { // Verify the module does not baseline issues that endanger safe updatability. if baselinePath := l.getBaselineFilepath(ctx); baselinePath.Valid() { cmd.FlagWithInput("--baseline ", baselinePath.Path()) @@ -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 { @@ -377,11 +377,12 @@ func (l *linter) lint(ctx android.ModuleContext) { html := android.PathForModuleOut(ctx, "lint", "lint-report.html") text := android.PathForModuleOut(ctx, "lint", "lint-report.txt") xml := android.PathForModuleOut(ctx, "lint", "lint-report.xml") + baseline := android.PathForModuleOut(ctx, "lint", "lint-baseline.xml") depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml) ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) { - if depLint, ok := dep.(lintDepSetsIntf); ok { + if depLint, ok := dep.(LintDepSetsIntf); ok { depSetsBuilder.Transitive(depLint.LintDepSets()) } }) @@ -426,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())). @@ -447,6 +448,8 @@ func (l *linter) lint(ctx android.ModuleContext) { cmd.FlagWithInput("--baseline ", lintBaseline.Path()) } + cmd.FlagWithOutput("--write-reference-baseline ", baseline) + cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)") rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String()) @@ -657,10 +660,10 @@ func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android // Enforce the strict updatability linting to all applicable transitive dependencies. func enforceStrictUpdatabilityLintingMutator(ctx android.TopDownMutatorContext) { m := ctx.Module() - if d, ok := m.(lintDepSetsIntf); ok && d.getStrictUpdatabilityLinting() { + if d, ok := m.(LintDepSetsIntf); ok && d.GetStrictUpdatabilityLinting() { ctx.VisitDirectDepsWithTag(staticLibTag, func(d android.Module) { - if a, ok := d.(lintDepSetsIntf); ok { - a.setStrictUpdatabilityLinting(true) + if a, ok := d.(LintDepSetsIntf); ok { + a.SetStrictUpdatabilityLinting(true) } }) } diff --git a/java/lint_defaults.txt b/java/lint_defaults.txt index 2de05b0a0..1eee354cd 100644 --- a/java/lint_defaults.txt +++ b/java/lint_defaults.txt @@ -1,10 +1,42 @@ # Treat LintError as fatal to catch invocation errors --fatal_check LintError +# Checks which do not apply to the platform (implementation +# in lint assumes that it's running on app code) + +--disable_check AnimatorKeep +--disable_check AppBundleLocaleChanges +--disable_check BlockedPrivateApi +--disable_check CustomSplashScreen +--disable_check CustomX509TrustManager +--disable_check Deprecated +--disable_check ExifInterface +--disable_check HardwareIds +--disable_check InvalidWakeLockTag +--disable_check LibraryCustomView +--disable_check MissingPermission +--disable_check NonConstantResourceId +--disable_check OldTargetApi +--disable_check Override +--disable_check PackageManagerGetSignatures +--disable_check PrivateApi +--disable_check ProtectedPermissions +--disable_check QueryPermissionsNeeded +--disable_check ScopedStorage +--disable_check ServiceCast +--disable_check SoonBlockedPrivateApi +--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 --warning_check AppLinkUrlError # 111 occurences in 53 modules ---warning_check BlockedPrivateApi # 2 occurences in 2 modules --warning_check ByteOrderMark # 2 occurences in 2 modules --warning_check DuplicateActivity # 3 occurences in 3 modules --warning_check DuplicateDefinition # 3623 occurences in 48 modules @@ -22,9 +54,7 @@ --warning_check Instantiatable # 145 occurences in 19 modules --warning_check InvalidPermission # 6 occurences in 4 modules --warning_check InvalidUsesTagAttribute # 6 occurences in 2 modules ---warning_check InvalidWakeLockTag # 111 occurences in 37 modules --warning_check JavascriptInterface # 3 occurences in 2 modules ---warning_check LibraryCustomView # 9 occurences in 4 modules --warning_check LogTagMismatch # 81 occurences in 13 modules --warning_check LongLogTag # 249 occurences in 12 modules --warning_check MenuTitle # 5 occurences in 4 modules @@ -35,21 +65,17 @@ --warning_check MissingLeanbackLauncher # 3 occurences in 3 modules --warning_check MissingLeanbackSupport # 2 occurences in 2 modules --warning_check MissingOnPlayFromSearch # 1 occurences in 1 modules ---warning_check MissingPermission # 2071 occurences in 150 modules --warning_check MissingPrefix # 46 occurences in 41 modules --warning_check MissingQuantity # 100 occurences in 1 modules --warning_check MissingSuperCall # 121 occurences in 36 modules --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 --warning_check Orientation # 77 occurences in 19 modules ---warning_check Override # 385 occurences in 36 modules --warning_check ParcelCreator # 23 occurences in 2 modules ---warning_check ProtectedPermissions # 2413 occurences in 381 modules --warning_check Range # 80 occurences in 28 modules --warning_check RecyclerView # 1 occurences in 1 modules --warning_check ReferenceType # 4 occurences in 1 modules @@ -60,8 +86,6 @@ --warning_check ResourceType # 137 occurences in 36 modules --warning_check RestrictedApi # 28 occurences in 5 modules --warning_check RtlCompat # 9 occurences in 6 modules ---warning_check ServiceCast # 3 occurences in 1 modules ---warning_check SoonBlockedPrivateApi # 5 occurences in 3 modules --warning_check StringFormatInvalid # 148 occurences in 11 modules --warning_check StringFormatMatches # 4800 occurences in 30 modules --warning_check UnknownId # 8 occurences in 7 modules @@ -74,7 +98,9 @@ --warning_check WrongThread # 14 occurences in 6 modules --warning_check WrongViewCast # 1 occurences in 1 modules -# TODO(b/158390965): remove this when lint doesn't crash ---disable_check HardcodedDebugMode - +--warning_check CoarseFineLocation +--warning_check IntentFilterExportedReceiver --warning_check QueryAllPackagesPermission +--warning_check RemoteViewLayout +--warning_check SupportAnnotationUsage +--warning_check UniqueConstants diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index 10739b015..1e2723845 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -32,9 +32,9 @@ func registerPlatformBootclasspathBuildComponents(ctx android.RegistrationContex // The tags used for the dependencies between the platform bootclasspath and any configured boot // jars. var ( - platformBootclasspathArtBootJarDepTag = bootclasspathDependencyTag{name: "art-boot-jar"} - platformBootclasspathNonUpdatableBootJarDepTag = bootclasspathDependencyTag{name: "non-updatable-boot-jar"} - platformBootclasspathUpdatableBootJarDepTag = bootclasspathDependencyTag{name: "updatable-boot-jar"} + platformBootclasspathArtBootJarDepTag = bootclasspathDependencyTag{name: "art-boot-jar"} + platformBootclasspathBootJarDepTag = bootclasspathDependencyTag{name: "platform-boot-jar"} + platformBootclasspathApexBootJarDepTag = bootclasspathDependencyTag{name: "apex-boot-jar"} ) type platformBootclasspathModule struct { @@ -68,7 +68,6 @@ type platformBootclasspathProperties struct { func platformBootclasspathFactory() android.SingletonModule { m := &platformBootclasspathModule{} m.AddProperties(&m.properties) - // TODO(satayev): split apex jars into separate configs. initClasspathFragment(m, BOOTCLASSPATH) android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) return m @@ -131,11 +130,11 @@ func (b *platformBootclasspathModule) BootclasspathDepsMutator(ctx android.Botto // Add dependencies on all the non-updatable module configured in the "boot" boot image. That does // not include modules configured in the "art" boot image. bootImageConfig := b.getImageConfig(ctx) - addDependenciesOntoBootImageModules(ctx, bootImageConfig.modules, platformBootclasspathNonUpdatableBootJarDepTag) + addDependenciesOntoBootImageModules(ctx, bootImageConfig.modules, platformBootclasspathBootJarDepTag) - // Add dependencies on all the updatable modules. - updatableModules := dexpreopt.GetGlobalConfig(ctx).UpdatableBootJars - addDependenciesOntoBootImageModules(ctx, updatableModules, platformBootclasspathUpdatableBootJarDepTag) + // Add dependencies on all the apex jars. + apexJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars + addDependenciesOntoBootImageModules(ctx, apexJars, platformBootclasspathApexBootJarDepTag) // Add dependencies on all the fragments. b.properties.BootclasspathFragmentsDepsProperties.addDependenciesOntoFragments(ctx) @@ -163,16 +162,16 @@ func (d *platformBootclasspathModule) MakeVars(ctx android.MakeVarsContext) { } func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { - // Gather all the dependencies from the art, updatable and non-updatable boot jars. + // Gather all the dependencies from the art, platform, and apex boot jars. artModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathArtBootJarDepTag) - nonUpdatableModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathNonUpdatableBootJarDepTag) - updatableModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathUpdatableBootJarDepTag) + platformModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathBootJarDepTag) + apexModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathApexBootJarDepTag) // Concatenate them all, in order as they would appear on the bootclasspath. var allModules []android.Module allModules = append(allModules, artModules...) - allModules = append(allModules, nonUpdatableModules...) - allModules = append(allModules, updatableModules...) + allModules = append(allModules, platformModules...) + allModules = append(allModules, apexModules...) b.configuredModules = allModules // Gather all the fragments dependencies. @@ -180,8 +179,8 @@ func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.Mo // Check the configuration of the boot modules. // ART modules are checked by the art-bootclasspath-fragment. - b.checkNonUpdatableModules(ctx, nonUpdatableModules) - b.checkUpdatableModules(ctx, updatableModules) + b.checkPlatformModules(ctx, platformModules) + b.checkApexModules(ctx, apexModules) b.generateClasspathProtoBuildActions(ctx) @@ -193,23 +192,40 @@ func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.Mo return } - b.generateBootImageBuildActions(ctx, nonUpdatableModules, updatableModules) + b.generateBootImageBuildActions(ctx, platformModules, apexModules) } // Generate classpaths.proto config func (b *platformBootclasspathModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) { + configuredJars := b.configuredJars(ctx) // ART and platform boot jars must have a corresponding entry in DEX2OATBOOTCLASSPATH - classpathJars := configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH) - b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars) + classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, BOOTCLASSPATH, DEX2OATBOOTCLASSPATH) + b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars) } -func (b *platformBootclasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList { - return b.getImageConfig(ctx).modules +func (b *platformBootclasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList { + // Include all non APEX jars + jars := b.getImageConfig(ctx).modules + + // Include jars from APEXes that don't populate their classpath proto config. + remainingJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars + for _, fragment := range b.fragments { + info := ctx.OtherModuleProvider(fragment, ClasspathFragmentProtoContentInfoProvider).(ClasspathFragmentProtoContentInfo) + if info.ClasspathFragmentProtoGenerated { + remainingJars = remainingJars.RemoveList(info.ClasspathFragmentProtoContents) + } + } + for i := 0; i < remainingJars.Len(); i++ { + jars = jars.Append(remainingJars.Apex(i), remainingJars.Jar(i)) + } + + return jars } -// checkNonUpdatableModules ensures that the non-updatable modules supplied are not part of an -// updatable module. -func (b *platformBootclasspathModule) checkNonUpdatableModules(ctx android.ModuleContext, modules []android.Module) { +// checkPlatformModules ensures that the non-updatable modules supplied are not part of an +// apex module. +func (b *platformBootclasspathModule) checkPlatformModules(ctx android.ModuleContext, modules []android.Module) { + // TODO(satayev): change this check to only allow core-icu4j, all apex jars should not be here. for _, m := range modules { apexInfo := ctx.OtherModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo) fromUpdatableApex := apexInfo.Updatable @@ -222,8 +238,8 @@ func (b *platformBootclasspathModule) checkNonUpdatableModules(ctx android.Modul } } -// checkUpdatableModules ensures that the updatable modules supplied are not from the platform. -func (b *platformBootclasspathModule) checkUpdatableModules(ctx android.ModuleContext, modules []android.Module) { +// checkApexModules ensures that the apex modules supplied are not from the platform. +func (b *platformBootclasspathModule) checkApexModules(ctx android.ModuleContext, modules []android.Module) { for _, m := range modules { apexInfo := ctx.OtherModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo) fromUpdatableApex := apexInfo.Updatable @@ -239,12 +255,12 @@ func (b *platformBootclasspathModule) checkUpdatableModules(ctx android.ModuleCo // modules is complete. if !ctx.Config().AlwaysUsePrebuiltSdks() { // error: this jar is part of the platform - ctx.ModuleErrorf("module %q from platform is not allowed in the updatable boot jars list", name) + ctx.ModuleErrorf("module %q from platform is not allowed in the apex boot jars list", name) } } else { // TODO(b/177892522): Treat this as an error. // Cannot do that at the moment because framework-wifi and framework-tethering are in the - // PRODUCT_UPDATABLE_BOOT_JARS but not marked as updatable in AOSP. + // PRODUCT_APEX_BOOT_JARS but not marked as updatable in AOSP. } } } @@ -299,7 +315,7 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android. // Generate the monolithic stub-flags.csv file. stubFlags := hiddenAPISingletonPaths(ctx).stubFlags - buildRuleToGenerateHiddenAPIStubFlagsFile(ctx, "platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags", stubFlags, bootDexJarByModule.bootDexJars(), input, monolithicInfo.StubFlagsPaths) + buildRuleToGenerateHiddenAPIStubFlagsFile(ctx, "platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags", stubFlags, bootDexJarByModule.bootDexJars(), input, monolithicInfo.StubFlagSubsets) // Generate the annotation-flags.csv file from all the module annotations. annotationFlags := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "annotation-flags-from-classes.csv") @@ -312,7 +328,7 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android. allAnnotationFlagFiles := android.Paths{annotationFlags} allAnnotationFlagFiles = append(allAnnotationFlagFiles, monolithicInfo.AnnotationFlagsPaths...) allFlags := hiddenAPISingletonPaths(ctx).flags - buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "monolithic hidden API flags", allFlags, stubFlags, allAnnotationFlagFiles, monolithicInfo.FlagsFilesByCategory, monolithicInfo.AllFlagsPaths, android.OptionalPath{}) + buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "monolithic hidden API flags", allFlags, stubFlags, allAnnotationFlagFiles, monolithicInfo.FlagsFilesByCategory, monolithicInfo.FlagSubsets, android.OptionalPath{}) // Generate an intermediate monolithic hiddenapi-metadata.csv file directly from the annotations // in the source code. @@ -389,7 +405,7 @@ func (b *platformBootclasspathModule) generateHiddenApiMakeVars(ctx android.Make } // generateBootImageBuildActions generates ninja rules related to the boot image creation. -func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android.ModuleContext, nonUpdatableModules, updatableModules []android.Module) { +func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android.ModuleContext, platformModules, apexModules []android.Module) { // Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars // GenerateSingletonBuildActions method as it cannot create it for itself. dexpreopt.GetGlobalSoongConfig(ctx) @@ -407,17 +423,14 @@ func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android. // Generate the framework profile rule bootFrameworkProfileRule(ctx, imageConfig) - // Generate the updatable bootclasspath packages rule. - generateUpdatableBcpPackagesRule(ctx, imageConfig, updatableModules) - - // Copy non-updatable module dex jars to their predefined locations. - nonUpdatableBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, nonUpdatableModules) - copyBootJarsToPredefinedLocations(ctx, nonUpdatableBootDexJarsByModule, imageConfig.dexPathsByModule) + // Copy platform module dex jars to their predefined locations. + platformBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, platformModules) + copyBootJarsToPredefinedLocations(ctx, platformBootDexJarsByModule, imageConfig.dexPathsByModule) - // Copy updatable module dex jars to their predefined locations. - config := GetUpdatableBootConfig(ctx) - updatableBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, updatableModules) - copyBootJarsToPredefinedLocations(ctx, updatableBootDexJarsByModule, config.dexPathsByModule) + // Copy apex module dex jars to their predefined locations. + config := GetApexBootConfig(ctx) + apexBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, apexModules) + copyBootJarsToPredefinedLocations(ctx, apexBootDexJarsByModule, config.dexPathsByModule) // Build a profile for the image config and then use that to build the boot image. profile := bootImageProfileRule(ctx, imageConfig) diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go index 712c2a203..f442ddfd4 100644 --- a/java/platform_compat_config.go +++ b/java/platform_compat_config.go @@ -115,7 +115,7 @@ func (p *platformCompatConfig) AndroidMkEntries() []android.AndroidMkEntries { Include: "$(BUILD_PREBUILT)", ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String()) + entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.String()) entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.configFile.Base()) }, }, @@ -134,8 +134,8 @@ type compatConfigMemberType struct { android.SdkMemberTypeBase } -func (b *compatConfigMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) { - mctx.AddVariationDependencies(nil, dependencyTag, names...) +func (b *compatConfigMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) { + ctx.AddVariationDependencies(nil, dependencyTag, names...) } func (b *compatConfigMemberType) IsInstance(module android.Module) bool { diff --git a/java/plugin.go b/java/plugin.go index 297ac2cb8..123dbd4c0 100644 --- a/java/plugin.go +++ b/java/plugin.go @@ -14,7 +14,10 @@ package java -import "android/soong/android" +import ( + "android/soong/android" + "android/soong/bazel" +) func init() { registerJavaPluginBuildComponents(android.InitRegistrationContext) @@ -24,7 +27,6 @@ func registerJavaPluginBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("java_plugin", PluginFactory) } -// A java_plugin module describes a host java library that will be used by javac as an annotation processor. func PluginFactory() android.Module { module := &Plugin{} @@ -32,9 +34,13 @@ func PluginFactory() android.Module { module.AddProperties(&module.pluginProperties) InitJavaModule(module, android.HostSupported) + + android.InitBazelModule(module) + return module } +// Plugin describes a java_plugin module, a host java library that will be used by javac as an annotation processor. type Plugin struct { Library @@ -50,3 +56,34 @@ type PluginProperties struct { // parallelism and cause more recompilation for modules that depend on modules that use this plugin. Generates_api *bool } + +type pluginAttributes struct { + *javaCommonAttributes + Deps bazel.LabelListAttribute + Processor_class *string +} + +// ConvertWithBp2build is used to convert android_app to Bazel. +func (p *Plugin) ConvertWithBp2build(ctx android.TopDownMutatorContext) { + pluginName := p.Name() + commonAttrs, depLabels := p.convertLibraryAttrsBp2Build(ctx) + + deps := depLabels.Deps + deps.Append(depLabels.StaticDeps) + + var processorClass *string + if p.pluginProperties.Processor_class != nil { + processorClass = p.pluginProperties.Processor_class + } + + attrs := &pluginAttributes{ + javaCommonAttributes: commonAttrs, + Deps: deps, + Processor_class: processorClass, + } + + props := bazel.BazelTargetModuleProperties{ + Rule_class: "java_plugin", + } + ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: pluginName}, attrs) +} diff --git a/java/plugin_test.go b/java/plugin_test.go index c7913d3db..dc29b1c3e 100644 --- a/java/plugin_test.go +++ b/java/plugin_test.go @@ -15,7 +15,6 @@ package java import ( - "android/soong/android" "testing" ) @@ -58,7 +57,7 @@ func TestPlugin(t *testing.T) { } `) - buildOS := android.BuildOs.String() + buildOS := ctx.Config().BuildOS.String() javac := ctx.ModuleForTests("foo", "android_common").Rule("javac") turbine := ctx.ModuleForTests("foo", "android_common").MaybeRule("turbine") @@ -98,7 +97,7 @@ func TestPluginGeneratesApi(t *testing.T) { } `) - buildOS := android.BuildOs.String() + buildOS := ctx.Config().BuildOS.String() javac := ctx.ModuleForTests("foo", "android_common").Rule("javac") turbine := ctx.ModuleForTests("foo", "android_common").MaybeRule("turbine") diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go index c33e6c229..44650a6d0 100644 --- a/java/prebuilt_apis.go +++ b/java/prebuilt_apis.go @@ -16,6 +16,7 @@ package java import ( "fmt" + "path" "strconv" "strings" @@ -37,6 +38,11 @@ type prebuiltApisProperties struct { // list of api version directories Api_dirs []string + // Directory containing finalized api txt files for extension versions. + // Extension versions higher than the base sdk extension version will + // be assumed to be finalized later than all Api_dirs. + Extensions_dir *string + // The next API directory can optionally point to a directory where // files incompatibility-tracking files are stored for the current // "in progress" API. Each module present in one of the api_dirs will have @@ -60,36 +66,45 @@ func (module *prebuiltApis) GenerateAndroidBuildActions(ctx android.ModuleContex // no need to implement } -func parseJarPath(path string) (module string, apiver string, scope string) { - elements := strings.Split(path, "/") - - apiver = elements[0] - scope = elements[1] +// parsePrebuiltPath parses the relevant variables out of a variety of paths, e.g. +// <version>/<scope>/<module>.jar +// <version>/<scope>/api/<module>.txt +// extensions/<version>/<scope>/<module>.jar +// extensions/<version>/<scope>/api/<module>.txt +func parsePrebuiltPath(ctx android.LoadHookContext, p string) (module string, version string, scope string) { + elements := strings.Split(p, "/") + + scopeIdx := len(elements) - 2 + if elements[scopeIdx] == "api" { + scopeIdx-- + } + scope = elements[scopeIdx] + if scope != "core" && scope != "public" && scope != "system" && scope != "test" && scope != "module-lib" && scope != "system-server" { + ctx.ModuleErrorf("invalid scope %q found in path: %q", scope, p) + return + } + version = elements[scopeIdx-1] - module = strings.TrimSuffix(elements[2], ".jar") + module = strings.TrimSuffix(path.Base(p), path.Ext(p)) return } -func parseApiFilePath(ctx android.LoadHookContext, path string) (module string, apiver string, scope string) { - elements := strings.Split(path, "/") - apiver = elements[0] - - scope = elements[1] - if scope != "public" && scope != "system" && scope != "test" && scope != "module-lib" && scope != "system-server" { - ctx.ModuleErrorf("invalid scope %q found in path: %q", scope, path) +// parseFinalizedPrebuiltPath is like parsePrebuiltPath, but verifies the version is numeric (a finalized version). +func parseFinalizedPrebuiltPath(ctx android.LoadHookContext, p string) (module string, version int, scope string) { + module, v, scope := parsePrebuiltPath(ctx, p) + version, err := strconv.Atoi(v) + if err != nil { + ctx.ModuleErrorf("Found finalized API files in non-numeric dir '%v'", v) return } - - // elements[2] is string literal "api". skipping. - module = strings.TrimSuffix(elements[3], ".txt") return } -func prebuiltApiModuleName(mctx android.LoadHookContext, module string, scope string, apiver string) string { - return mctx.ModuleName() + "_" + scope + "_" + apiver + "_" + module +func prebuiltApiModuleName(mctx android.LoadHookContext, module, scope, version string) string { + return fmt.Sprintf("%s_%s_%s_%s", mctx.ModuleName(), scope, version, module) } -func createImport(mctx android.LoadHookContext, module, scope, apiver, path, sdkVersion string, compileDex bool) { +func createImport(mctx android.LoadHookContext, module, scope, version, path, sdkVersion string, compileDex bool) { props := struct { Name *string Jars []string @@ -97,7 +112,7 @@ func createImport(mctx android.LoadHookContext, module, scope, apiver, path, sdk Installable *bool Compile_dex *bool }{} - props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, apiver)) + props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, version)) props.Jars = append(props.Jars, path) props.Sdk_version = proptools.StringPtr(sdkVersion) props.Installable = proptools.BoolPtr(false) @@ -132,117 +147,125 @@ func createEmptyFile(mctx android.LoadHookContext, name string) { mctx.CreateModule(genrule.GenRuleFactory, &props) } -func getPrebuiltFiles(mctx android.LoadHookContext, p *prebuiltApis, name string) []string { +// globApiDirs collects all the files in all api_dirs and all scopes that match the given glob, e.g. '*.jar' or 'api/*.txt'. +// <api-dir>/<scope>/<glob> for all api-dir and scope. +func globApiDirs(mctx android.LoadHookContext, p *prebuiltApis, api_dir_glob string) []string { var files []string for _, apiver := range p.properties.Api_dirs { - files = append(files, getPrebuiltFilesInSubdir(mctx, apiver, name)...) + files = append(files, globScopeDir(mctx, apiver, api_dir_glob)...) } return files } -func getPrebuiltFilesInSubdir(mctx android.LoadHookContext, subdir string, name string) []string { +// globExtensionDirs collects all the files under the extension dir (for all versions and scopes) that match the given glob +// <extension-dir>/<version>/<scope>/<glob> for all version and scope. +func globExtensionDirs(mctx android.LoadHookContext, p *prebuiltApis, extension_dir_glob string) []string { + // <extensions-dir>/<num>/<extension-dir-glob> + return globScopeDir(mctx, *p.properties.Extensions_dir+"/*", extension_dir_glob) +} + +// globScopeDir collects all the files in the given subdir across all scopes that match the given glob, e.g. '*.jar' or 'api/*.txt'. +// <subdir>/<scope>/<glob> for all scope. +func globScopeDir(mctx android.LoadHookContext, subdir string, subdir_glob string) []string { var files []string dir := mctx.ModuleDir() + "/" + subdir for _, scope := range []string{"public", "system", "test", "core", "module-lib", "system-server"} { - glob := fmt.Sprintf("%s/%s/%s", dir, scope, name) + glob := fmt.Sprintf("%s/%s/%s", dir, scope, subdir_glob) vfiles, err := mctx.GlobWithDeps(glob, nil) if err != nil { - mctx.ModuleErrorf("failed to glob %s files under %q: %s", name, dir+"/"+scope, err) + mctx.ModuleErrorf("failed to glob %s files under %q: %s", subdir_glob, dir+"/"+scope, err) } files = append(files, vfiles...) } + for i, f := range files { + files[i] = strings.TrimPrefix(f, mctx.ModuleDir()+"/") + } return files } func prebuiltSdkStubs(mctx android.LoadHookContext, p *prebuiltApis) { - mydir := mctx.ModuleDir() + "/" // <apiver>/<scope>/<module>.jar - files := getPrebuiltFiles(mctx, p, "*.jar") + files := globApiDirs(mctx, p, "*.jar") sdkVersion := proptools.StringDefault(p.properties.Imports_sdk_version, "current") compileDex := proptools.BoolDefault(p.properties.Imports_compile_dex, false) for _, f := range files { // create a Import module for each jar file - localPath := strings.TrimPrefix(f, mydir) - module, apiver, scope := parseJarPath(localPath) - createImport(mctx, module, scope, apiver, localPath, sdkVersion, compileDex) + module, version, scope := parsePrebuiltPath(mctx, f) + createImport(mctx, module, scope, version, f, sdkVersion, compileDex) + + if module == "core-for-system-modules" { + createSystemModules(mctx, version, scope) + } } } -func createSystemModules(mctx android.LoadHookContext, apiver string) { +func createSystemModules(mctx android.LoadHookContext, version, scope string) { props := struct { Name *string Libs []string }{} - props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", "public", apiver)) - props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", "public", apiver)) + props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", scope, version)) + props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", scope, version)) mctx.CreateModule(systemModulesImportFactory, &props) } -func prebuiltSdkSystemModules(mctx android.LoadHookContext, p *prebuiltApis) { - for _, apiver := range p.properties.Api_dirs { - jar := android.ExistentPathForSource(mctx, - mctx.ModuleDir(), apiver, "public", "core-for-system-modules.jar") - if jar.Valid() { - createSystemModules(mctx, apiver) - } - } -} - func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { - mydir := mctx.ModuleDir() + "/" // <apiver>/<scope>/api/<module>.txt - files := getPrebuiltFiles(mctx, p, "api/*.txt") - - if len(files) == 0 { - mctx.ModuleErrorf("no api file found under %q", mydir) - } - - // construct a map to find out the latest api file path - // for each (<module>, <scope>) pair. - type latestApiInfo struct { - module string - scope string - version int - path string + apiLevelFiles := globApiDirs(mctx, p, "api/*.txt") + if len(apiLevelFiles) == 0 { + mctx.ModuleErrorf("no api file found under %q", mctx.ModuleDir()) } // Create modules for all (<module>, <scope, <version>) triplets, - // and a "latest" module variant for each (<module>, <scope>) pair apiModuleName := func(module, scope, version string) string { return module + ".api." + scope + "." + version } - m := make(map[string]latestApiInfo) - for _, f := range files { - localPath := strings.TrimPrefix(f, mydir) - module, apiver, scope := parseApiFilePath(mctx, localPath) - createApiModule(mctx, apiModuleName(module, scope, apiver), localPath) + for _, f := range apiLevelFiles { + module, version, scope := parseFinalizedPrebuiltPath(mctx, f) + createApiModule(mctx, apiModuleName(module, scope, strconv.Itoa(version)), f) + } - version, err := strconv.Atoi(apiver) - if err != nil { - mctx.ModuleErrorf("Found finalized API files in non-numeric dir %v", apiver) - return - } + // Figure out the latest version of each module/scope + type latestApiInfo struct { + module, scope, path string + version int + } - // Track latest version of each module/scope, except for incompatibilities - if !strings.HasSuffix(module, "incompatibilities") { + getLatest := func(files []string) map[string]latestApiInfo { + m := make(map[string]latestApiInfo) + for _, f := range files { + module, version, scope := parseFinalizedPrebuiltPath(mctx, f) + if strings.HasSuffix(module, "incompatibilities") { + continue + } key := module + "." + scope - info, ok := m[key] - if !ok { - m[key] = latestApiInfo{module, scope, version, localPath} - } else if version > info.version { - info.version = version - info.path = localPath - m[key] = info + info, exists := m[key] + if !exists || version > info.version { + m[key] = latestApiInfo{module, scope, f, version} + } + } + return m + } + + latest := getLatest(apiLevelFiles) + if p.properties.Extensions_dir != nil { + extensionApiFiles := globExtensionDirs(mctx, p, "api/*.txt") + for k, v := range getLatest(extensionApiFiles) { + if v.version > mctx.Config().PlatformBaseSdkExtensionVersion() { + if _, exists := latest[k]; !exists { + mctx.ModuleErrorf("Module %v finalized for extension %d but never during an API level; likely error", v.module, v.version) + } + latest[k] = v } } } // Sort the keys in order to make build.ninja stable - for _, k := range android.SortedStringKeys(m) { - info := m[k] + for _, k := range android.SortedStringKeys(latest) { + info := latest[k] name := apiModuleName(info.module, info.scope, "latest") createApiModule(mctx, name, info.path) } @@ -250,21 +273,20 @@ func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { // Create incompatibilities tracking files for all modules, if we have a "next" api. incompatibilities := make(map[string]bool) if nextApiDir := String(p.properties.Next_api_dir); nextApiDir != "" { - files := getPrebuiltFilesInSubdir(mctx, nextApiDir, "api/*incompatibilities.txt") + files := globScopeDir(mctx, nextApiDir, "api/*incompatibilities.txt") for _, f := range files { - localPath := strings.TrimPrefix(f, mydir) - filename, _, scope := parseApiFilePath(mctx, localPath) + filename, _, scope := parsePrebuiltPath(mctx, f) referencedModule := strings.TrimSuffix(filename, "-incompatibilities") - createApiModule(mctx, apiModuleName(referencedModule+"-incompatibilities", scope, "latest"), localPath) + createApiModule(mctx, apiModuleName(referencedModule+"-incompatibilities", scope, "latest"), f) incompatibilities[referencedModule+"."+scope] = true } } // Create empty incompatibilities files for remaining modules - for _, k := range android.SortedStringKeys(m) { + for _, k := range android.SortedStringKeys(latest) { if _, ok := incompatibilities[k]; !ok { - createEmptyFile(mctx, apiModuleName(m[k].module+"-incompatibilities", m[k].scope, "latest")) + createEmptyFile(mctx, apiModuleName(latest[k].module+"-incompatibilities", latest[k].scope, "latest")) } } } @@ -273,7 +295,6 @@ func createPrebuiltApiModules(mctx android.LoadHookContext) { if p, ok := mctx.Module().(*prebuiltApis); ok { prebuiltApiFiles(mctx, p) prebuiltSdkStubs(mctx, p) - prebuiltSdkSystemModules(mctx, p) } } diff --git a/java/prebuilt_apis_test.go b/java/prebuilt_apis_test.go new file mode 100644 index 000000000..75422ad45 --- /dev/null +++ b/java/prebuilt_apis_test.go @@ -0,0 +1,92 @@ +// Copyright 2021 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 ( + "sort" + "strings" + "testing" + + "android/soong/android" + + "github.com/google/blueprint" +) + +func intPtr(v int) *int { + return &v +} + +func TestPrebuiltApis_SystemModulesCreation(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + FixtureWithPrebuiltApis(map[string][]string{ + "31": {}, + "32": {}, + "current": {}, + }), + ).RunTest(t) + + sdkSystemModules := []string{} + result.VisitAllModules(func(module blueprint.Module) { + name := android.RemoveOptionalPrebuiltPrefix(module.Name()) + if strings.HasPrefix(name, "sdk_") && strings.HasSuffix(name, "_system_modules") { + sdkSystemModules = append(sdkSystemModules, name) + } + }) + sort.Strings(sdkSystemModules) + expected := []string{ + // 31 only has public system modules. + "sdk_public_31_system_modules", + + // 32 and current both have public and module-lib system modules. + "sdk_public_32_system_modules", + "sdk_module-lib_32_system_modules", + "sdk_public_current_system_modules", + "sdk_module-lib_current_system_modules", + } + sort.Strings(expected) + android.AssertArrayString(t, "sdk system modules", expected, sdkSystemModules) +} + +func TestPrebuiltApis_WithExtensions(t *testing.T) { + runTestWithBaseExtensionLevel := func(v int) (foo_input string, bar_input string) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_base_sdk_extension_version = intPtr(v) + }), + FixtureWithPrebuiltApisAndExtensions(map[string][]string{ + "31": {"foo"}, + "32": {"foo", "bar"}, + "current": {"foo", "bar"}, + }, map[string][]string{ + "1": {"foo"}, + "2": {"foo", "bar"}, + }), + ).RunTest(t) + foo_input = result.ModuleForTests("foo.api.public.latest", "").Rule("generator").Implicits[0].String() + bar_input = result.ModuleForTests("bar.api.public.latest", "").Rule("generator").Implicits[0].String() + return + } + // Here, the base extension level is 1, so extension level 2 is the latest + foo_input, bar_input := runTestWithBaseExtensionLevel(1) + android.AssertStringEquals(t, "Expected latest = extension level 2", "prebuilts/sdk/extensions/2/public/api/foo.txt", foo_input) + android.AssertStringEquals(t, "Expected latest = extension level 2", "prebuilts/sdk/extensions/2/public/api/bar.txt", bar_input) + + // Here, the base extension level is 2, so 2 is not later than 32. + foo_input, bar_input = runTestWithBaseExtensionLevel(2) + android.AssertStringEquals(t, "Expected latest = api level 32", "prebuilts/sdk/32/public/api/foo.txt", foo_input) + android.AssertStringEquals(t, "Expected latest = api level 32", "prebuilts/sdk/32/public/api/bar.txt", bar_input) +} diff --git a/java/proto.go b/java/proto.go index 8731822c3..5ba486fd6 100644 --- a/java/proto.go +++ b/java/proto.go @@ -19,12 +19,19 @@ import ( "strconv" "android/soong/android" + "android/soong/bazel" + + "github.com/google/blueprint/proptools" +) + +const ( + protoTypeDefault = "lite" ) func genProto(ctx android.ModuleContext, protoFiles android.Paths, flags android.ProtoFlags) android.Paths { // Shard proto files into groups of 100 to avoid having to recompile all of them if one changes and to avoid // hitting command line length limits. - shards := android.ShardPaths(protoFiles, 100) + shards := android.ShardPaths(protoFiles, 50) srcJarFiles := make(android.Paths, 0, len(shards)) @@ -73,13 +80,15 @@ func genProto(ctx android.ModuleContext, protoFiles android.Paths, flags android } func protoDeps(ctx android.BottomUpMutatorContext, p *android.ProtoProperties) { + const unspecifiedProtobufPluginType = "" if String(p.Proto.Plugin) == "" { switch String(p.Proto.Type) { + case "stream": // does not require additional dependencies case "micro": ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-micro") case "nano": ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-nano") - case "lite", "": + case "lite", unspecifiedProtobufPluginType: ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-lite") case "full": if ctx.Host() || ctx.BazelConversionMode() { @@ -102,6 +111,9 @@ func protoFlags(ctx android.ModuleContext, j *CommonProperties, p *android.Proto if String(p.Proto.Plugin) == "" { var typeToPlugin string switch String(p.Proto.Type) { + case "stream": + flags.proto.OutTypeFlag = "--javastream_out" + typeToPlugin = "javastream" case "micro": flags.proto.OutTypeFlag = "--javamicro_out" typeToPlugin = "javamicro" @@ -129,3 +141,52 @@ func protoFlags(ctx android.ModuleContext, j *CommonProperties, p *android.Proto return flags } + +type protoAttributes struct { + Deps bazel.LabelListAttribute +} + +func bp2buildProto(ctx android.Bp2buildMutatorContext, m *Module, protoSrcs bazel.LabelListAttribute) *bazel.Label { + protoInfo, ok := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, protoSrcs) + if !ok { + return nil + } + + typ := proptools.StringDefault(protoInfo.Type, protoTypeDefault) + var rule_class string + suffix := "_java_proto" + switch typ { + case "nano": + suffix += "_nano" + rule_class = "java_nano_proto_library" + case "micro": + suffix += "_micro" + rule_class = "java_micro_proto_library" + case "lite": + suffix += "_lite" + rule_class = "java_lite_proto_library" + case "stream": + suffix += "_stream" + rule_class = "java_stream_proto_library" + case "full": + rule_class = "java_proto_library" + default: + ctx.PropertyErrorf("proto.type", "cannot handle conversion at this time: %q", typ) + } + + protoLabel := bazel.Label{Label: ":" + m.Name() + "_proto"} + var protoAttrs protoAttributes + protoAttrs.Deps.SetValue(bazel.LabelList{Includes: []bazel.Label{protoLabel}}) + + name := m.Name() + suffix + + ctx.CreateBazelTargetModule( + bazel.BazelTargetModuleProperties{ + Rule_class: rule_class, + Bzl_load_location: "//build/bazel/rules/java:proto.bzl", + }, + android.CommonAttributes{Name: name}, + &protoAttrs) + + return &bazel.Label{Label: ":" + name} +} diff --git a/java/proto_test.go b/java/proto_test.go new file mode 100644 index 000000000..d1cb71448 --- /dev/null +++ b/java/proto_test.go @@ -0,0 +1,53 @@ +// Copyright 2022 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 ( + "strings" + "testing" + + "android/soong/android" +) + +const protoModules = ` +java_library_static { + name: "libprotobuf-java-lite", +} +` + +func TestProtoStream(t *testing.T) { + bp := ` + java_library { + name: "java-stream-protos", + proto: { + type: "stream", + }, + srcs: [ + "a.proto", + "b.proto", + ], + } + ` + + ctx := android.GroupFixturePreparers( + PrepareForIntegrationTestWithJava, + ).RunTestWithBp(t, protoModules+bp) + + proto0 := ctx.ModuleForTests("java-stream-protos", "android_common").Output("proto/proto0.srcjar") + + if cmd := proto0.RuleParams.Command; !strings.Contains(cmd, "--javastream_out=") { + t.Errorf("expected '--javastream_out' in %q", cmd) + } +} diff --git a/java/robolectric.go b/java/robolectric.go index 9fe1f0e0f..f71952172 100644 --- a/java/robolectric.go +++ b/java/robolectric.go @@ -83,6 +83,9 @@ type robolectricTest struct { testConfig android.Path data android.Paths + + forceOSType android.OsType + forceArchType android.ArchType } func (r *robolectricTest) TestSuites() []string { @@ -115,6 +118,9 @@ func (r *robolectricTest) DepsMutator(ctx android.BottomUpMutatorContext) { } func (r *robolectricTest) GenerateAndroidBuildActions(ctx android.ModuleContext) { + r.forceOSType = ctx.Config().BuildOS + r.forceArchType = ctx.Config().BuildArch + r.testConfig = tradefed.AutoGenRobolectricTestConfig(ctx, r.testProperties.Test_config, r.testProperties.Test_config_template, r.testProperties.Test_suites, r.testProperties.Auto_gen_config) @@ -179,7 +185,7 @@ func (r *robolectricTest) GenerateAndroidBuildActions(ctx android.ModuleContext) continue } else if strings.HasSuffix(s, "/BaseRobolectricTest.java") { continue - } else if strings.HasPrefix(s, "src/") { + } else { s = strings.TrimPrefix(s, "src/") } r.tests = append(r.tests, s) @@ -206,7 +212,7 @@ func (r *robolectricTest) GenerateAndroidBuildActions(ctx android.ModuleContext) installDeps = append(installDeps, installedData) } - ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.combinedJar, installDeps...) + r.installFile = ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.combinedJar, installDeps...) } func generateRoboTestConfig(ctx android.ModuleContext, outputFile android.WritablePath, @@ -270,6 +276,10 @@ func (r *robolectricTest) generateRoboSrcJar(ctx android.ModuleContext, outputFi func (r *robolectricTest) AndroidMkEntries() []android.AndroidMkEntries { entriesList := r.Library.AndroidMkEntries() entries := &entriesList[0] + entries.ExtraEntries = append(entries.ExtraEntries, + func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { + entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true) + }) entries.ExtraFooters = []android.AndroidMkExtraFootersFunc{ func(w io.Writer, name, prefix, moduleDir string) { @@ -342,10 +352,9 @@ func RobolectricTestFactory() android.Module { return module } -func (r *robolectricTest) InstallBypassMake() bool { return true } func (r *robolectricTest) InstallInTestcases() bool { return true } func (r *robolectricTest) InstallForceOS() (*android.OsType, *android.ArchType) { - return &android.BuildOs, &android.BuildArch + return &r.forceOSType, &r.forceArchType } func robolectricRuntimesFactory() android.Module { @@ -366,6 +375,9 @@ type robolectricRuntimes struct { props robolectricRuntimesProperties runtimes []android.InstallPath + + forceOSType android.OsType + forceArchType android.ArchType } func (r *robolectricRuntimes) TestSuites() []string { @@ -385,6 +397,9 @@ func (r *robolectricRuntimes) GenerateAndroidBuildActions(ctx android.ModuleCont return } + r.forceOSType = ctx.Config().BuildOS + r.forceArchType = ctx.Config().BuildArch + files := android.PathsForModuleSrc(ctx, r.props.Jars) androidAllDir := android.PathForModuleInstall(ctx, "android-all") @@ -405,15 +420,16 @@ func (r *robolectricRuntimes) GenerateAndroidBuildActions(ctx android.ModuleCont } runtimeFromSourceJar := android.OutputFileForModule(ctx, runtimeFromSourceModule, "") - runtimeName := fmt.Sprintf("android-all-%s-robolectric-r0.jar", - ctx.Config().PlatformSdkCodename()) + // "TREE" name is essential here because it hooks into the "TREE" name in + // Robolectric's SdkConfig.java that will always correspond to the NEWEST_SDK + // in Robolectric configs. + runtimeName := "android-all-current-robolectric-r0.jar" installedRuntime := ctx.InstallFile(androidAllDir, runtimeName, runtimeFromSourceJar) r.runtimes = append(r.runtimes, installedRuntime) } } -func (r *robolectricRuntimes) InstallBypassMake() bool { return true } func (r *robolectricRuntimes) InstallInTestcases() bool { return true } func (r *robolectricRuntimes) InstallForceOS() (*android.OsType, *android.ArchType) { - return &android.BuildOs, &android.BuildArch + return &r.forceOSType, &r.forceArchType } diff --git a/java/rro.go b/java/rro.go index 2e58c042f..c98cbbd2d 100644 --- a/java/rro.go +++ b/java/rro.go @@ -51,6 +51,9 @@ type RuntimeResourceOverlayProperties struct { // Name of the signing certificate lineage file. Lineage *string + // For overriding the --rotation-min-sdk-version property of apksig + RotationMinSdkVersion *string + // optional theme name. If specified, the overlay package will be applied // only when the ro.boot.vendor.overlay.theme system property is set to the same value. Theme *string @@ -90,6 +93,22 @@ type RuntimeResourceOverlayModule interface { Theme() string } +// RRO's partition logic is different from the partition logic of other modules defined in soong/android/paths.go +// The default partition for RRO is "/product" and not "/system" +func rroPartition(ctx android.ModuleContext) string { + var partition string + if ctx.DeviceSpecific() { + partition = ctx.DeviceConfig().OdmPath() + } else if ctx.SocSpecific() { + partition = ctx.DeviceConfig().VendorPath() + } else if ctx.SystemExtSpecific() { + partition = ctx.DeviceConfig().SystemExtPath() + } else { + partition = ctx.DeviceConfig().ProductPath() + } + return partition +} + func (r *RuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) { sdkDep := decodeSdkDep(ctx, android.SdkContext(r)) if sdkDep.hasFrameworkLibs() { @@ -123,7 +142,7 @@ func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleC aaptLinkFlags = append(aaptLinkFlags, "--rename-overlay-target-package "+*r.overridableProperties.Target_package_name) } - r.aapt.buildActions(ctx, r, nil, aaptLinkFlags...) + r.aapt.buildActions(ctx, r, nil, nil, aaptLinkFlags...) // Sign the built package _, certificates := collectAppDeps(ctx, r, false, false) @@ -133,11 +152,15 @@ func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleC if lineage := String(r.properties.Lineage); lineage != "" { lineageFile = android.PathForModuleSrc(ctx, lineage) } - SignAppPackage(ctx, signed, r.aapt.exportPackage, certificates, nil, lineageFile) + + rotationMinSdkVersion := String(r.properties.RotationMinSdkVersion) + + SignAppPackage(ctx, signed, r.aapt.exportPackage, certificates, nil, lineageFile, rotationMinSdkVersion) r.certificate = certificates[0] r.outputFile = signed - r.installDir = android.PathForModuleInstall(ctx, "overlay", String(r.properties.Theme)) + partition := rroPartition(ctx) + r.installDir = android.PathForModuleInPartitionInstall(ctx, partition, "overlay", String(r.properties.Theme)) ctx.InstallFile(r.installDir, r.outputFile.Base(), r.outputFile) } diff --git a/java/rro_test.go b/java/rro_test.go index bad60bc16..00ba5ba11 100644 --- a/java/rro_test.go +++ b/java/rro_test.go @@ -33,6 +33,7 @@ func TestRuntimeResourceOverlay(t *testing.T) { name: "foo", certificate: "platform", lineage: "lineage.bin", + rotationMinSdkVersion: "32", product_specific: true, static_libs: ["bar"], resource_libs: ["baz"], @@ -62,6 +63,7 @@ func TestRuntimeResourceOverlay(t *testing.T) { result := android.GroupFixturePreparers( PrepareForTestWithJavaDefaultModules, PrepareForTestWithOverlayBuildComponents, + android.FixtureModifyConfig(android.SetKatiEnabledForTests), fs.AddToFixture(), ).RunTestWithBp(t, bp) @@ -88,13 +90,14 @@ func TestRuntimeResourceOverlay(t *testing.T) { t.Errorf("Resource lib flag %q missing in aapt2 link flags: %q", resourceLibFlag, aapt2Flags) } - // Check cert signing flag. + // Check cert signing flags. signedApk := m.Output("signed/foo.apk") - lineageFlag := signedApk.Args["flags"] - expectedLineageFlag := "--lineage lineage.bin" - if expectedLineageFlag != lineageFlag { - t.Errorf("Incorrect signing lineage flags, expected: %q, got: %q", expectedLineageFlag, lineageFlag) + actualCertSigningFlags := signedApk.Args["flags"] + expectedCertSigningFlags := "--lineage lineage.bin --rotation-min-sdk-version 32" + if expectedCertSigningFlags != actualCertSigningFlags { + t.Errorf("Incorrect cert signing flags, expected: %q, got: %q", expectedCertSigningFlags, actualCertSigningFlags) } + signingFlag := signedApk.Args["certificates"] expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8" if expected != signingFlag { @@ -127,7 +130,10 @@ func TestRuntimeResourceOverlay(t *testing.T) { } func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) { - ctx, config := testJava(t, ` + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + android.FixtureModifyConfig(android.SetKatiEnabledForTests), + ).RunTestWithBp(t, ` java_defaults { name: "rro_defaults", theme: "default_theme", @@ -148,7 +154,7 @@ func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) { // // RRO module with defaults // - m := ctx.ModuleForTests("foo_with_defaults", "android_common") + m := result.ModuleForTests("foo_with_defaults", "android_common") // Check AAPT2 link flags. aapt2Flags := strings.Split(m.Output("package-res.apk").Args["flags"], " ") @@ -159,14 +165,14 @@ func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) { } // Check device location. - path := android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"] + path := android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"] expectedPath := []string{shared.JoinPath("out/target/product/test_device/product/overlay/default_theme")} - android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path) + android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path) // // RRO module without defaults // - m = ctx.ModuleForTests("foo_barebones", "android_common") + m = result.ModuleForTests("foo_barebones", "android_common") // Check AAPT2 link flags. aapt2Flags = strings.Split(m.Output("package-res.apk").Args["flags"], " ") @@ -176,9 +182,9 @@ func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) { } // Check device location. - path = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"] - expectedPath = []string{shared.JoinPath("out/target/product/test_device/system/overlay")} - android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path) + path = android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"] + expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay")} + android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path) } func TestOverrideRuntimeResourceOverlay(t *testing.T) { @@ -343,3 +349,57 @@ func TestEnforceRRO_propagatesToDependencies(t *testing.T) { }) } } + +func TestRuntimeResourceOverlayPartition(t *testing.T) { + bp := ` + runtime_resource_overlay { + name: "device_specific", + device_specific: true, + } + runtime_resource_overlay { + name: "soc_specific", + soc_specific: true, + } + runtime_resource_overlay { + name: "system_ext_specific", + system_ext_specific: true, + } + runtime_resource_overlay { + name: "product_specific", + product_specific: true, + } + runtime_resource_overlay { + name: "default" + } + ` + testCases := []struct { + name string + expectedPath string + }{ + { + name: "device_specific", + expectedPath: "out/soong/target/product/test_device/odm/overlay", + }, + { + name: "soc_specific", + expectedPath: "out/soong/target/product/test_device/vendor/overlay", + }, + { + name: "system_ext_specific", + expectedPath: "out/soong/target/product/test_device/system_ext/overlay", + }, + { + name: "product_specific", + expectedPath: "out/soong/target/product/test_device/product/overlay", + }, + { + name: "default", + expectedPath: "out/soong/target/product/test_device/product/overlay", + }, + } + for _, testCase := range testCases { + ctx, _ := testJava(t, bp) + mod := ctx.ModuleForTests(testCase.name, "android_common").Module().(*RuntimeResourceOverlay) + android.AssertPathRelativeToTopEquals(t, "Install dir is not correct for "+testCase.name, testCase.expectedPath, mod.installDir) + } +} diff --git a/java/sdk.go b/java/sdk.go index d1b899e48..0dddd40aa 100644 --- a/java/sdk.go +++ b/java/sdk.go @@ -55,9 +55,35 @@ func defaultJavaLanguageVersion(ctx android.EarlyModuleContext, s android.SdkSpe return JAVA_VERSION_7 } else if sdk.FinalOrFutureInt() <= 29 { return JAVA_VERSION_8 - } else { + } else if sdk.FinalOrFutureInt() <= 31 { return JAVA_VERSION_9 + } else { + return JAVA_VERSION_11 + } +} + +// systemModuleKind returns the kind of system modules to use for the supplied combination of sdk +// kind and API level. +func systemModuleKind(sdkKind android.SdkKind, apiLevel android.ApiLevel) android.SdkKind { + systemModuleKind := sdkKind + if apiLevel.LessThanOrEqualTo(android.LastWithoutModuleLibCoreSystemModules) { + // API levels less than or equal to 31 did not provide a core-for-system-modules.jar + // specifically for the module-lib API. So, always use the public system modules for them. + systemModuleKind = android.SdkPublic + } else if systemModuleKind == android.SdkCore { + // Core is by definition what is included in the system module for the public API so should + // just use its system modules. + systemModuleKind = android.SdkPublic + } else if systemModuleKind == android.SdkSystem || systemModuleKind == android.SdkTest { + // The core system and test APIs are currently the same as the public API so they should use + // its system modules. + systemModuleKind = android.SdkPublic + } else if systemModuleKind == android.SdkSystemServer { + // The core system server API is the same as the core module-lib API. + systemModuleKind = android.SdkModule } + + return systemModuleKind } func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) sdkDep { @@ -105,7 +131,8 @@ func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) var systemModules string if defaultJavaLanguageVersion(ctx, sdkVersion).usesJavaModules() { - systemModules = "sdk_public_" + sdkVersion.ApiLevel.String() + "_system_modules" + systemModuleKind := systemModuleKind(sdkVersion.Kind, sdkVersion.ApiLevel) + systemModules = fmt.Sprintf("sdk_%s_%s_system_modules", systemModuleKind, sdkVersion.ApiLevel) } return sdkDep{ @@ -116,13 +143,15 @@ func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) } } - toModule := func(modules []string, res string, aidl android.Path) sdkDep { + toModule := func(module string, aidl android.Path) sdkDep { + // Select the kind of system modules needed for the sdk version. + systemModulesKind := systemModuleKind(sdkVersion.Kind, android.FutureApiLevel) return sdkDep{ useModule: true, - bootclasspath: append(modules, config.DefaultLambdaStubsLibrary), - systemModules: "core-current-stubs-system-modules", - java9Classpath: modules, - frameworkResModule: res, + bootclasspath: []string{module, config.DefaultLambdaStubsLibrary}, + systemModules: fmt.Sprintf("core-%s-stubs-system-modules", systemModulesKind), + java9Classpath: []string{module}, + frameworkResModule: "framework-res", aidl: android.OptionalPathForPath(aidl), } } @@ -161,38 +190,24 @@ func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) noFrameworksLibs: true, } case android.SdkPublic: - return toModule([]string{"android_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx)) + return toModule("android_stubs_current", sdkFrameworkAidlPath(ctx)) case android.SdkSystem: - return toModule([]string{"android_system_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx)) + return toModule("android_system_stubs_current", sdkFrameworkAidlPath(ctx)) case android.SdkTest: - return toModule([]string{"android_test_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx)) + return toModule("android_test_stubs_current", sdkFrameworkAidlPath(ctx)) case android.SdkCore: return sdkDep{ useModule: true, bootclasspath: []string{"core.current.stubs", config.DefaultLambdaStubsLibrary}, - systemModules: "core-current-stubs-system-modules", + systemModules: "core-public-stubs-system-modules", noFrameworksLibs: true, } case android.SdkModule: // TODO(146757305): provide .apk and .aidl that have more APIs for modules - return sdkDep{ - useModule: true, - bootclasspath: []string{"android_module_lib_stubs_current", config.DefaultLambdaStubsLibrary}, - systemModules: "core-module-lib-stubs-system-modules", - java9Classpath: []string{"android_module_lib_stubs_current"}, - frameworkResModule: "framework-res", - aidl: android.OptionalPathForPath(nonUpdatableFrameworkAidlPath(ctx)), - } + return toModule("android_module_lib_stubs_current", nonUpdatableFrameworkAidlPath(ctx)) case android.SdkSystemServer: // TODO(146757305): provide .apk and .aidl that have more APIs for modules - return sdkDep{ - useModule: true, - bootclasspath: []string{"android_system_server_stubs_current", config.DefaultLambdaStubsLibrary}, - systemModules: "core-module-lib-stubs-system-modules", - java9Classpath: []string{"android_system_server_stubs_current"}, - frameworkResModule: "framework-res", - aidl: android.OptionalPathForPath(sdkFrameworkAidlPath(ctx)), - } + return toModule("android_system_server_stubs_current", sdkFrameworkAidlPath(ctx)) default: panic(fmt.Errorf("invalid sdk %q", sdkVersion.Raw)) } @@ -370,7 +385,7 @@ func createAPIFingerprint(ctx android.SingletonContext) { "frameworks-base-api-current.txt", "frameworks-base-api-system-current.txt", "frameworks-base-api-module-lib-current.txt", - "services-system-server-current.txt", + "frameworks-base-api-system-server-current.txt", } count := 0 ctx.VisitAllModules(func(module android.Module) { diff --git a/java/sdk_library.go b/java/sdk_library.go index 8c664383d..47ffc6afc 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -32,25 +32,7 @@ import ( ) const ( - sdkXmlFileSuffix = ".xml" - permissionsTemplate = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n` + - `<!-- Copyright (C) 2018 The Android Open Source Project\n` + - `\n` + - ` Licensed under the Apache License, Version 2.0 (the \"License\");\n` + - ` you may not use this file except in compliance with the License.\n` + - ` You may obtain a copy of the License at\n` + - `\n` + - ` http://www.apache.org/licenses/LICENSE-2.0\n` + - `\n` + - ` Unless required by applicable law or agreed to in writing, software\n` + - ` distributed under the License is distributed on an \"AS IS\" BASIS,\n` + - ` WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n` + - ` See the License for the specific language governing permissions and\n` + - ` limitations under the License.\n` + - `-->\n` + - `<permissions>\n` + - ` <library name=\"%s\" file=\"%s\"/>\n` + - `</permissions>\n` + sdkXmlFileSuffix = ".xml" ) // A tag to associated a dependency with a specific api scope. @@ -361,13 +343,14 @@ type ApiScopeProperties struct { // The sdk_version to use for building the stubs. // // If not specified then it will use an sdk_version determined as follows: + // // 1) If the sdk_version specified on the java_sdk_library is none then this - // will be none. This is used for java_sdk_library instances that are used - // to create stubs that contribute to the core_current sdk version. - // 2) Otherwise, it is assumed that this library extends but does not contribute - // directly to a specific sdk_version and so this uses the sdk_version appropriate - // for the api scope. e.g. public will use sdk_version: current, system will use - // sdk_version: system_current, etc. + // will be none. This is used for java_sdk_library instances that are used + // to create stubs that contribute to the core_current sdk version. + // 2) Otherwise, it is assumed that this library extends but does not + // contribute directly to a specific sdk_version and so this uses the + // sdk_version appropriate for the api scope. e.g. public will use + // sdk_version: current, system will use sdk_version: system_current, etc. // // This does not affect the sdk_version used for either generating the stubs source // or the API file. They both have to use the same sdk_version as is used for @@ -376,6 +359,9 @@ type ApiScopeProperties struct { } type sdkLibraryProperties struct { + // List of source files that are needed to compile the API, but are not part of runtime library. + Api_srcs []string `android:"arch_variant"` + // Visibility for impl library module. If not specified then defaults to the // visibility property. Impl_library_visibility []string @@ -391,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"` @@ -419,7 +408,7 @@ type sdkLibraryProperties struct { // local files that are used within user customized droiddoc options. Droiddoc_option_files []string - // additional droiddoc options + // additional droiddoc options. // Available variables for substitution: // // $(location <label>): the path to the droiddoc_option_files with name <label> @@ -537,7 +526,7 @@ type scopePaths struct { // The dex jar for the stubs. // // This is not the implementation jar, it still only contains stubs. - stubsDexJarPath android.Path + stubsDexJarPath OptionalDexJarPath // The API specification file, e.g. system_current.txt. currentApiFilePath android.OptionalPath @@ -547,6 +536,9 @@ type scopePaths struct { // The stubs source jar. stubsSrcJar android.OptionalPath + + // Extracted annotations. + annotationsZip android.OptionalPath } func (paths *scopePaths) extractStubsLibraryInfoFromDependency(ctx android.ModuleContext, dep android.Module) error { @@ -582,6 +574,7 @@ func (paths *scopePaths) treatDepAsApiStubsSrcProvider(dep android.Module, actio } func (paths *scopePaths) extractApiInfoFromApiStubsProvider(provider ApiStubsProvider) { + paths.annotationsZip = android.OptionalPathForPath(provider.AnnotationsZip()) paths.currentApiFilePath = android.OptionalPathForPath(provider.ApiFilePath()) paths.removedApiFilePath = android.OptionalPathForPath(provider.RemovedApiFilePath()) } @@ -629,6 +622,33 @@ type commonToSdkLibraryAndImportProperties struct { // Files containing information about supported java doc tags. Doctag_files []string `android:"path"` + + // Signals that this shared library is part of the bootclasspath starting + // on the version indicated in this attribute. + // + // This will make platforms at this level and above to ignore + // <uses-library> tags with this library name because the library is already + // available + On_bootclasspath_since *string + + // Signals that this shared library was part of the bootclasspath before + // (but not including) the version indicated in this attribute. + // + // The system will automatically add a <uses-library> tag with this library to + // apps that target any SDK less than the version indicated in this attribute. + On_bootclasspath_before *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is below the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Min_device_sdk *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is above the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Max_device_sdk *string } // commonSdkLibraryAndImportModule defines the interface that must be provided by a module that @@ -736,6 +756,8 @@ const ( apiTxtComponentName = "api.txt" removedApiTxtComponentName = "removed-api.txt" + + annotationsComponentName = "annotations.zip" ) // A regular expression to match tags that reference a specific stubs component. @@ -754,7 +776,7 @@ var tagSplitter = func() *regexp.Regexp { scopesRegexp := choice(allScopeNames...) // Regular expression to match one of the components. - componentsRegexp := choice(stubsSourceComponentName, apiTxtComponentName, removedApiTxtComponentName) + componentsRegexp := choice(stubsSourceComponentName, apiTxtComponentName, removedApiTxtComponentName, annotationsComponentName) // Regular expression to match any combination of one scope and one component. return regexp.MustCompile(fmt.Sprintf(`^\.(%s)\.(%s)$`, scopesRegexp, componentsRegexp)) @@ -762,9 +784,7 @@ var tagSplitter = func() *regexp.Regexp { // For OutputFileProducer interface // -// .<scope>.stubs.source -// .<scope>.api.txt -// .<scope>.removed-api.txt +// .<scope>.<component name>, for all ComponentNames (for example: .public.removed-api.txt) func (c *commonToSdkLibraryAndImport) commonOutputFiles(tag string) (android.Paths, error) { if groups := tagSplitter.FindStringSubmatch(tag); groups != nil { scopeName := groups[1] @@ -791,6 +811,11 @@ func (c *commonToSdkLibraryAndImport) commonOutputFiles(tag string) (android.Pat if paths.removedApiFilePath.Valid() { return android.Paths{paths.removedApiFilePath.Path()}, nil } + + case annotationsComponentName: + if paths.annotationsZip.Valid() { + return android.Paths{paths.annotationsZip.Path()}, nil + } } return nil, fmt.Errorf("%s not available for api scope %s", component, scopeName) @@ -903,10 +928,10 @@ func sdkKindToApiScope(kind android.SdkKind) *apiScope { } // to satisfy SdkLibraryDependency interface -func (c *commonToSdkLibraryAndImport) SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) android.Path { +func (c *commonToSdkLibraryAndImport) SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath { paths := c.selectScopePaths(ctx, kind) if paths == nil { - return nil + return makeUnsetDexJarPath() } return paths.stubsDexJarPath @@ -981,13 +1006,15 @@ func (e *EmbeddableSdkLibraryComponent) SdkLibraryName() *string { } // to satisfy SdkLibraryComponentDependency -func (e *EmbeddableSdkLibraryComponent) OptionalImplicitSdkLibrary() *string { - return e.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack -} - -// to satisfy SdkLibraryComponentDependency func (e *EmbeddableSdkLibraryComponent) OptionalSdkLibraryImplementation() *string { - // Currently implementation library name is the same as the SDK library name. + // For shared libraries, this is the same as the SDK library name. If a Java library or app + // depends on a component library (e.g. a stub library) it still needs to know the name of the + // run-time library and the corresponding module that provides the implementation. This name is + // passed to manifest_fixer (to be added to AndroidManifest.xml) and added to CLC (to be used + // in dexpreopt). + // + // For non-shared SDK (component or not) libraries this returns `nil`, as they are not + // <uses-library> and should not be added to the manifest or to CLC. return e.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack } @@ -999,12 +1026,6 @@ type SdkLibraryComponentDependency interface { // SdkLibraryName returns the name of the java_sdk_library/_import module. SdkLibraryName() *string - // The optional name of the sdk library that should be implicitly added to the - // AndroidManifest of an app that contains code which references the sdk library. - // - // Returns the name of the optional implicit SDK library or nil, if there isn't one. - OptionalImplicitSdkLibrary() *string - // The name of the implementation library for the optional SDK library or nil, if there isn't one. OptionalSdkLibraryImplementation() *string } @@ -1036,7 +1057,7 @@ type SdkLibraryDependency interface { // SdkApiStubDexJar returns the dex jar for the stubs. It is needed by the hiddenapi processing // tool which processes dex files. - SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) android.Path + SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath // SdkRemovedTxtFile returns the optional path to the removed.txt file for the specified sdk kind. SdkRemovedTxtFile(ctx android.BaseModuleContext, kind android.SdkKind) android.OptionalPath @@ -1110,6 +1131,22 @@ func (module *SdkLibrary) getGeneratedApiScopes(ctx android.EarlyModuleContext) return generatedScopes } +var _ android.ModuleWithMinSdkVersionCheck = (*SdkLibrary)(nil) + +func (module *SdkLibrary) CheckMinSdkVersion(ctx android.ModuleContext) { + android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx).ApiLevel, func(c android.ModuleContext, do android.PayloadDepsCallback) { + ctx.WalkDeps(func(child android.Module, parent android.Module) bool { + isExternal := !module.depIsInSameApex(ctx, child) + if am, ok := child.(android.ApexModule); ok { + if !do(ctx, parent, am, isExternal) { + return false + } + } + return !isExternal + }) + }) +} + type sdkLibraryComponentTag struct { blueprint.BaseDependencyTag name string @@ -1187,14 +1224,23 @@ func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { func (module *SdkLibrary) OutputFiles(tag string) (android.Paths, error) { paths, err := module.commonOutputFiles(tag) - if paths == nil && err == nil { - return module.Library.OutputFiles(tag) - } else { + if paths != nil || err != nil { return paths, err } + if module.requiresRuntimeImplementationLibrary() { + return module.Library.OutputFiles(tag) + } + if tag == "" { + return nil, nil + } + return nil, fmt.Errorf("unsupported module reference tag %q", tag) } func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { + if proptools.String(module.deviceProperties.Min_sdk_version) != "" { + module.CheckMinSdkVersion(ctx) + } + module.generateCommonBuildActions(ctx) // Only build an implementation library if required. @@ -1303,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, @@ -1315,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{}{ @@ -1442,6 +1496,7 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC props.Name = proptools.StringPtr(name) props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_source_visibility) props.Srcs = append(props.Srcs, module.properties.Srcs...) + props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...) props.Sdk_version = module.deviceProperties.Sdk_version props.System_modules = module.deviceProperties.System_modules props.Installable = proptools.BoolPtr(false) @@ -1467,15 +1522,15 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC } droidstubsArgs = append(droidstubsArgs, module.sdkLibraryProperties.Droiddoc_options...) disabledWarnings := []string{ - "MissingPermission", "BroadcastBehavior", - "HiddenSuperclass", "DeprecationMismatch", - "UnavailableSymbol", - "SdkConstant", + "HiddenSuperclass", "HiddenTypeParameter", + "MissingPermission", + "SdkConstant", "Todo", "Typo", + "UnavailableSymbol", } droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide ")) @@ -1570,14 +1625,29 @@ func (module *SdkLibrary) UniqueApexVariations() bool { // Creates the xml file that publicizes the runtime library func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) { + moduleMinApiLevel := module.Library.MinSdkVersion(mctx).ApiLevel + var moduleMinApiLevelStr = moduleMinApiLevel.String() + if moduleMinApiLevel == android.NoneApiLevel { + moduleMinApiLevelStr = "current" + } props := struct { - Name *string - Lib_name *string - Apex_available []string + Name *string + Lib_name *string + Apex_available []string + On_bootclasspath_since *string + On_bootclasspath_before *string + Min_device_sdk *string + Max_device_sdk *string + Sdk_library_min_api_level *string }{ - Name: proptools.StringPtr(module.xmlPermissionsModuleName()), - Lib_name: proptools.StringPtr(module.BaseModuleName()), - Apex_available: module.ApexProperties.Apex_available, + Name: proptools.StringPtr(module.xmlPermissionsModuleName()), + Lib_name: proptools.StringPtr(module.BaseModuleName()), + Apex_available: module.ApexProperties.Apex_available, + On_bootclasspath_since: module.commonSdkLibraryProperties.On_bootclasspath_since, + On_bootclasspath_before: module.commonSdkLibraryProperties.On_bootclasspath_before, + Min_device_sdk: module.commonSdkLibraryProperties.Min_device_sdk, + Max_device_sdk: module.commonSdkLibraryProperties.Max_device_sdk, + Sdk_library_min_api_level: &moduleMinApiLevelStr, } mctx.CreateModule(sdkLibraryXmlFactory, &props) @@ -1700,8 +1770,12 @@ func (module *SdkLibrary) CreateInternalModules(mctx android.DefaultableHookCont path := path.Join(mctx.ModuleDir(), apiDir, scope.apiFilePrefix+api) p := android.ExistentPathForSource(mctx, path) if !p.Valid() { - mctx.ModuleErrorf("Current api file %#v doesn't exist", path) - missingCurrentApi = true + if mctx.Config().AllowMissingDependencies() { + mctx.AddMissingDependencies([]string{path}) + } else { + mctx.ModuleErrorf("Current api file %#v doesn't exist", path) + missingCurrentApi = true + } } } } @@ -1751,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() { @@ -1884,6 +1959,9 @@ type sdkLibraryScopeProperties struct { // The removed.txt Removed_api *string `android:"path"` + + // Annotation zip + Annotations *string `android:"path"` } type sdkLibraryImportProperties struct { @@ -1895,7 +1973,6 @@ type sdkLibraryImportProperties struct { Compile_dex *bool // If not empty, classes are restricted to the specified packages and their sub-packages. - // This information is used to generate the updatable-bcp-packages.txt file. Permitted_packages []string } @@ -1907,6 +1984,7 @@ type SdkLibraryImport struct { android.SdkBase hiddenAPI + dexpreopter properties sdkLibraryImportProperties @@ -1923,8 +2001,12 @@ type SdkLibraryImport struct { // Is nil if the source module does not exist. xmlPermissionsFileModule *sdkLibraryXml - // Path to the dex implementation jar obtained from the prebuilt_apex, if any. - dexJarFile android.Path + // Build path to the dex implementation jar obtained from the prebuilt_apex, if any. + dexJarFile OptionalDexJarPath + + // Expected install file path of the source module(sdk_library) + // or dex implementation jar obtained from the prebuilt_apex, if any. + installFile android.Path } var _ SdkLibraryDependency = (*SdkLibraryImport)(nil) @@ -2107,6 +2189,14 @@ func (module *SdkLibraryImport) DepsMutator(ctx android.BottomUpMutatorContext) } } +func (module *SdkLibraryImport) AndroidMkEntries() []android.AndroidMkEntries { + // For an SDK library imported from a prebuilt APEX, we don't need a Make module for itself, as we + // don't need to install it. However, we need to add its dexpreopt outputs as sub-modules, if it + // is preopted. + dexpreoptEntries := module.dexpreopter.AndroidMkEntriesForApex() + return append(dexpreoptEntries, android.AndroidMkEntries{Disabled: true}) +} + var _ android.ApexModule = (*SdkLibraryImport)(nil) // Implements android.ApexModule @@ -2133,14 +2223,30 @@ 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) { module.generateCommonBuildActions(ctx) - var deapexerModule android.Module + // Assume that source module(sdk_library) is installed in /<sdk_library partition>/framework + module.installFile = android.PathForModuleInstall(ctx, "framework", module.Stem()+".jar") // Record the paths to the prebuilt stubs library and stubs source. ctx.VisitDirectDeps(func(to android.Module) { @@ -2167,15 +2273,6 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo ctx.ModuleErrorf("xml permissions file module must be of type *sdkLibraryXml but was %T", to) } } - - // Save away the `deapexer` module on which this depends, if any. - if tag == android.DeapexerTag { - if deapexerModule != nil { - ctx.ModuleErrorf("Ambiguous duplicate deapexer module dependencies %q and %q", - deapexerModule.Name(), to.Name()) - } - deapexerModule = to - } }) // Populate the scope paths with information from the properties. @@ -2185,6 +2282,7 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo } paths := module.getScopePathsCreateIfNeeded(apiScope) + paths.annotationsZip = android.OptionalPathForModuleSrc(ctx, scopeProperties.Annotations) paths.currentApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Current_api) paths.removedApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Removed_api) } @@ -2194,22 +2292,28 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo // obtained from the associated deapexer module. ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) if ai.ForPrebuiltApex { - if deapexerModule == nil { - // This should never happen as a variant for a prebuilt_apex is only created if the - // deapxer module has been configured to export the dex implementation jar for this module. - ctx.ModuleErrorf("internal error: module %q does not depend on a `deapexer` module for prebuilt_apex %q", - module.Name(), ai.ApexVariationName) - } - // Get the path of the dex implementation jar from the `deapexer` module. - di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo) + di := android.FindDeapexerProviderForModule(ctx) + if di == nil { + return // An error has been reported by FindDeapexerProviderForModule. + } if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(module.BaseModuleName())); dexOutputPath != nil { - module.dexJarFile = dexOutputPath - module.initHiddenAPI(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil) + dexJarFile := makeDexJarPathFromPath(dexOutputPath) + module.dexJarFile = dexJarFile + installPath := android.PathForModuleInPartitionInstall( + ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(module.BaseModuleName())) + module.installFile = installPath + module.initHiddenAPI(ctx, dexJarFile, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil) + + // Dexpreopting. + module.dexpreopter.installPath = module.dexpreopter.getInstallPath(ctx, installPath) + module.dexpreopter.isSDKLibrary = true + module.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &module.dexpreopter) + module.dexpreopt(ctx, dexOutputPath) } else { // This should never happen as a variant for a prebuilt_apex is only created if the // prebuilt_apex has been configured to export the java library dex file. - ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt_apex %q", deapexerModule.Name()) + ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt APEX %s", di.ApexModuleName()) } } } @@ -2244,14 +2348,14 @@ func (module *SdkLibraryImport) SdkImplementationJars(ctx android.BaseModuleCont } // to satisfy UsesLibraryDependency interface -func (module *SdkLibraryImport) DexJarBuildPath() android.Path { +func (module *SdkLibraryImport) DexJarBuildPath() OptionalDexJarPath { // The dex implementation jar extracted from the .apex file should be used in preference to the // source. - if module.dexJarFile != nil { + if module.dexJarFile.IsSet() { return module.dexJarFile } if module.implLibraryModule == nil { - return nil + return makeUnsetDexJarPath() } else { return module.implLibraryModule.DexJarBuildPath() } @@ -2259,11 +2363,7 @@ func (module *SdkLibraryImport) DexJarBuildPath() android.Path { // to satisfy UsesLibraryDependency interface func (module *SdkLibraryImport) DexJarInstallPath() android.Path { - if module.implLibraryModule == nil { - return nil - } else { - return module.implLibraryModule.DexJarInstallPath() - } + return module.installFile } // to satisfy UsesLibraryDependency interface @@ -2289,17 +2389,17 @@ func (module *SdkLibraryImport) LintDepSets() LintDepSets { } } -func (module *SdkLibraryImport) getStrictUpdatabilityLinting() bool { +func (module *SdkLibraryImport) GetStrictUpdatabilityLinting() bool { if module.implLibraryModule == nil { return false } else { - return module.implLibraryModule.getStrictUpdatabilityLinting() + return module.implLibraryModule.GetStrictUpdatabilityLinting() } } -func (module *SdkLibraryImport) setStrictUpdatabilityLinting(strictLinting bool) { +func (module *SdkLibraryImport) SetStrictUpdatabilityLinting(strictLinting bool) { if module.implLibraryModule != nil { - module.implLibraryModule.setStrictUpdatabilityLinting(strictLinting) + module.implLibraryModule.SetStrictUpdatabilityLinting(strictLinting) } } @@ -2328,6 +2428,11 @@ func (module *SdkLibraryImport) ImplementationAndResourcesJars() android.Paths { } } +// to satisfy java.DexpreopterInterface interface +func (module *SdkLibraryImport) IsInstallable() bool { + return true +} + var _ android.RequiredFilesFromPrebuiltApex = (*SdkLibraryImport)(nil) func (module *SdkLibraryImport) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string { @@ -2354,6 +2459,38 @@ type sdkLibraryXml struct { type sdkLibraryXmlProperties struct { // canonical name of the lib Lib_name *string + + // Signals that this shared library is part of the bootclasspath starting + // on the version indicated in this attribute. + // + // This will make platforms at this level and above to ignore + // <uses-library> tags with this library name because the library is already + // available + On_bootclasspath_since *string + + // Signals that this shared library was part of the bootclasspath before + // (but not including) the version indicated in this attribute. + // + // The system will automatically add a <uses-library> tag with this library to + // apps that target any SDK less than the version indicated in this attribute. + On_bootclasspath_before *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is below the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Min_device_sdk *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is above the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Max_device_sdk *string + + // The SdkLibrary's min api level as a string + // + // This value comes from the ApiLevel of the MinSdkVersion property. + Sdk_library_min_api_level *string } // java_sdk_library_xml builds the permission xml file for a java_sdk_library. @@ -2430,11 +2567,90 @@ func (module *sdkLibraryXml) implPath(ctx android.ModuleContext) string { return "/" + partition + "/framework/" + implName + ".jar" } +func formattedOptionalSdkLevelAttribute(ctx android.ModuleContext, attrName string, value *string) string { + if value == nil { + return "" + } + apiLevel, err := android.ApiLevelFromUser(ctx, *value) + if err != nil { + // attributes in bp files have underscores but in the xml have dashes. + ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), err.Error()) + return "" + } + if apiLevel.IsCurrent() { + // passing "current" would always mean a future release, never the current (or the current in + // progress) which means some conditions would never be triggered. + ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), + `"current" is not an allowed value for this attribute`) + return "" + } + // "safeValue" is safe because it translates finalized codenames to a string + // with their SDK int. + safeValue := apiLevel.String() + return formattedOptionalAttribute(attrName, &safeValue) +} + +// formats an attribute for the xml permissions file if the value is not null +// returns empty string otherwise +func formattedOptionalAttribute(attrName string, value *string) string { + if value == nil { + return "" + } + return fmt.Sprintf(` %s=\"%s\"\n`, attrName, *value) +} + +func (module *sdkLibraryXml) permissionsContents(ctx android.ModuleContext) string { + libName := proptools.String(module.properties.Lib_name) + libNameAttr := formattedOptionalAttribute("name", &libName) + filePath := module.implPath(ctx) + filePathAttr := formattedOptionalAttribute("file", &filePath) + implicitFromAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-since", module.properties.On_bootclasspath_since) + implicitUntilAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-before", module.properties.On_bootclasspath_before) + minSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "min-device-sdk", module.properties.Min_device_sdk) + maxSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "max-device-sdk", module.properties.Max_device_sdk) + // <library> is understood in all android versions whereas <apex-library> is only understood from API T (and ignored before that). + // similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the apex-library to make sure this library is not loaded before T + var libraryTag string + if module.properties.Min_device_sdk != nil { + libraryTag = ` <apex-library\n` + } else { + libraryTag = ` <library\n` + } + + return strings.Join([]string{ + `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n`, + `<!-- Copyright (C) 2018 The Android Open Source Project\n`, + `\n`, + ` Licensed under the Apache License, Version 2.0 (the \"License\");\n`, + ` you may not use this file except in compliance with the License.\n`, + ` You may obtain a copy of the License at\n`, + `\n`, + ` http://www.apache.org/licenses/LICENSE-2.0\n`, + `\n`, + ` Unless required by applicable law or agreed to in writing, software\n`, + ` distributed under the License is distributed on an \"AS IS\" BASIS,\n`, + ` WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n`, + ` See the License for the specific language governing permissions and\n`, + ` limitations under the License.\n`, + `-->\n`, + `<permissions>\n`, + libraryTag, + libNameAttr, + filePathAttr, + implicitFromAttr, + implicitUntilAttr, + minSdkAttr, + maxSdkAttr, + ` />\n`, + `</permissions>\n`}, "") +} + func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleContext) { module.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform() libName := proptools.String(module.properties.Lib_name) - xmlContent := fmt.Sprintf(permissionsTemplate, libName, module.implPath(ctx)) + module.selfValidate(ctx) + xmlContent := module.permissionsContents(ctx) module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath rule := android.NewRuleBuilder(pctx, ctx) @@ -2449,30 +2665,105 @@ func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleConte func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries { if module.hideApexVariantFromMake { - return []android.AndroidMkEntries{android.AndroidMkEntries{ + return []android.AndroidMkEntries{{ Disabled: true, }} } - return []android.AndroidMkEntries{android.AndroidMkEntries{ + return []android.AndroidMkEntries{{ Class: "ETC", OutputFile: android.OptionalPathForPath(module.outputFilePath), ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetString("LOCAL_MODULE_TAGS", "optional") - entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.ToMakePath().String()) + entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.String()) entries.SetString("LOCAL_INSTALLED_MODULE_STEM", module.outputFilePath.Base()) }, }, }} } +func (module *sdkLibraryXml) selfValidate(ctx android.ModuleContext) { + module.validateAtLeastTAttributes(ctx) + module.validateMinAndMaxDeviceSdk(ctx) + module.validateMinMaxDeviceSdkAndModuleMinSdk(ctx) + module.validateOnBootclasspathBeforeRequirements(ctx) +} + +func (module *sdkLibraryXml) validateAtLeastTAttributes(ctx android.ModuleContext) { + t := android.ApiLevelOrPanic(ctx, "Tiramisu") + module.attrAtLeastT(ctx, t, module.properties.Min_device_sdk, "min_device_sdk") + module.attrAtLeastT(ctx, t, module.properties.Max_device_sdk, "max_device_sdk") + module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_before, "on_bootclasspath_before") + module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_since, "on_bootclasspath_since") +} + +func (module *sdkLibraryXml) attrAtLeastT(ctx android.ModuleContext, t android.ApiLevel, attr *string, attrName string) { + if attr != nil { + if level, err := android.ApiLevelFromUser(ctx, *attr); err == nil { + // we will inform the user of invalid inputs when we try to write the + // permissions xml file so we don't need to do it here + if t.GreaterThan(level) { + ctx.PropertyErrorf(attrName, "Attribute value needs to be at least T") + } + } + } +} + +func (module *sdkLibraryXml) validateMinAndMaxDeviceSdk(ctx android.ModuleContext) { + if module.properties.Min_device_sdk != nil && module.properties.Max_device_sdk != nil { + min, minErr := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk) + max, maxErr := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk) + if minErr == nil && maxErr == nil { + // we will inform the user of invalid inputs when we try to write the + // permissions xml file so we don't need to do it here + if min.GreaterThan(max) { + ctx.ModuleErrorf("min_device_sdk can't be greater than max_device_sdk") + } + } + } +} + +func (module *sdkLibraryXml) validateMinMaxDeviceSdkAndModuleMinSdk(ctx android.ModuleContext) { + moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level) + if module.properties.Min_device_sdk != nil { + api, err := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk) + if err == nil { + if moduleMinApi.GreaterThan(api) { + ctx.PropertyErrorf("min_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi) + } + } + } + if module.properties.Max_device_sdk != nil { + api, err := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk) + if err == nil { + if moduleMinApi.GreaterThan(api) { + ctx.PropertyErrorf("max_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi) + } + } + } +} + +func (module *sdkLibraryXml) validateOnBootclasspathBeforeRequirements(ctx android.ModuleContext) { + moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level) + if module.properties.On_bootclasspath_before != nil { + t := android.ApiLevelOrPanic(ctx, "Tiramisu") + // if we use the attribute, then we need to do this validation + if moduleMinApi.LessThan(t) { + // if minAPi is < T, then we need to have min_device_sdk (which only accepts T+) + if module.properties.Min_device_sdk == nil { + ctx.PropertyErrorf("on_bootclasspath_before", "Using this property requires that the module's min_sdk_version or the shared library's min_device_sdk is at least T") + } + } + } +} + type sdkLibrarySdkMemberType struct { android.SdkMemberTypeBase } -func (s *sdkLibrarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) { - mctx.AddVariationDependencies(nil, dependencyTag, names...) +func (s *sdkLibrarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) { + ctx.AddVariationDependencies(nil, dependencyTag, names...) } func (s *sdkLibrarySdkMemberType) IsInstance(module android.Module) bool { @@ -2499,7 +2790,7 @@ type sdkLibrarySdkMemberProperties struct { android.SdkMemberPropertiesBase // Scope to per scope properties. - Scopes map[*apiScope]scopeProperties + Scopes map[*apiScope]*scopeProperties // The Java stubs source files. Stub_srcs []string @@ -2518,6 +2809,33 @@ type sdkLibrarySdkMemberProperties struct { Doctag_paths android.Paths Permitted_packages []string + + // Signals that this shared library is part of the bootclasspath starting + // on the version indicated in this attribute. + // + // This will make platforms at this level and above to ignore + // <uses-library> tags with this library name because the library is already + // available + On_bootclasspath_since *string + + // Signals that this shared library was part of the bootclasspath before + // (but not including) the version indicated in this attribute. + // + // The system will automatically add a <uses-library> tag with this library to + // apps that target any SDK less than the version indicated in this attribute. + On_bootclasspath_before *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is below the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Min_device_sdk *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is above the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Max_device_sdk *string } type scopeProperties struct { @@ -2525,13 +2843,14 @@ type scopeProperties struct { StubsSrcJar android.Path CurrentApiFile android.Path RemovedApiFile android.Path + AnnotationsZip android.Path `supported_build_releases:"Tiramisu+"` SdkVersion string } func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) { sdk := variant.(*SdkLibrary) - s.Scopes = make(map[*apiScope]scopeProperties) + s.Scopes = make(map[*apiScope]*scopeProperties) for _, apiScope := range allApiScopes { paths := sdk.findScopePaths(apiScope) if paths == nil { @@ -2550,7 +2869,11 @@ func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMembe if paths.removedApiFilePath.Valid() { properties.RemovedApiFile = paths.removedApiFilePath.Path() } - s.Scopes[apiScope] = properties + // The annotations zip is only available for modules that set annotations_enabled: true. + if paths.annotationsZip.Valid() { + properties.AnnotationsZip = paths.annotationsZip.Path() + } + s.Scopes[apiScope] = &properties } } @@ -2559,6 +2882,10 @@ func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMembe s.Compile_dex = sdk.dexProperties.Compile_dex s.Doctag_paths = sdk.doctagPaths s.Permitted_packages = sdk.PermittedPackagesForUpdatableBootJars() + s.On_bootclasspath_since = sdk.commonSdkLibraryProperties.On_bootclasspath_since + s.On_bootclasspath_before = sdk.commonSdkLibraryProperties.On_bootclasspath_before + s.Min_device_sdk = sdk.commonSdkLibraryProperties.Min_device_sdk + s.Max_device_sdk = sdk.commonSdkLibraryProperties.Max_device_sdk } func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) { @@ -2614,6 +2941,12 @@ func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberCo scopeSet.AddProperty("removed_api", removedApiSnapshotPath) } + if properties.AnnotationsZip != nil { + annotationsSnapshotPath := filepath.Join(scopeDir, ctx.Name()+"_annotations.zip") + ctx.SnapshotBuilder().CopyToSnapshot(properties.AnnotationsZip, annotationsSnapshotPath) + scopeSet.AddProperty("annotations", annotationsSnapshotPath) + } + if properties.SdkVersion != "" { scopeSet.AddProperty("sdk_version", properties.SdkVersion) } diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go index 65af95314..3500c84d2 100644 --- a/java/sdk_library_test.go +++ b/java/sdk_library_test.go @@ -15,12 +15,13 @@ package java import ( - "android/soong/android" "fmt" "path/filepath" "regexp" "testing" + "android/soong/android" + "github.com/google/blueprint/proptools" ) @@ -47,6 +48,7 @@ func TestJavaSdkLibrary(t *testing.T) { name: "bar", srcs: ["a.java", "b.java"], api_packages: ["bar"], + exclude_kotlinc_generated_files: true, } java_library { name: "baz", @@ -107,7 +109,7 @@ func TestJavaSdkLibrary(t *testing.T) { libs: ["foo"], sdk_version: "module_30", } - `) + `) // check the existence of the internal modules foo := result.ModuleForTests("foo", "android_common") @@ -122,7 +124,7 @@ func TestJavaSdkLibrary(t *testing.T) { result.ModuleForTests("foo.api.system.28", "") result.ModuleForTests("foo.api.test.28", "") - exportedComponentsInfo := result.ModuleProvider(foo.Module(), ExportedComponentsInfoProvider).(ExportedComponentsInfo) + exportedComponentsInfo := result.ModuleProvider(foo.Module(), android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo) expectedFooExportedComponents := []string{ "foo.stubs", "foo.stubs.source", @@ -156,9 +158,198 @@ func TestJavaSdkLibrary(t *testing.T) { // test if baz has exported SDK lib names foo and bar to qux qux := result.ModuleForTests("qux", "android_common") if quxLib, ok := qux.Module().(*Library); ok { - sdkLibs := quxLib.ClassLoaderContexts().UsesLibs() - android.AssertDeepEquals(t, "qux exports", []string{"foo", "bar", "fred", "quuz"}, sdkLibs) + requiredSdkLibs, optionalSdkLibs := quxLib.ClassLoaderContexts().UsesLibs() + android.AssertDeepEquals(t, "qux exports (required)", []string{"fred", "quuz", "foo", "bar"}, requiredSdkLibs) + android.AssertDeepEquals(t, "qux exports (optional)", []string{}, optionalSdkLibs) } + + fooDexJar := result.ModuleForTests("foo", "android_common").Rule("d8") + // tests if kotlinc generated files are NOT excluded from output of foo. + android.AssertStringDoesNotContain(t, "foo dex", fooDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module") + + barDexJar := result.ModuleForTests("bar", "android_common").Rule("d8") + // tests if kotlinc generated files are excluded from output of bar. + android.AssertStringDoesContain(t, "bar dex", barDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module") +} + +func TestJavaSdkLibrary_UpdatableLibrary(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "28": {"foo"}, + "29": {"foo"}, + "30": {"foo", "fooUpdatable", "fooUpdatableErr"}, + }), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V", "W", "X"} + }), + ).RunTestWithBp(t, + ` + java_sdk_library { + name: "fooUpdatable", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + on_bootclasspath_since: "U", + on_bootclasspath_before: "V", + min_device_sdk: "W", + max_device_sdk: "X", + min_sdk_version: "S", + } + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + } +`) + + // test that updatability attributes are passed on correctly + fooUpdatable := result.ModuleForTests("fooUpdatable.xml", "android_common").Rule("java_sdk_xml") + android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-since=\"U\"`) + android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-before=\"V\"`) + android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `min-device-sdk=\"W\"`) + android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `max-device-sdk=\"X\"`) + + // double check that updatability attributes are not written if they don't exist in the bp file + // the permissions file for the foo library defined above + fooPermissions := result.ModuleForTests("foo.xml", "android_common").Rule("java_sdk_xml") + android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `on-bootclasspath-since`) + android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `on-bootclasspath-before`) + android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `min-device-sdk`) + android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `max-device-sdk`) +} + +func TestJavaSdkLibrary_UpdatableLibrary_Validation_ValidVersion(t *testing.T) { + android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "30": {"fooUpdatable", "fooUpdatableErr"}, + }), + ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern( + []string{ + `on_bootclasspath_since: "aaa" could not be parsed as an integer and is not a recognized codename`, + `on_bootclasspath_before: "bbc" could not be parsed as an integer and is not a recognized codename`, + `min_device_sdk: "ccc" could not be parsed as an integer and is not a recognized codename`, + `max_device_sdk: "current" is not an allowed value for this attribute`, + })).RunTestWithBp(t, + ` + java_sdk_library { + name: "fooUpdatableErr", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + on_bootclasspath_since: "aaa", + on_bootclasspath_before: "bbc", + min_device_sdk: "ccc", + max_device_sdk: "current", + } +`) +} + +func TestJavaSdkLibrary_UpdatableLibrary_Validation_AtLeastTAttributes(t *testing.T) { + android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "28": {"foo"}, + }), + ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern( + []string{ + "on_bootclasspath_since: Attribute value needs to be at least T", + "on_bootclasspath_before: Attribute value needs to be at least T", + "min_device_sdk: Attribute value needs to be at least T", + "max_device_sdk: Attribute value needs to be at least T", + }, + )).RunTestWithBp(t, + ` + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + on_bootclasspath_since: "S", + on_bootclasspath_before: "S", + min_device_sdk: "S", + max_device_sdk: "S", + min_sdk_version: "S", + } +`) +} + +func TestJavaSdkLibrary_UpdatableLibrary_Validation_MinAndMaxDeviceSdk(t *testing.T) { + android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "28": {"foo"}, + }), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V"} + }), + ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern( + []string{ + "min_device_sdk can't be greater than max_device_sdk", + }, + )).RunTestWithBp(t, + ` + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + min_device_sdk: "V", + max_device_sdk: "U", + min_sdk_version: "S", + } +`) +} + +func TestJavaSdkLibrary_UpdatableLibrary_Validation_MinAndMaxDeviceSdkAndModuleMinSdk(t *testing.T) { + android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "28": {"foo"}, + }), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V"} + }), + ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern( + []string{ + regexp.QuoteMeta("min_device_sdk: Can't be less than module's min sdk (V)"), + regexp.QuoteMeta("max_device_sdk: Can't be less than module's min sdk (V)"), + }, + )).RunTestWithBp(t, + ` + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + min_device_sdk: "U", + max_device_sdk: "U", + min_sdk_version: "V", + } +`) +} + +func TestJavaSdkLibrary_UpdatableLibrary_usesNewTag(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "30": {"foo"}, + }), + ).RunTestWithBp(t, + ` + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + min_device_sdk: "Tiramisu", + min_sdk_version: "S", + } +`) + // test that updatability attributes are passed on correctly + fooUpdatable := result.ModuleForTests("foo.xml", "android_common").Rule("java_sdk_xml") + android.AssertStringDoesContain(t, "foo.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `<apex-library`) + android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `<library`) } func TestJavaSdkLibrary_StubOrImplOnlyLibs(t *testing.T) { @@ -247,7 +438,7 @@ func TestJavaSdkLibrary_DoNotAccessImplWhenItIsNotBuilt(t *testing.T) { } } -func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) { +func TestJavaSdkLibrary_AccessOutputFiles(t *testing.T) { android.GroupFixturePreparers( prepareForJavaTest, PrepareForTestWithJavaSdkLibraryFiles, @@ -257,6 +448,31 @@ func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) { name: "foo", srcs: ["a.java"], api_packages: ["foo"], + annotations_enabled: true, + public: { + enabled: true, + }, + } + java_library { + name: "bar", + srcs: ["b.java", ":foo{.public.stubs.source}"], + java_resources: [":foo{.public.annotations.zip}"], + } + `) +} + +func TestJavaSdkLibrary_AccessOutputFiles_NoAnnotations(t *testing.T) { + android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis("foo"), + ). + ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": path dependency ":foo{.public.annotations.zip}": annotations.zip not available for api scope public`)). + RunTestWithBp(t, ` + java_sdk_library { + name: "foo", + srcs: ["a.java"], + api_packages: ["foo"], public: { enabled: true, }, @@ -265,6 +481,7 @@ func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) { java_library { name: "bar", srcs: ["b.java", ":foo{.public.stubs.source}"], + java_resources: [":foo{.public.annotations.zip}"], } `) } @@ -328,6 +545,7 @@ func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) { stub_srcs: ["a.java"], current_api: "api/current.txt", removed_api: "api/removed.txt", + annotations: "x/annotations.zip", }, } @@ -337,6 +555,7 @@ func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) { java_resources: [ ":foo{.public.api.txt}", ":foo{.public.removed-api.txt}", + ":foo{.public.annotations.zip}", ], } `) @@ -597,6 +816,7 @@ func TestJavaSdkLibraryImport(t *testing.T) { } CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{ + `dex2oatd`, `prebuilt_sdklib.stubs`, `prebuilt_sdklib.stubs.source.test`, `prebuilt_sdklib.stubs.system`, @@ -673,7 +893,6 @@ func TestJavaSdkLibraryImport_Preferred(t *testing.T) { `) CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{ - `dex2oatd`, `prebuilt_sdklib`, `sdklib.impl`, `sdklib.stubs`, @@ -682,6 +901,7 @@ func TestJavaSdkLibraryImport_Preferred(t *testing.T) { }) CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{ + `dex2oatd`, `prebuilt_sdklib.stubs`, `sdklib.impl`, `sdklib.xml`, @@ -930,3 +1150,87 @@ func TestJavaSdkLibraryDist(t *testing.T) { }) } } + +func TestSdkLibrary_CheckMinSdkVersion(t *testing.T) { + preparer := android.GroupFixturePreparers( + PrepareForTestWithJavaBuildComponents, + PrepareForTestWithJavaDefaultModules, + PrepareForTestWithJavaSdkLibraryFiles, + ) + + preparer.RunTestWithBp(t, ` + java_sdk_library { + name: "sdklib", + srcs: ["a.java"], + static_libs: ["util"], + min_sdk_version: "30", + unsafe_ignore_missing_latest_api: true, + } + + java_library { + name: "util", + srcs: ["a.java"], + min_sdk_version: "30", + } + `) + + preparer. + RunTestWithBp(t, ` + java_sdk_library { + name: "sdklib", + srcs: ["a.java"], + libs: ["util"], + impl_only_libs: ["util"], + stub_only_libs: ["util"], + stub_only_static_libs: ["util"], + min_sdk_version: "30", + unsafe_ignore_missing_latest_api: true, + } + + java_library { + name: "util", + srcs: ["a.java"], + } + `) + + preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "util".*should support min_sdk_version\(30\)`)). + RunTestWithBp(t, ` + java_sdk_library { + name: "sdklib", + srcs: ["a.java"], + static_libs: ["util"], + min_sdk_version: "30", + unsafe_ignore_missing_latest_api: true, + } + + java_library { + name: "util", + srcs: ["a.java"], + min_sdk_version: "31", + } + `) + + preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "another_util".*should support min_sdk_version\(30\)`)). + RunTestWithBp(t, ` + java_sdk_library { + name: "sdklib", + srcs: ["a.java"], + static_libs: ["util"], + min_sdk_version: "30", + unsafe_ignore_missing_latest_api: true, + } + + java_library { + name: "util", + srcs: ["a.java"], + static_libs: ["another_util"], + min_sdk_version: "30", + } + + java_library { + name: "another_util", + srcs: ["a.java"], + min_sdk_version: "31", + } + `) +} diff --git a/java/sdk_test.go b/java/sdk_test.go index bb595a54e..9e8ba6ed0 100644 --- a/java/sdk_test.go +++ b/java/sdk_test.go @@ -25,27 +25,36 @@ import ( "android/soong/java/config" ) +type classpathTestCase struct { + name string + unbundled bool + moduleType string + host android.OsClass + properties string + + // for java 8 + bootclasspath []string + java8classpath []string + + // for java 9 + system string + java9classpath []string + + forces8 bool // if set, javac will always be called with java 8 arguments + + aidl string + + // Indicates how this test case is affected by the setting of Always_use_prebuilt_sdks. + // + // If this is nil then the test case is unaffected by the setting of Always_use_prebuilt_sdks. + // Otherwise, the test case can only be used when + // Always_use_prebuilt_sdks=*forAlwaysUsePrebuiltSdks. + forAlwaysUsePrebuiltSdks *bool +} + func TestClasspath(t *testing.T) { const frameworkAidl = "-I" + defaultJavaDir + "/framework/aidl" - var classpathTestcases = []struct { - name string - unbundled bool - moduleType string - host android.OsClass - properties string - - // for java 8 - bootclasspath []string - java8classpath []string - - // for java 9 - system string - java9classpath []string - - forces8 bool // if set, javac will always be called with java 8 arguments - - aidl string - }{ + var classpathTestcases = []classpathTestCase{ { name: "default", bootclasspath: config.StableCorePlatformBootclasspathLibraries, @@ -91,25 +100,52 @@ func TestClasspath(t *testing.T) { aidl: "-pprebuilts/sdk/30/public/framework.aidl", }, { + // Test case only applies when Always_use_prebuilt_sdks=false (the default). + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false), name: "current", properties: `sdk_version: "current",`, bootclasspath: []string{"android_stubs_current", "core-lambda-stubs"}, - system: "core-current-stubs-system-modules", + system: "core-public-stubs-system-modules", java9classpath: []string{"android_stubs_current"}, aidl: "-pout/soong/framework.aidl", }, { + // Test case only applies when Always_use_prebuilt_sdks=true. + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true), + + name: "current", + properties: `sdk_version: "current",`, + bootclasspath: []string{`""`}, + system: "sdk_public_current_system_modules", + java8classpath: []string{"prebuilts/sdk/current/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/current/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/current/public/framework.aidl", + }, + { + // Test case only applies when Always_use_prebuilt_sdks=false (the default). + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false), name: "system_current", properties: `sdk_version: "system_current",`, bootclasspath: []string{"android_system_stubs_current", "core-lambda-stubs"}, - system: "core-current-stubs-system-modules", + system: "core-public-stubs-system-modules", java9classpath: []string{"android_system_stubs_current"}, aidl: "-pout/soong/framework.aidl", }, { + // Test case only applies when Always_use_prebuilt_sdks=true. + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true), + name: "system_current", + properties: `sdk_version: "system_current",`, + bootclasspath: []string{`""`}, + system: "sdk_public_current_system_modules", + java8classpath: []string{"prebuilts/sdk/current/system/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/current/system/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/current/public/framework.aidl", + }, + { name: "system_29", properties: `sdk_version: "system_29",`, bootclasspath: []string{`""`}, @@ -118,7 +154,6 @@ func TestClasspath(t *testing.T) { aidl: "-pprebuilts/sdk/29/public/framework.aidl", }, { - name: "system_30", properties: `sdk_version: "system_30",`, bootclasspath: []string{`""`}, @@ -128,20 +163,57 @@ func TestClasspath(t *testing.T) { aidl: "-pprebuilts/sdk/30/public/framework.aidl", }, { + // Test case only applies when Always_use_prebuilt_sdks=false (the default). + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false), name: "test_current", properties: `sdk_version: "test_current",`, bootclasspath: []string{"android_test_stubs_current", "core-lambda-stubs"}, - system: "core-current-stubs-system-modules", + system: "core-public-stubs-system-modules", java9classpath: []string{"android_test_stubs_current"}, aidl: "-pout/soong/framework.aidl", }, { + // Test case only applies when Always_use_prebuilt_sdks=true. + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true), + + name: "test_current", + properties: `sdk_version: "test_current",`, + bootclasspath: []string{`""`}, + system: "sdk_public_current_system_modules", + java8classpath: []string{"prebuilts/sdk/current/test/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/current/test/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/current/public/framework.aidl", + }, + { + name: "test_30", + properties: `sdk_version: "test_30",`, + bootclasspath: []string{`""`}, + system: "sdk_public_30_system_modules", + java8classpath: []string{"prebuilts/sdk/30/test/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/30/test/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/30/public/framework.aidl", + }, + { + // Test case only applies when Always_use_prebuilt_sdks=false (the default). + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false), name: "core_current", properties: `sdk_version: "core_current",`, bootclasspath: []string{"core.current.stubs", "core-lambda-stubs"}, - system: "core-current-stubs-system-modules", + system: "core-public-stubs-system-modules", + }, + { + // Test case only applies when Always_use_prebuilt_sdks=true. + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true), + + name: "core_current", + properties: `sdk_version: "core_current",`, + bootclasspath: []string{`""`}, + system: "sdk_public_current_system_modules", + java8classpath: []string{"prebuilts/sdk/current/core/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/current/core/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/current/public/framework.aidl", }, { @@ -214,8 +286,10 @@ func TestClasspath(t *testing.T) { java9classpath: []string{"prebuilts/sdk/current/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, aidl: "-pprebuilts/sdk/current/public/framework.aidl", }, - { + // Test case only applies when Always_use_prebuilt_sdks=false (the default). + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false), + name: "module_current", properties: `sdk_version: "module_current",`, bootclasspath: []string{"android_module_lib_stubs_current", "core-lambda-stubs"}, @@ -224,6 +298,48 @@ func TestClasspath(t *testing.T) { aidl: "-pout/soong/framework_non_updatable.aidl", }, { + // Test case only applies when Always_use_prebuilt_sdks=true. + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true), + + name: "module_current", + properties: `sdk_version: "module_current",`, + bootclasspath: []string{`""`}, + system: "sdk_module-lib_current_system_modules", + java8classpath: []string{"prebuilts/sdk/current/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/current/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/current/public/framework.aidl", + }, + { + name: "module_30", + properties: `sdk_version: "module_30",`, + bootclasspath: []string{`""`}, + system: "sdk_public_30_system_modules", + java8classpath: []string{"prebuilts/sdk/30/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/30/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/30/public/framework.aidl", + }, + { + name: "module_31", + properties: `sdk_version: "module_31",`, + bootclasspath: []string{`""`}, + system: "sdk_public_31_system_modules", + java8classpath: []string{"prebuilts/sdk/31/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/31/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/31/public/framework.aidl", + }, + { + name: "module_32", + properties: `sdk_version: "module_32",`, + bootclasspath: []string{`""`}, + system: "sdk_module-lib_32_system_modules", + java8classpath: []string{"prebuilts/sdk/32/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/32/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/32/public/framework.aidl", + }, + { + // Test case only applies when Always_use_prebuilt_sdks=false (the default). + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false), + name: "system_server_current", properties: `sdk_version: "system_server_current",`, bootclasspath: []string{"android_system_server_stubs_current", "core-lambda-stubs"}, @@ -231,9 +347,62 @@ func TestClasspath(t *testing.T) { java9classpath: []string{"android_system_server_stubs_current"}, aidl: "-pout/soong/framework.aidl", }, + { + // Test case only applies when Always_use_prebuilt_sdks=true. + forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true), + + name: "system_server_current", + properties: `sdk_version: "system_server_current",`, + bootclasspath: []string{`""`}, + system: "sdk_module-lib_current_system_modules", + java8classpath: []string{"prebuilts/sdk/current/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/current/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/current/public/framework.aidl", + }, + { + name: "system_server_30", + properties: `sdk_version: "system_server_30",`, + bootclasspath: []string{`""`}, + system: "sdk_public_30_system_modules", + java8classpath: []string{"prebuilts/sdk/30/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/30/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/30/public/framework.aidl", + }, + { + name: "system_server_31", + properties: `sdk_version: "system_server_31",`, + bootclasspath: []string{`""`}, + system: "sdk_public_31_system_modules", + java8classpath: []string{"prebuilts/sdk/31/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/31/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/31/public/framework.aidl", + }, + { + name: "system_server_32", + properties: `sdk_version: "system_server_32",`, + bootclasspath: []string{`""`}, + system: "sdk_module-lib_32_system_modules", + java8classpath: []string{"prebuilts/sdk/32/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + java9classpath: []string{"prebuilts/sdk/32/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"}, + aidl: "-pprebuilts/sdk/32/public/framework.aidl", + }, } + t.Run("basic", func(t *testing.T) { + testClasspathTestCases(t, classpathTestcases, false) + }) + + t.Run("Always_use_prebuilt_sdks=true", func(t *testing.T) { + testClasspathTestCases(t, classpathTestcases, true) + }) +} + +func testClasspathTestCases(t *testing.T, classpathTestcases []classpathTestCase, alwaysUsePrebuiltSdks bool) { for _, testcase := range classpathTestcases { + if testcase.forAlwaysUsePrebuiltSdks != nil && *testcase.forAlwaysUsePrebuiltSdks != alwaysUsePrebuiltSdks { + continue + } + t.Run(testcase.name, func(t *testing.T) { moduleType := "java_library" if testcase.moduleType != "" { @@ -255,9 +424,11 @@ func TestClasspath(t *testing.T) { ` + testcase.properties + ` }` - variant := "android_common" - if testcase.host == android.Host { - variant = android.BuildOs.String() + "_common" + variant := func(result *android.TestResult) string { + if testcase.host == android.Host { + return result.Config.BuildOS.String() + "_common" + } + return "android_common" } convertModulesToPaths := func(cp []string) []string { @@ -297,7 +468,9 @@ func TestClasspath(t *testing.T) { system = "--system=none" } else if testcase.system != "" { dir := "" - if strings.HasPrefix(testcase.system, "sdk_public_") { + // If the system modules name starts with sdk_ then it is a prebuilt module and so comes + // from the prebuilt directory. + if strings.HasPrefix(testcase.system, "sdk_") { dir = "prebuilts/sdk" } else { dir = defaultJavaDir @@ -312,7 +485,7 @@ func TestClasspath(t *testing.T) { } checkClasspath := func(t *testing.T, result *android.TestResult, isJava8 bool) { - foo := result.ModuleForTests("foo", variant) + foo := result.ModuleForTests("foo", variant(result)) javac := foo.Rule("javac") var deps []string @@ -349,11 +522,20 @@ func TestClasspath(t *testing.T) { android.AssertPathsRelativeToTopEquals(t, "implicits", deps, javac.Implicits) } + preparer := android.NullFixturePreparer + if alwaysUsePrebuiltSdks { + preparer = android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true) + }) + } + fixtureFactory := android.GroupFixturePreparers( prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{ "29": {}, "30": {}, + "31": {}, + "32": {}, "current": {}, }), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { @@ -367,6 +549,7 @@ func TestClasspath(t *testing.T) { env["ANDROID_JAVA8_HOME"] = "jdk8" } }), + preparer, ) // Test with legacy javac -source 1.8 -target 1.8 @@ -376,7 +559,7 @@ func TestClasspath(t *testing.T) { checkClasspath(t, result, true /* isJava8 */) if testcase.host != android.Host { - aidl := result.ModuleForTests("foo", variant).Rule("aidl") + aidl := result.ModuleForTests("foo", variant(result)).Rule("aidl") android.AssertStringDoesContain(t, "aidl command", aidl.RuleParams.Command, testcase.aidl+" -I.") } @@ -389,7 +572,7 @@ func TestClasspath(t *testing.T) { checkClasspath(t, result, false /* isJava8 */) if testcase.host != android.Host { - aidl := result.ModuleForTests("foo", variant).Rule("aidl") + aidl := result.ModuleForTests("foo", variant(result)).Rule("aidl") android.AssertStringDoesContain(t, "aidl command", aidl.RuleParams.Command, testcase.aidl+" -I.") } diff --git a/java/system_modules.go b/java/system_modules.go index d0dc74adc..fec8ebaeb 100644 --- a/java/system_modules.go +++ b/java/system_modules.go @@ -245,8 +245,8 @@ type systemModulesSdkMemberType struct { android.SdkMemberTypeBase } -func (mt *systemModulesSdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) { - mctx.AddVariationDependencies(nil, dependencyTag, names...) +func (mt *systemModulesSdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) { + ctx.AddVariationDependencies(nil, dependencyTag, names...) } func (mt *systemModulesSdkMemberType) IsInstance(module android.Module) bool { diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go index a2006b778..fa61ea68c 100644 --- a/java/systemserver_classpath_fragment.go +++ b/java/systemserver_classpath_fragment.go @@ -23,11 +23,19 @@ import ( func init() { registerSystemserverClasspathBuildComponents(android.InitRegistrationContext) + + android.RegisterSdkMemberType(&systemServerClasspathFragmentMemberType{ + SdkMemberTypeBase: android.SdkMemberTypeBase{ + PropertyName: "systemserverclasspath_fragments", + SupportsSdk: true, + }, + }) } func registerSystemserverClasspathBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("platform_systemserverclasspath", platformSystemServerClasspathFactory) ctx.RegisterModuleType("systemserverclasspath_fragment", systemServerClasspathFactory) + ctx.RegisterModuleType("prebuilt_systemserverclasspath_fragment", prebuiltSystemServerClasspathModuleFactory) } type platformSystemServerClasspathModule struct { @@ -48,22 +56,35 @@ func (p *platformSystemServerClasspathModule) AndroidMkEntries() (entries []andr } func (p *platformSystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { - classpathJars := configuredJarListToClasspathJars(ctx, p.ClasspathFragmentToConfiguredJarList(ctx), p.classpathType) - p.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars) + configuredJars := p.configuredJars(ctx) + classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, p.classpathType) + standaloneConfiguredJars := p.standaloneConfiguredJars(ctx) + standaloneClasspathJars := configuredJarListToClasspathJars(ctx, standaloneConfiguredJars, STANDALONE_SYSTEMSERVER_JARS) + configuredJars = configuredJars.AppendList(&standaloneConfiguredJars) + classpathJars = append(classpathJars, standaloneClasspathJars...) + p.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars) } -func (p *platformSystemServerClasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList { - global := dexpreopt.GetGlobalConfig(ctx) - return global.SystemServerJars +func (p *platformSystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList { + // TODO(satayev): include any apex jars that don't populate their classpath proto config. + return dexpreopt.GetGlobalConfig(ctx).SystemServerJars +} + +func (p *platformSystemServerClasspathModule) standaloneConfiguredJars(ctx android.ModuleContext) android.ConfiguredJarList { + return dexpreopt.GetGlobalConfig(ctx).StandaloneSystemServerJars } type SystemServerClasspathModule struct { android.ModuleBase android.ApexModuleBase + android.SdkBase ClasspathFragmentBase properties systemServerClasspathFragmentProperties + + // Collect the module directory for IDE info in java/jdeps.go. + modulePaths []string } func (s *SystemServerClasspathModule) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error { @@ -71,39 +92,85 @@ func (s *SystemServerClasspathModule) ShouldSupportSdkVersion(ctx android.BaseMo } type systemServerClasspathFragmentProperties struct { - // The contents of this systemserverclasspath_fragment, could be either java_library, or java_sdk_library. + // List of system_server classpath jars, could be either java_library, or java_sdk_library. // // The order of this list matters as it is the order that is used in the SYSTEMSERVERCLASSPATH. Contents []string + + // List of jars that system_server loads dynamically using separate classloaders. + // + // The order does not matter. + Standalone_contents []string } func systemServerClasspathFactory() android.Module { m := &SystemServerClasspathModule{} m.AddProperties(&m.properties) android.InitApexModule(m) + android.InitSdkAwareModule(m) initClasspathFragment(m, SYSTEMSERVERCLASSPATH) android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) return m } func (s *SystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { - if len(s.properties.Contents) == 0 { - ctx.PropertyErrorf("contents", "empty contents are not allowed") + if len(s.properties.Contents) == 0 && len(s.properties.Standalone_contents) == 0 { + ctx.PropertyErrorf("contents", "Either contents or standalone_contents needs to be non-empty") + } + + configuredJars := s.configuredJars(ctx) + classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, s.classpathType) + standaloneConfiguredJars := s.standaloneConfiguredJars(ctx) + standaloneClasspathJars := configuredJarListToClasspathJars(ctx, standaloneConfiguredJars, STANDALONE_SYSTEMSERVER_JARS) + configuredJars = configuredJars.AppendList(&standaloneConfiguredJars) + classpathJars = append(classpathJars, standaloneClasspathJars...) + s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars) + + // Collect the module directory for IDE info in java/jdeps.go. + s.modulePaths = append(s.modulePaths, ctx.ModuleDir()) +} + +func (s *SystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList { + global := dexpreopt.GetGlobalConfig(ctx) + + possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, s.properties.Contents, systemServerClasspathFragmentContentDepTag) + jars, unknown := global.ApexSystemServerJars.Filter(possibleUpdatableModules) + // TODO(satayev): remove geotz ssc_fragment, since geotz is not part of SSCP anymore. + _, unknown = android.RemoveFromList("geotz", unknown) + // This module only exists in car products. + // So ignore it even if it is not in PRODUCT_APEX_SYSTEM_SERVER_JARS. + // TODO(b/203233647): Add better mechanism to make it optional. + _, unknown = android.RemoveFromList("car-frameworks-service-module", unknown) + + // This module is optional, so it is not present in all products. + // (See PRODUCT_ISOLATED_COMPILATION_ENABLED.) + // So ignore it even if it is not in PRODUCT_APEX_SYSTEM_SERVER_JARS. + // TODO(b/203233647): Add better mechanism to make it optional. + _, unknown = android.RemoveFromList("service-compos", unknown) + + // TODO(satayev): for apex_test we want to include all contents unconditionally to classpaths + // config. However, any test specific jars would not be present in ApexSystemServerJars. Instead, + // we should check if we are creating a config for apex_test via ApexInfo and amend the values. + // This is an exception to support end-to-end test for ApexdUnitTests, until such support exists. + if android.InList("test_service-apexd", possibleUpdatableModules) { + jars = jars.Append("com.android.apex.test_package", "test_service-apexd") + } else if global.ApexSystemServerJars.Len() > 0 && len(unknown) > 0 && !android.IsModuleInVersionedSdk(ctx.Module()) { + // For non test apexes, make sure that all contents are actually declared in make. + ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_SYSTEM_SERVER_JARS", unknown) } - classpathJars := configuredJarListToClasspathJars(ctx, s.ClasspathFragmentToConfiguredJarList(ctx), s.classpathType) - s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars) + return jars } -func (s *SystemServerClasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList { +func (s *SystemServerClasspathModule) standaloneConfiguredJars(ctx android.ModuleContext) android.ConfiguredJarList { global := dexpreopt.GetGlobalConfig(ctx) - possibleUpdatableModules := gatherPossibleUpdatableModuleNamesAndStems(ctx, s.properties.Contents, systemServerClasspathFragmentContentDepTag) + possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, s.properties.Standalone_contents, systemServerClasspathFragmentContentDepTag) + jars, _ := global.ApexStandaloneSystemServerJars.Filter(possibleUpdatableModules) - // Only create configs for updatable boot jars. Non-updatable system server jars must be part of the - // platform_systemserverclasspath's classpath proto config to guarantee that they come before any - // updatable jars at runtime. - return global.UpdatableSystemServerJars.Filter(possibleUpdatableModules) + // TODO(jiakaiz): add a check to ensure that the contents are declared in make. + + return jars } type systemServerClasspathFragmentContentDependencyTag struct { @@ -115,11 +182,33 @@ func (systemServerClasspathFragmentContentDependencyTag) ReplaceSourceWithPrebui return false } +// SdkMemberType causes dependencies added with this tag to be automatically added to the sdk as if +// they were specified using java_systemserver_libs or java_sdk_libs. +func (b systemServerClasspathFragmentContentDependencyTag) SdkMemberType(child android.Module) android.SdkMemberType { + // If the module is a java_sdk_library then treat it as if it was specified in the java_sdk_libs + // property, otherwise treat if it was specified in the java_systemserver_libs property. + if javaSdkLibrarySdkMemberType.IsInstance(child) { + return javaSdkLibrarySdkMemberType + } + + return javaSystemserverLibsSdkMemberType +} + +func (b systemServerClasspathFragmentContentDependencyTag) ExportMember() bool { + return true +} + // Contents of system server fragments in an apex are considered to be directly in the apex, as if // they were listed in java_libs. func (systemServerClasspathFragmentContentDependencyTag) CopyDirectlyInAnyApex() {} +// Contents of system server fragments require files from prebuilt apex files. +func (systemServerClasspathFragmentContentDependencyTag) RequiresFilesFromPrebuiltApex() {} + +var _ android.ReplaceSourceWithPrebuilt = systemServerClasspathFragmentContentDepTag +var _ android.SdkMemberDependencyTag = systemServerClasspathFragmentContentDepTag var _ android.CopyDirectlyInAnyApexTag = systemServerClasspathFragmentContentDepTag +var _ android.RequiresFilesFromPrebuiltApexTag = systemServerClasspathFragmentContentDepTag // The tag used for the dependency between the systemserverclasspath_fragment module and its contents. var systemServerClasspathFragmentContentDepTag = systemServerClasspathFragmentContentDependencyTag{} @@ -130,8 +219,113 @@ func IsSystemServerClasspathFragmentContentDepTag(tag blueprint.DependencyTag) b func (s *SystemServerClasspathModule) ComponentDepsMutator(ctx android.BottomUpMutatorContext) { module := ctx.Module() + _, isSourceModule := module.(*SystemServerClasspathModule) + var deps []string + deps = append(deps, s.properties.Contents...) + deps = append(deps, s.properties.Standalone_contents...) - for _, name := range s.properties.Contents { + for _, name := range deps { + // A systemserverclasspath_fragment must depend only on other source modules, while the + // prebuilt_systemserverclasspath_fragment_fragment must only depend on other prebuilt modules. + if !isSourceModule { + name = android.PrebuiltNameFromSource(name) + } ctx.AddDependency(module, systemServerClasspathFragmentContentDepTag, name) } } + +// Collect information for opening IDE project files in java/jdeps.go. +func (s *SystemServerClasspathModule) IDEInfo(dpInfo *android.IdeInfo) { + dpInfo.Deps = append(dpInfo.Deps, s.properties.Contents...) + dpInfo.Deps = append(dpInfo.Deps, s.properties.Standalone_contents...) + dpInfo.Paths = append(dpInfo.Paths, s.modulePaths...) +} + +type systemServerClasspathFragmentMemberType struct { + android.SdkMemberTypeBase +} + +func (s *systemServerClasspathFragmentMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) { + ctx.AddVariationDependencies(nil, dependencyTag, names...) +} + +func (s *systemServerClasspathFragmentMemberType) IsInstance(module android.Module) bool { + _, ok := module.(*SystemServerClasspathModule) + return ok +} + +func (s *systemServerClasspathFragmentMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule { + return ctx.SnapshotBuilder().AddPrebuiltModule(member, "prebuilt_systemserverclasspath_fragment") +} + +func (s *systemServerClasspathFragmentMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties { + return &systemServerClasspathFragmentSdkMemberProperties{} +} + +type systemServerClasspathFragmentSdkMemberProperties struct { + android.SdkMemberPropertiesBase + + // List of system_server classpath jars, could be either java_library, or java_sdk_library. + // + // The order of this list matters as it is the order that is used in the SYSTEMSERVERCLASSPATH. + Contents []string + + // List of jars that system_server loads dynamically using separate classloaders. + // + // The order does not matter. + Standalone_contents []string +} + +func (s *systemServerClasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) { + module := variant.(*SystemServerClasspathModule) + + s.Contents = module.properties.Contents + s.Standalone_contents = module.properties.Standalone_contents +} + +func (s *systemServerClasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) { + builder := ctx.SnapshotBuilder() + requiredMemberDependency := builder.SdkMemberReferencePropertyTag(true) + + if len(s.Contents) > 0 { + propertySet.AddPropertyWithTag("contents", s.Contents, requiredMemberDependency) + } + + if len(s.Standalone_contents) > 0 { + propertySet.AddPropertyWithTag("standalone_contents", s.Standalone_contents, requiredMemberDependency) + } +} + +var _ android.SdkMemberType = (*systemServerClasspathFragmentMemberType)(nil) + +// A prebuilt version of the systemserverclasspath_fragment module. +type prebuiltSystemServerClasspathModule struct { + SystemServerClasspathModule + prebuilt android.Prebuilt +} + +func (module *prebuiltSystemServerClasspathModule) Prebuilt() *android.Prebuilt { + return &module.prebuilt +} + +func (module *prebuiltSystemServerClasspathModule) Name() string { + return module.prebuilt.Name(module.ModuleBase.Name()) +} + +func (module *prebuiltSystemServerClasspathModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string { + return nil +} + +var _ android.RequiredFilesFromPrebuiltApex = (*prebuiltSystemServerClasspathModule)(nil) + +func prebuiltSystemServerClasspathModuleFactory() android.Module { + m := &prebuiltSystemServerClasspathModule{} + m.AddProperties(&m.properties) + // This doesn't actually have any prebuilt files of its own so pass a placeholder for the srcs + // array. + android.InitPrebuiltModule(m, &[]string{"placeholder"}) + android.InitApexModule(m) + android.InitSdkAwareModule(m) + android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) + return m +} diff --git a/java/systemserver_classpath_fragment_test.go b/java/systemserver_classpath_fragment_test.go index 9ad50dd4a..ba328e7b1 100644 --- a/java/systemserver_classpath_fragment_test.go +++ b/java/systemserver_classpath_fragment_test.go @@ -99,7 +99,7 @@ func TestPlatformSystemServerClasspathModule_AndroidMkEntries(t *testing.T) { func TestSystemServerClasspathFragmentWithoutContents(t *testing.T) { prepareForTestWithSystemServerClasspath. ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern( - `\Qempty contents are not allowed\E`)). + `\QEither contents or standalone_contents needs to be non-empty\E`)). RunTestWithBp(t, ` systemserverclasspath_fragment { name: "systemserverclasspath-fragment", diff --git a/java/testing.go b/java/testing.go index 3ef51bd4d..511cc5ddb 100644 --- a/java/testing.go +++ b/java/testing.go @@ -54,6 +54,8 @@ var PrepareForTestWithJavaBuildComponents = android.GroupFixturePreparers( "build/soong/java/lint_defaults.txt": nil, // Needed for apps that do not provide their own. "build/make/target/product/security": nil, + // Required to generate Java used-by API coverage + "build/soong/scripts/gen_java_usedby_apex.sh": nil, }.AddToFixture(), ) @@ -70,6 +72,10 @@ var PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd = android.GroupFixtu defaultJavaDir + "/framework/aidl": nil, // Needed for various deps defined in GatherRequiredDepsForTest() defaultJavaDir + "/a.java": nil, + + // Needed for R8 rules on apps + "build/make/core/proguard.flags": nil, + "build/make/core/proguard_basic_keeps.flags": nil, }.AddToFixture(), // The java default module definitions. android.FixtureAddTextFile(defaultJavaDir+"/Android.bp", gatherRequiredDepsForTest()), @@ -146,6 +152,10 @@ var PrepareForTestWithPrebuiltsOfCurrentApi = FixtureWithPrebuiltApis(map[string // This defines a file in the mock file system in a predefined location (prebuilts/sdk/Android.bp) // and so only one instance of this can be used in each fixture. func FixtureWithPrebuiltApis(release2Modules map[string][]string) android.FixturePreparer { + return FixtureWithPrebuiltApisAndExtensions(release2Modules, nil) +} + +func FixtureWithPrebuiltApisAndExtensions(apiLevel2Modules map[string][]string, extensionLevel2Modules map[string][]string) android.FixturePreparer { mockFS := android.MockFS{} path := "prebuilts/sdk/Android.bp" @@ -153,14 +163,19 @@ func FixtureWithPrebuiltApis(release2Modules map[string][]string) android.Fixtur prebuilt_apis { name: "sdk", api_dirs: ["%s"], + extensions_dir: "extensions", imports_sdk_version: "none", imports_compile_dex: true, } - `, strings.Join(android.SortedStringKeys(release2Modules), `", "`)) + `, strings.Join(android.SortedStringKeys(apiLevel2Modules), `", "`)) - for release, modules := range release2Modules { - libs := append([]string{"android", "core-for-system-modules"}, modules...) - mockFS.Merge(prebuiltApisFilesForLibs([]string{release}, libs)) + for release, modules := range apiLevel2Modules { + mockFS.Merge(prebuiltApisFilesForModules([]string{release}, modules)) + } + if extensionLevel2Modules != nil { + for release, modules := range extensionLevel2Modules { + mockFS.Merge(prebuiltExtensionApiFiles([]string{release}, modules)) + } } return android.GroupFixturePreparers( android.FixtureAddTextFile(path, bp), @@ -168,24 +183,50 @@ func FixtureWithPrebuiltApis(release2Modules map[string][]string) android.Fixtur ) } -func prebuiltApisFilesForLibs(apiLevels []string, sdkLibs []string) map[string][]byte { +func prebuiltApisFilesForModules(apiLevels []string, modules []string) map[string][]byte { + libs := append([]string{"android"}, modules...) + fs := make(map[string][]byte) for _, level := range apiLevels { - for _, lib := range sdkLibs { - for _, scope := range []string{"public", "system", "module-lib", "system-server", "test"} { - fs[fmt.Sprintf("prebuilts/sdk/%s/%s/%s.jar", level, scope, lib)] = nil + apiLevel := android.ApiLevelForTest(level) + for _, sdkKind := range []android.SdkKind{android.SdkPublic, android.SdkSystem, android.SdkModule, android.SdkSystemServer, android.SdkTest} { + // A core-for-system-modules file must only be created for the sdk kind that supports it. + if sdkKind == systemModuleKind(sdkKind, apiLevel) { + fs[fmt.Sprintf("prebuilts/sdk/%s/%s/core-for-system-modules.jar", level, sdkKind)] = nil + } + + for _, lib := range libs { + // Create a jar file for every library. + fs[fmt.Sprintf("prebuilts/sdk/%s/%s/%s.jar", level, sdkKind, lib)] = nil + // No finalized API files for "current" if level != "current" { - fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s.txt", level, scope, lib)] = nil - fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s-removed.txt", level, scope, lib)] = nil + fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s.txt", level, sdkKind, lib)] = nil + fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s-removed.txt", level, sdkKind, lib)] = nil } } } + if level == "current" { + fs["prebuilts/sdk/current/core/android.jar"] = nil + } fs[fmt.Sprintf("prebuilts/sdk/%s/public/framework.aidl", level)] = nil } return fs } +func prebuiltExtensionApiFiles(extensionLevels []string, modules []string) map[string][]byte { + fs := make(map[string][]byte) + for _, level := range extensionLevels { + for _, sdkKind := range []android.SdkKind{android.SdkPublic, android.SdkSystem, android.SdkModule, android.SdkSystemServer} { + for _, lib := range modules { + fs[fmt.Sprintf("prebuilts/sdk/extensions/%s/%s/api/%s.txt", level, sdkKind, lib)] = nil + fs[fmt.Sprintf("prebuilts/sdk/extensions/%s/%s/api/%s-removed.txt", level, sdkKind, lib)] = nil + } + } + } + return fs +} + // FixtureConfigureBootJars configures the boot jars in both the dexpreopt.GlobalConfig and // Config.productVariables structs. As a side effect that enables dexpreopt. func FixtureConfigureBootJars(bootJars ...string) android.FixturePreparer { @@ -214,21 +255,41 @@ func FixtureConfigureBootJars(bootJars ...string) android.FixturePreparer { ) } -// FixtureConfigureUpdatableBootJars configures the updatable boot jars in both the +// FixtureConfigureApexBootJars configures the apex boot jars in both the // dexpreopt.GlobalConfig and Config.productVariables structs. As a side effect that enables // dexpreopt. -func FixtureConfigureUpdatableBootJars(bootJars ...string) android.FixturePreparer { +func FixtureConfigureApexBootJars(bootJars ...string) android.FixturePreparer { return android.GroupFixturePreparers( android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.UpdatableBootJars = android.CreateTestConfiguredJarList(bootJars) + variables.ApexBootJars = android.CreateTestConfiguredJarList(bootJars) }), - dexpreopt.FixtureSetUpdatableBootJars(bootJars...), + dexpreopt.FixtureSetApexBootJars(bootJars...), // Add a fake dex2oatd module. dexpreopt.PrepareForTestWithFakeDex2oatd, ) } +// FixtureUseLegacyCorePlatformApi prepares the fixture by setting the exception list of those +// modules that are allowed to use the legacy core platform API to be the ones supplied. +func FixtureUseLegacyCorePlatformApi(moduleNames ...string) android.FixturePreparer { + lookup := make(map[string]struct{}) + for _, moduleName := range moduleNames { + lookup[moduleName] = struct{}{} + } + return android.FixtureModifyConfig(func(config android.Config) { + // Try and set the legacyCorePlatformApiLookup in the config, the returned value will be the + // actual value that is set. + cached := config.Once(legacyCorePlatformApiLookupKey, func() interface{} { + return lookup + }) + // Make sure that the cached value is the one we need. + if !reflect.DeepEqual(cached, lookup) { + panic(fmt.Errorf("attempting to set legacyCorePlatformApiLookupKey to %q but it has already been set to %q", lookup, cached)) + } + }) +} + // registerRequiredBuildComponentsForTest registers the build components used by // PrepareForTestWithJavaDefaultModules. // @@ -280,6 +341,7 @@ func gatherRequiredDepsForTest() string { "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8", "kotlin-annotations", + "stub-annotations", } for _, extra := range extraModules { @@ -311,7 +373,7 @@ func gatherRequiredDepsForTest() string { }` systemModules := []string{ - "core-current-stubs-system-modules", + "core-public-stubs-system-modules", "core-module-lib-stubs-system-modules", "legacy-core-platform-api-stubs-system-modules", "stable-core-platform-api-stubs-system-modules", @@ -364,6 +426,17 @@ func CheckPlatformBootclasspathModules(t *testing.T, result *android.TestResult, android.AssertDeepEquals(t, fmt.Sprintf("%s modules", "platform-bootclasspath"), expected, pairs) } +func CheckClasspathFragmentProtoContentInfoProvider(t *testing.T, result *android.TestResult, generated bool, contents, outputFilename, installDir string) { + t.Helper() + p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule) + info := result.ModuleProvider(p, ClasspathFragmentProtoContentInfoProvider).(ClasspathFragmentProtoContentInfo) + + android.AssertBoolEquals(t, "classpath proto generated", generated, info.ClasspathFragmentProtoGenerated) + android.AssertStringEquals(t, "classpath proto contents", contents, info.ClasspathFragmentProtoContents.String()) + android.AssertStringEquals(t, "output filepath", outputFilename, info.ClasspathFragmentProtoOutput.Base()) + android.AssertPathRelativeToTopEquals(t, "install filepath", installDir, info.ClasspathFragmentProtoInstallDir) +} + // ApexNamePairsFromModules returns the apex:module pair for the supplied modules. func ApexNamePairsFromModules(ctx *android.TestContext, modules []android.Module) []string { pairs := []string{} @@ -420,3 +493,61 @@ func CheckMergedCompatConfigInputs(t *testing.T, result *android.TestResult, mes output := sourceGlobalCompatConfig.Output(allOutputs[0]) android.AssertPathsRelativeToTopEquals(t, message+": inputs", expectedPaths, output.Implicits) } + +// Register the fake APEX mutator to `android.InitRegistrationContext` as if the real mutator exists +// at runtime. This must be called in `init()` of a test if the test is going to use the fake APEX +// mutator. Otherwise, we will be missing the runtime mutator because "soong-apex" is not a +// dependency, which will cause an inconsistency between testing and runtime mutators. +func RegisterFakeRuntimeApexMutator() { + registerFakeApexMutator(android.InitRegistrationContext) +} + +var PrepareForTestWithFakeApexMutator = android.GroupFixturePreparers( + android.FixtureRegisterWithContext(registerFakeApexMutator), +) + +func registerFakeApexMutator(ctx android.RegistrationContext) { + ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { + ctx.BottomUp("apex", fakeApexMutator).Parallel() + }) +} + +type apexModuleBase interface { + ApexAvailable() []string +} + +var _ apexModuleBase = (*Library)(nil) +var _ apexModuleBase = (*SdkLibrary)(nil) + +// A fake APEX mutator that creates a platform variant and an APEX variant for modules with +// `apex_available`. It helps us avoid a dependency on the real mutator defined in "soong-apex", +// which will cause a cyclic dependency, and it provides an easy way to create an APEX variant for +// testing without dealing with all the complexities in the real mutator. +func fakeApexMutator(mctx android.BottomUpMutatorContext) { + switch mctx.Module().(type) { + case *Library, *SdkLibrary: + if len(mctx.Module().(apexModuleBase).ApexAvailable()) > 0 { + modules := mctx.CreateVariations("", "apex1000") + apexInfo := android.ApexInfo{ + ApexVariationName: "apex1000", + } + mctx.SetVariationProvider(modules[1], android.ApexInfoProvider, apexInfo) + } + } +} + +// Applies the given modifier on the boot image config with the given name. +func FixtureModifyBootImageConfig(name string, configModifier func(*bootImageConfig)) android.FixturePreparer { + return android.FixtureModifyConfig(func(androidConfig android.Config) { + pathCtx := android.PathContextForTesting(androidConfig) + config := genBootImageConfigRaw(pathCtx) + configModifier(config[name]) + }) +} + +// Sets the value of `installDirOnDevice` of the boot image config with the given name. +func FixtureSetBootImageInstallDirOnDevice(name string, installDir string) android.FixturePreparer { + return FixtureModifyBootImageConfig(name, func(config *bootImageConfig) { + config.installDirOnDevice = installDir + }) +} |