diff options
Diffstat (limited to 'java')
94 files changed, 12612 insertions, 4781 deletions
diff --git a/java/Android.bp b/java/Android.bp index 4af2a14eb..a941754db 100644 --- a/java/Android.bp +++ b/java/Android.bp @@ -9,12 +9,13 @@ bootstrap_go_package { "blueprint", "blueprint-pathtools", "soong", + "soong-aconfig", "soong-android", - "soong-bazel", "soong-cc", "soong-dexpreopt", "soong-genrule", "soong-java-config", + "soong-testing", "soong-provenance", "soong-python", "soong-remoteexec", @@ -48,6 +49,7 @@ bootstrap_go_package { "droidstubs.go", "fuzz.go", "gen.go", + "generated_java_library.go", "genrule.go", "hiddenapi.go", "hiddenapi_modular.go", @@ -65,7 +67,7 @@ bootstrap_go_package { "plugin.go", "prebuilt_apis.go", "proto.go", - "resourceshrinker.go", + "ravenwood.go", "robolectric.go", "rro.go", "sdk.go", @@ -79,10 +81,13 @@ bootstrap_go_package { ], testSrcs: [ "aar_test.go", + "android_manifest_test.go", "androidmk_test.go", "app_import_test.go", "app_set_test.go", "app_test.go", + "code_metadata_test.go", + "container_test.go", "bootclasspath_fragment_test.go", "device_host_converter_test.go", "dex_test.go", @@ -92,6 +97,7 @@ bootstrap_go_package { "droidstubs_test.go", "fuzz_test.go", "genrule_test.go", + "generated_java_library_test.go", "hiddenapi_singleton_test.go", "jacoco_test.go", "java_test.go", @@ -103,12 +109,14 @@ bootstrap_go_package { "plugin_test.go", "prebuilt_apis_test.go", "proto_test.go", - "resourceshrinker_test.go", + "ravenwood_test.go", "rro_test.go", - "sdk_test.go", "sdk_library_test.go", + "sdk_test.go", + "sdk_version_test.go", "system_modules_test.go", "systemserver_classpath_fragment_test.go", + "test_spec_test.go", ], pluginFor: ["soong_build"], } diff --git a/java/aapt2.go b/java/aapt2.go index 7845a0b23..f704fc6fc 100644 --- a/java/aapt2.go +++ b/java/aapt2.go @@ -25,17 +25,23 @@ import ( "android/soong/android" ) +func isPathValueResource(res android.Path) bool { + subDir := filepath.Dir(res.String()) + subDir, lastDir := filepath.Split(subDir) + return strings.HasPrefix(lastDir, "values") +} + // Convert input resource file path to output file path. // values-[config]/<file>.xml -> values-[config]_<file>.arsc.flat; // For other resource file, just replace the last "/" with "_" and add .flat extension. func pathToAapt2Path(ctx android.ModuleContext, res android.Path) android.WritablePath { name := res.Base() - subDir := filepath.Dir(res.String()) - subDir, lastDir := filepath.Split(subDir) - if strings.HasPrefix(lastDir, "values") { + if isPathValueResource(res) { name = strings.TrimSuffix(name, ".xml") + ".arsc" } + subDir := filepath.Dir(res.String()) + subDir, lastDir := filepath.Split(subDir) name = lastDir + "_" + name + ".flat" return android.PathForModuleOut(ctx, "aapt2", subDir, name) } @@ -63,7 +69,21 @@ var aapt2CompileRule = pctx.AndroidStaticRule("aapt2Compile", // aapt2Compile compiles resources and puts the results in the requested directory. func aapt2Compile(ctx android.ModuleContext, dir android.Path, paths android.Paths, - flags []string) android.WritablePaths { + flags []string, productToFilter string) android.WritablePaths { + if productToFilter != "" && productToFilter != "default" { + // --filter-product leaves only product-specific resources. Product-specific resources only exist + // in value resources (values/*.xml), so filter value resource files only. Ignore other types of + // resources as they don't need to be in product characteristics RRO (and they will cause aapt2 + // compile errors) + filteredPaths := android.Paths{} + for _, path := range paths { + if isPathValueResource(path) { + filteredPaths = append(filteredPaths, path) + } + } + paths = filteredPaths + flags = append([]string{"--filter-product " + productToFilter}, flags...) + } // Shard the input paths so that they can be processed in parallel. If we shard them into too // small chunks, the additional cost of spinning up aapt2 outweighs the performance gain. The @@ -146,20 +166,25 @@ func aapt2CompileZip(ctx android.ModuleContext, flata android.WritablePath, zip var aapt2LinkRule = pctx.AndroidStaticRule("aapt2Link", blueprint.RuleParams{ - Command: `rm -rf $genDir && ` + - `${config.Aapt2Cmd} link -o $out $flags --java $genDir --proguard $proguardOptions ` + - `--output-text-symbols ${rTxt} $inFlags && ` + - `${config.SoongZipCmd} -write_if_changed -jar -o $genJar -C $genDir -D $genDir &&` + - `${config.ExtractJarPackagesCmd} -i $genJar -o $extraPackages --prefix '--extra-packages '`, + Command: `$preamble` + + `${config.Aapt2Cmd} link -o $out $flags --proguard $proguardOptions ` + + `--output-text-symbols ${rTxt} $inFlags` + + `$postamble`, CommandDeps: []string{ "${config.Aapt2Cmd}", "${config.SoongZipCmd}", - "${config.ExtractJarPackagesCmd}", }, Restat: true, }, - "flags", "inFlags", "proguardOptions", "genDir", "genJar", "rTxt", "extraPackages") + "flags", "inFlags", "proguardOptions", "rTxt", "extraPackages", "preamble", "postamble") + +var aapt2ExtractExtraPackagesRule = pctx.AndroidStaticRule("aapt2ExtractExtraPackages", + blueprint.RuleParams{ + Command: `${config.ExtractJarPackagesCmd} -i $in -o $out --prefix '--extra-packages '`, + CommandDeps: []string{"${config.ExtractJarPackagesCmd}"}, + Restat: true, + }) var fileListToFileRule = pctx.AndroidStaticRule("fileListToFile", blueprint.RuleParams{ @@ -175,11 +200,10 @@ var mergeAssetsRule = pctx.AndroidStaticRule("mergeAssets", }) func aapt2Link(ctx android.ModuleContext, - packageRes, genJar, proguardOptions, rTxt, extraPackages android.WritablePath, + packageRes, genJar, proguardOptions, rTxt android.WritablePath, flags []string, deps android.Paths, - compiledRes, compiledOverlay, assetPackages android.Paths, splitPackages android.WritablePaths) { - - genDir := android.PathForModuleGen(ctx, "aapt2", "R") + compiledRes, compiledOverlay, assetPackages android.Paths, splitPackages android.WritablePaths, + featureFlagsPaths android.Paths) { var inFlags []string @@ -217,7 +241,7 @@ func aapt2Link(ctx android.ModuleContext, } // Set auxiliary outputs as implicit outputs to establish correct dependency chains. - implicitOutputs := append(splitPackages, proguardOptions, genJar, rTxt, extraPackages) + implicitOutputs := append(splitPackages, proguardOptions, rTxt) linkOutput := packageRes // AAPT2 ignores assets in overlays. Merge them after linking. @@ -232,31 +256,61 @@ func aapt2Link(ctx android.ModuleContext, }) } + for _, featureFlagsPath := range featureFlagsPaths { + deps = append(deps, featureFlagsPath) + inFlags = append(inFlags, "--feature-flags", "@"+featureFlagsPath.String()) + } + + // Note the absence of splitPackages. The caller is supposed to compose and provide --split flag + // values via the flags parameter when it wants to split outputs. + // TODO(b/174509108): Perhaps we can process it in this func while keeping the code reasonably + // tidy. + args := map[string]string{ + "flags": strings.Join(flags, " "), + "inFlags": strings.Join(inFlags, " "), + "proguardOptions": proguardOptions.String(), + "rTxt": rTxt.String(), + } + + if genJar != nil { + // Generating java source files from aapt2 was requested, use aapt2LinkAndGenRule and pass it + // genJar and genDir args. + genDir := android.PathForModuleGen(ctx, "aapt2", "R") + ctx.Variable(pctx, "aapt2GenDir", genDir.String()) + ctx.Variable(pctx, "aapt2GenJar", genJar.String()) + implicitOutputs = append(implicitOutputs, genJar) + args["preamble"] = `rm -rf $aapt2GenDir && ` + args["postamble"] = `&& ${config.SoongZipCmd} -write_if_changed -jar -o $aapt2GenJar -C $aapt2GenDir -D $aapt2GenDir && ` + + `rm -rf $aapt2GenDir` + args["flags"] += " --java $aapt2GenDir" + } + ctx.Build(pctx, android.BuildParams{ Rule: aapt2LinkRule, Description: "aapt2 link", Implicits: deps, Output: linkOutput, ImplicitOutputs: implicitOutputs, - // Note the absence of splitPackages. The caller is supposed to compose and provide --split flag - // values via the flags parameter when it wants to split outputs. - // TODO(b/174509108): Perhaps we can process it in this func while keeping the code reasonably - // tidy. - Args: map[string]string{ - "flags": strings.Join(flags, " "), - "inFlags": strings.Join(inFlags, " "), - "proguardOptions": proguardOptions.String(), - "genDir": genDir.String(), - "genJar": genJar.String(), - "rTxt": rTxt.String(), - "extraPackages": extraPackages.String(), - }, + Args: args, + }) +} + +// aapt2ExtractExtraPackages takes a srcjar generated by aapt2 or a classes jar generated by ResourceProcessorBusyBox +// and converts it to a text file containing a list of --extra_package arguments for passing to Make modules so they +// correctly generate R.java entries for packages provided by transitive dependencies. +func aapt2ExtractExtraPackages(ctx android.ModuleContext, out android.WritablePath, in android.Path) { + ctx.Build(pctx, android.BuildParams{ + Rule: aapt2ExtractExtraPackagesRule, + Description: "aapt2 extract extra packages", + Input: in, + Output: out, }) } var aapt2ConvertRule = pctx.AndroidStaticRule("aapt2Convert", blueprint.RuleParams{ - Command: `${config.Aapt2Cmd} convert --output-format $format $in -o $out`, + Command: `${config.Aapt2Cmd} convert --enable-compact-entries ` + + `--output-format $format $in -o $out`, CommandDeps: []string{"${config.Aapt2Cmd}"}, }, "format", ) diff --git a/java/aar.go b/java/aar.go index f1b137de1..2f49a959d 100644 --- a/java/aar.go +++ b/java/aar.go @@ -17,11 +17,11 @@ package java import ( "fmt" "path/filepath" + "slices" "strconv" "strings" "android/soong/android" - "android/soong/bazel" "android/soong/dexpreopt" "github.com/google/blueprint" @@ -29,12 +29,10 @@ import ( ) type AndroidLibraryDependency interface { - LibraryDependency ExportPackage() android.Path - ExportedRRODirs() []rroDir - ExportedStaticPackages() android.Paths - ExportedManifests() android.Paths - ExportedAssets() android.OptionalPath + ResourcesNodeDepSet() *android.DepSet[*resourcesNode] + RRODirsDepSet() *android.DepSet[rroDir] + ManifestsDepSet() *android.DepSet[android.Path] SetRROEnforcedForDependent(enforce bool) IsRROEnforced(ctx android.BaseModuleContext) bool } @@ -47,7 +45,7 @@ func RegisterAARBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("android_library_import", AARImportFactory) ctx.RegisterModuleType("android_library", AndroidLibraryFactory) ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { - ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator).Parallel() + ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator) }) } @@ -67,6 +65,9 @@ type aaptProperties struct { // ones. Aapt_include_all_resources *bool + // list of files to use as assets. + Assets []string `android:"path"` + // list of directories relative to the Blueprints file containing assets. // Defaults to ["assets"] if a directory called assets exists. Set to [] // to disable the default. @@ -89,35 +90,59 @@ type aaptProperties struct { // do not include AndroidManifest from dependent libraries Dont_merge_manifests *bool + // If use_resource_processor is set, use Bazel's resource processor instead of aapt2 to generate R.class files. + // The resource processor produces more optimal R.class files that only list resources in the package of the + // library that provided them, as opposed to aapt2 which produces R.java files for every package containing + // every resource. Using the resource processor can provide significant build time speedups, but requires + // fixing the module to use the correct package to reference each resource, and to avoid having any other + // libraries in the tree that use the same package name. Defaults to false, but will default to true in the + // future. + Use_resource_processor *bool + // true if RRO is enforced for any of the dependent modules RROEnforcedForDependent bool `blueprint:"mutated"` + + // Filter only specified product and ignore other products + Filter_product *string `blueprint:"mutated"` + + // Names of aconfig_declarations modules that specify aconfig flags that the module depends on. + Flags_packages []string } type aapt struct { - aaptSrcJar android.Path - exportPackage android.Path - manifestPath android.Path - transitiveManifestPaths android.Paths - proguardOptionsFile android.Path - rroDirs []rroDir - rTxt android.Path - extraAaptPackagesFile android.Path - mergedManifestFile android.Path - noticeFile android.OptionalPath - assetPackage android.OptionalPath - isLibrary bool - defaultManifestVersion string - useEmbeddedNativeLibs bool - useEmbeddedDex bool - usesNonSdkApis bool - hasNoCode bool - LoggingParent string - resourceFiles android.Paths + aaptSrcJar android.Path + transitiveAaptRJars android.Paths + transitiveAaptResourcePackagesFile android.Path + exportPackage android.Path + manifestPath android.Path + proguardOptionsFile android.Path + rTxt android.Path + rJar android.Path + extraAaptPackagesFile android.Path + mergedManifestFile android.Path + noticeFile android.OptionalPath + assetPackage android.OptionalPath + isLibrary bool + defaultManifestVersion string + useEmbeddedNativeLibs bool + useEmbeddedDex bool + usesNonSdkApis bool + hasNoCode bool + LoggingParent string + resourceFiles android.Paths splitNames []string splits []split aaptProperties aaptProperties + + resourcesNodesDepSet *android.DepSet[*resourcesNode] + rroDirsDepSet *android.DepSet[rroDir] + manifestsDepSet *android.DepSet[android.Path] + + manifestValues struct { + applicationId string + } } type split struct { @@ -138,20 +163,29 @@ func propagateRROEnforcementMutator(ctx android.TopDownMutatorContext) { } } +func (a *aapt) useResourceProcessorBusyBox(ctx android.BaseModuleContext) bool { + return BoolDefault(a.aaptProperties.Use_resource_processor, ctx.Config().UseResourceProcessorByDefault()) && + // TODO(b/331641946): remove this when ResourceProcessorBusyBox supports generating shared libraries. + !slices.Contains(a.aaptProperties.Aaptflags, "--shared-lib") +} + +func (a *aapt) filterProduct() string { + return String(a.aaptProperties.Filter_product) +} + func (a *aapt) ExportPackage() android.Path { return a.exportPackage } - -func (a *aapt) ExportedRRODirs() []rroDir { - return a.rroDirs +func (a *aapt) ResourcesNodeDepSet() *android.DepSet[*resourcesNode] { + return a.resourcesNodesDepSet } -func (a *aapt) ExportedManifests() android.Paths { - return a.transitiveManifestPaths +func (a *aapt) RRODirsDepSet() *android.DepSet[rroDir] { + return a.rroDirsDepSet } -func (a *aapt) ExportedAssets() android.OptionalPath { - return a.assetPackage +func (a *aapt) ManifestsDepSet() *android.DepSet[android.Path] { + return a.manifestsDepSet } func (a *aapt) SetRROEnforcedForDependent(enforce bool) { @@ -175,9 +209,14 @@ func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext android.SdkConte // Flags specified in Android.bp linkFlags = append(linkFlags, a.aaptProperties.Aaptflags...) - linkFlags = append(linkFlags, "--no-static-lib-packages") + linkFlags = append(linkFlags, "--enable-compact-entries") // Find implicit or explicit asset and resource dirs + assets := android.PathsRelativeToModuleSourceDir(android.SourceInput{ + Context: ctx, + Paths: a.aaptProperties.Assets, + IncludeDirs: false, + }) assetDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Asset_dirs, "assets") resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res") resourceZips := android.PathsForModuleSrc(ctx, a.aaptProperties.Resource_zips) @@ -212,6 +251,28 @@ func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext android.SdkConte assetDirStrings = append(assetDirStrings, filepath.Dir(a.noticeFile.Path().String())) assetDeps = append(assetDeps, a.noticeFile.Path()) } + if len(assets) > 0 { + // aapt2 doesn't support adding individual asset files. Create a temp directory to hold asset + // files and pass it to aapt2. + tmpAssetDir := android.PathForModuleOut(ctx, "tmp_asset_dir") + + rule := android.NewRuleBuilder(pctx, ctx) + rule.Command(). + Text("rm -rf").Text(tmpAssetDir.String()). + Text("&&"). + Text("mkdir -p").Text(tmpAssetDir.String()) + + for _, asset := range assets { + output := tmpAssetDir.Join(ctx, asset.Rel()) + assetDeps = append(assetDeps, output) + rule.Command().Text("mkdir -p").Text(filepath.Dir(output.String())) + rule.Command().Text("cp").Input(asset).Output(output) + } + + rule.Build("tmp_asset_dir", "tmp_asset_dir") + + assetDirStrings = append(assetDirStrings, tmpAssetDir.String()) + } linkFlags = append(linkFlags, "--manifest "+manifestPath.String()) linkDeps = append(linkDeps, manifestPath) @@ -287,23 +348,32 @@ var extractAssetsRule = pctx.AndroidStaticRule("extractAssets", CommandDeps: []string{"${config.Zip2ZipCmd}"}, }) -func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkContext, - classLoaderContexts dexpreopt.ClassLoaderContextMap, excludedLibs []string, - enforceDefaultTargetSdkVersion bool, extraLinkFlags ...string) { +type aaptBuildActionOptions struct { + sdkContext android.SdkContext + classLoaderContexts dexpreopt.ClassLoaderContextMap + excludedLibs []string + enforceDefaultTargetSdkVersion bool + forceNonFinalResourceIDs bool + extraLinkFlags []string + aconfigTextFiles android.Paths + usesLibrary *usesLibrary +} + +func (a *aapt) buildActions(ctx android.ModuleContext, opts aaptBuildActionOptions) { - transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assetPackages, libDeps, libFlags := - aaptLibs(ctx, sdkContext, classLoaderContexts) + staticResourcesNodesDepSet, sharedResourcesNodesDepSet, staticRRODirsDepSet, staticManifestsDepSet, sharedExportPackages, libFlags := + aaptLibs(ctx, opts.sdkContext, opts.classLoaderContexts, opts.usesLibrary) // Exclude any libraries from the supplied list. - classLoaderContexts = classLoaderContexts.ExcludeLibs(excludedLibs) + opts.classLoaderContexts = opts.classLoaderContexts.ExcludeLibs(opts.excludedLibs) // App manifest file manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml") manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile) manifestPath := ManifestFixer(ctx, manifestSrcPath, ManifestFixerParams{ - SdkContext: sdkContext, - ClassLoaderContexts: classLoaderContexts, + SdkContext: opts.sdkContext, + ClassLoaderContexts: opts.classLoaderContexts, IsLibrary: a.isLibrary, DefaultManifestVersion: a.defaultManifestVersion, UseEmbeddedNativeLibs: a.useEmbeddedNativeLibs, @@ -311,16 +381,24 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkCon UseEmbeddedDex: a.useEmbeddedDex, HasNoCode: a.hasNoCode, LoggingParent: a.LoggingParent, - EnforceDefaultTargetSdkVersion: enforceDefaultTargetSdkVersion, + EnforceDefaultTargetSdkVersion: opts.enforceDefaultTargetSdkVersion, }) + staticDeps := transitiveAarDeps(staticResourcesNodesDepSet.ToList()) + sharedDeps := transitiveAarDeps(sharedResourcesNodesDepSet.ToList()) + // Add additional manifest files to transitive manifests. additionalManifests := android.PathsForModuleSrc(ctx, a.aaptProperties.Additional_manifests) - a.transitiveManifestPaths = append(android.Paths{manifestPath}, additionalManifests...) - a.transitiveManifestPaths = append(a.transitiveManifestPaths, transitiveStaticLibManifests...) - - if len(a.transitiveManifestPaths) > 1 && !Bool(a.aaptProperties.Dont_merge_manifests) { - a.mergedManifestFile = manifestMerger(ctx, a.transitiveManifestPaths[0], a.transitiveManifestPaths[1:], a.isLibrary) + transitiveManifestPaths := append(android.Paths{manifestPath}, additionalManifests...) + transitiveManifestPaths = append(transitiveManifestPaths, staticManifestsDepSet.ToList()...) + + if len(transitiveManifestPaths) > 1 && !Bool(a.aaptProperties.Dont_merge_manifests) { + manifestMergerParams := ManifestMergerParams{ + staticLibManifests: transitiveManifestPaths[1:], + isLibrary: a.isLibrary, + packageName: a.manifestValues.applicationId, + } + a.mergedManifestFile = manifestMerger(ctx, transitiveManifestPaths[0], manifestMergerParams) if !a.isLibrary { // Only use the merged manifest for applications. For libraries, the transitive closure of manifests // will be propagated to the final application and merged there. The merged manifest for libraries is @@ -331,28 +409,38 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkCon a.mergedManifestFile = manifestPath } - compileFlags, linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resZips := a.aapt2Flags(ctx, sdkContext, manifestPath) + compileFlags, linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resZips := a.aapt2Flags(ctx, opts.sdkContext, manifestPath) - rroDirs = append(rroDirs, staticRRODirs...) linkFlags = append(linkFlags, libFlags...) - linkDeps = append(linkDeps, libDeps...) - linkFlags = append(linkFlags, extraLinkFlags...) + linkDeps = append(linkDeps, sharedExportPackages...) + linkDeps = append(linkDeps, staticDeps.resPackages()...) + linkFlags = append(linkFlags, opts.extraLinkFlags...) if a.isLibrary { linkFlags = append(linkFlags, "--static-lib") } + if opts.forceNonFinalResourceIDs { + linkFlags = append(linkFlags, "--non-final-ids") + } + + linkFlags = append(linkFlags, "--no-static-lib-packages") + if a.isLibrary && a.useResourceProcessorBusyBox(ctx) { + // When building an android_library using ResourceProcessorBusyBox pass --merge-only to skip resource + // references validation until the final app link step when all static libraries are present. + linkFlags = append(linkFlags, "--merge-only") + } packageRes := android.PathForModuleOut(ctx, "package-res.apk") - // the subdir "android" is required to be filtered by package names - srcJar := android.PathForModuleGen(ctx, "android", "R.srcjar") proguardOptionsFile := android.PathForModuleGen(ctx, "proguard.options") rTxt := android.PathForModuleOut(ctx, "R.txt") // This file isn't used by Soong, but is generated for exporting extraPackages := android.PathForModuleOut(ctx, "extra_packages") + var transitiveRJars android.Paths + var srcJar android.WritablePath var compiledResDirs []android.Paths for _, dir := range resDirs { a.resourceFiles = append(a.resourceFiles, dir.files...) - compiledResDirs = append(compiledResDirs, aapt2Compile(ctx, dir.dir, dir.files, compileFlags).Paths()) + compiledResDirs = append(compiledResDirs, aapt2Compile(ctx, dir.dir, dir.files, compileFlags, a.filterProduct()).Paths()) } for i, zip := range resZips { @@ -363,7 +451,32 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkCon var compiledRes, compiledOverlay android.Paths - compiledOverlay = append(compiledOverlay, transitiveStaticLibs...) + // AAPT2 overlays are in lowest to highest priority order, reverse the topological order + // of transitiveStaticLibs. + transitiveStaticLibs := android.ReversePaths(staticDeps.resPackages()) + + if a.isLibrary && a.useResourceProcessorBusyBox(ctx) { + // When building an android_library with ResourceProcessorBusyBox enabled treat static library dependencies + // as imports. The resources from dependencies will not be merged into this module's package-res.apk, and + // instead modules depending on this module will reference package-res.apk from all transitive static + // dependencies. + for _, sharedDep := range sharedDeps { + if sharedDep.usedResourceProcessor { + transitiveRJars = append(transitiveRJars, sharedDep.rJar) + } + } + for _, staticDep := range staticDeps { + linkDeps = append(linkDeps, staticDep.resPackage) + linkFlags = append(linkFlags, "-I "+staticDep.resPackage.String()) + if staticDep.usedResourceProcessor { + transitiveRJars = append(transitiveRJars, staticDep.rJar) + } + } + } else { + // When building an app or building a library without ResourceProcessorBusyBox enabled all static + // dependencies are compiled into this module's package-res.apk as overlays. + compiledOverlay = append(compiledOverlay, transitiveStaticLibs...) + } if len(transitiveStaticLibs) > 0 { // If we are using static android libraries, every source file becomes an overlay. @@ -386,7 +499,7 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkCon } for _, dir := range overlayDirs { - compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files, compileFlags).Paths()...) + compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files, compileFlags, a.filterProduct()).Paths()...) } var splitPackages android.WritablePaths @@ -404,12 +517,23 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkCon }) } - aapt2Link(ctx, packageRes, srcJar, proguardOptionsFile, rTxt, extraPackages, - linkFlags, linkDeps, compiledRes, compiledOverlay, assetPackages, splitPackages) + if !a.useResourceProcessorBusyBox(ctx) { + // the subdir "android" is required to be filtered by package names + srcJar = android.PathForModuleGen(ctx, "android", "R.srcjar") + } + // No need to specify assets from dependencies to aapt2Link for libraries, all transitive assets will be + // provided to the final app aapt2Link step. + var transitiveAssets android.Paths + if !a.isLibrary { + transitiveAssets = android.ReverseSliceInPlace(staticDeps.assets()) + } + aapt2Link(ctx, packageRes, srcJar, proguardOptionsFile, rTxt, + linkFlags, linkDeps, compiledRes, compiledOverlay, transitiveAssets, splitPackages, + opts.aconfigTextFiles) // Extract assets from the resource package output so that they can be used later in aapt2link // for modules that depend on this one. - if android.PrefixInList(linkFlags, "-A ") || len(assetPackages) > 0 { + if android.PrefixInList(linkFlags, "-A ") { assets := android.PathForModuleOut(ctx, "assets.zip") ctx.Build(pctx, android.BuildParams{ Rule: extractAssetsRule, @@ -420,21 +544,173 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkCon a.assetPackage = android.OptionalPathForPath(assets) } + if a.useResourceProcessorBusyBox(ctx) { + rJar := android.PathForModuleOut(ctx, "busybox/R.jar") + resourceProcessorBusyBoxGenerateBinaryR(ctx, rTxt, a.mergedManifestFile, rJar, staticDeps, a.isLibrary, a.aaptProperties.Aaptflags, + opts.forceNonFinalResourceIDs) + aapt2ExtractExtraPackages(ctx, extraPackages, rJar) + transitiveRJars = append(transitiveRJars, rJar) + a.rJar = rJar + } else { + aapt2ExtractExtraPackages(ctx, extraPackages, srcJar) + } + + transitiveAaptResourcePackages := staticDeps.resPackages().Strings() + transitiveAaptResourcePackages = slices.DeleteFunc(transitiveAaptResourcePackages, func(p string) bool { + return p == packageRes.String() + }) + transitiveAaptResourcePackagesFile := android.PathForModuleOut(ctx, "transitive-res-packages") + android.WriteFileRule(ctx, transitiveAaptResourcePackagesFile, strings.Join(transitiveAaptResourcePackages, "\n")) + + // Reverse the list of R.jar files so that the current module comes first, and direct dependencies come before + // transitive dependencies. + transitiveRJars = android.ReversePaths(transitiveRJars) + a.aaptSrcJar = srcJar + a.transitiveAaptRJars = transitiveRJars + a.transitiveAaptResourcePackagesFile = transitiveAaptResourcePackagesFile a.exportPackage = packageRes a.manifestPath = manifestPath a.proguardOptionsFile = proguardOptionsFile - a.rroDirs = rroDirs a.extraAaptPackagesFile = extraPackages a.rTxt = rTxt a.splits = splits + a.resourcesNodesDepSet = android.NewDepSetBuilder[*resourcesNode](android.TOPOLOGICAL). + Direct(&resourcesNode{ + resPackage: a.exportPackage, + manifest: a.manifestPath, + additionalManifests: additionalManifests, + rTxt: a.rTxt, + rJar: a.rJar, + assets: a.assetPackage, + + usedResourceProcessor: a.useResourceProcessorBusyBox(ctx), + }). + Transitive(staticResourcesNodesDepSet).Build() + a.rroDirsDepSet = android.NewDepSetBuilder[rroDir](android.TOPOLOGICAL). + Direct(rroDirs...). + Transitive(staticRRODirsDepSet).Build() + a.manifestsDepSet = android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL). + Direct(a.manifestPath). + DirectSlice(additionalManifests). + Transitive(staticManifestsDepSet).Build() } -// aaptLibs collects libraries from dependencies and sdk_version and converts them into paths -func aaptLibs(ctx android.ModuleContext, sdkContext android.SdkContext, classLoaderContexts dexpreopt.ClassLoaderContextMap) ( - transitiveStaticLibs, transitiveStaticLibManifests android.Paths, staticRRODirs []rroDir, assets, deps android.Paths, flags []string) { +var resourceProcessorBusyBox = pctx.AndroidStaticRule("resourceProcessorBusyBox", + blueprint.RuleParams{ + Command: "${config.JavaCmd} -cp ${config.ResourceProcessorBusyBox} " + + "com.google.devtools.build.android.ResourceProcessorBusyBox --tool=GENERATE_BINARY_R -- @${out}.args && " + + "if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out}; fi", + CommandDeps: []string{"${config.ResourceProcessorBusyBox}"}, + Rspfile: "${out}.args", + RspfileContent: "--primaryRTxt ${rTxt} --primaryManifest ${manifest} --classJarOutput ${out}.tmp ${args}", + Restat: true, + }, "rTxt", "manifest", "args") + +// resourceProcessorBusyBoxGenerateBinaryR converts the R.txt file produced by aapt2 into R.class files +// using Bazel's ResourceProcessorBusyBox tool, which is faster than compiling the R.java files and +// supports producing classes for static dependencies that only include resources from that dependency. +func resourceProcessorBusyBoxGenerateBinaryR(ctx android.ModuleContext, rTxt, manifest android.Path, + rJar android.WritablePath, transitiveDeps transitiveAarDeps, isLibrary bool, aaptFlags []string, + forceNonFinalIds bool) { + + var args []string + var deps android.Paths + + if !isLibrary { + // When compiling an app, pass all R.txt and AndroidManifest.xml from transitive static library dependencies + // to ResourceProcessorBusyBox so that it can regenerate R.class files with the final resource IDs for each + // package. + args, deps = transitiveDeps.resourceProcessorDeps() + if forceNonFinalIds { + args = append(args, "--finalFields=false") + } + } else { + // When compiling a library don't pass any dependencies as it only needs to generate an R.class file for this + // library. Pass --finalFields=false so that the R.class file contains non-final fields so they don't get + // inlined into the library before the final IDs are assigned during app compilation. + args = append(args, "--finalFields=false") + } + + for i, arg := range aaptFlags { + const AAPT_CUSTOM_PACKAGE = "--custom-package" + if strings.HasPrefix(arg, AAPT_CUSTOM_PACKAGE) { + pkg := strings.TrimSpace(strings.TrimPrefix(arg, AAPT_CUSTOM_PACKAGE)) + if pkg == "" && i+1 < len(aaptFlags) { + pkg = aaptFlags[i+1] + } + args = append(args, "--packageForR "+pkg) + } + } + + deps = append(deps, rTxt, manifest) + + ctx.Build(pctx, android.BuildParams{ + Rule: resourceProcessorBusyBox, + Output: rJar, + Implicits: deps, + Description: "ResourceProcessorBusyBox", + Args: map[string]string{ + "rTxt": rTxt.String(), + "manifest": manifest.String(), + "args": strings.Join(args, " "), + }, + }) +} + +type resourcesNode struct { + resPackage android.Path + manifest android.Path + additionalManifests android.Paths + rTxt android.Path + rJar android.Path + assets android.OptionalPath + + usedResourceProcessor bool +} + +type transitiveAarDeps []*resourcesNode + +func (t transitiveAarDeps) resPackages() android.Paths { + paths := make(android.Paths, 0, len(t)) + for _, dep := range t { + paths = append(paths, dep.resPackage) + } + return paths +} + +func (t transitiveAarDeps) manifests() android.Paths { + paths := make(android.Paths, 0, len(t)) + for _, dep := range t { + paths = append(paths, dep.manifest) + paths = append(paths, dep.additionalManifests...) + } + return paths +} + +func (t transitiveAarDeps) resourceProcessorDeps() (args []string, deps android.Paths) { + for _, dep := range t { + args = append(args, "--library="+dep.rTxt.String()+","+dep.manifest.String()) + deps = append(deps, dep.rTxt, dep.manifest) + } + return args, deps +} - var sharedLibs android.Paths +func (t transitiveAarDeps) assets() android.Paths { + paths := make(android.Paths, 0, len(t)) + for _, dep := range t { + if dep.assets.Valid() { + paths = append(paths, dep.assets.Path()) + } + } + return paths +} + +// aaptLibs collects libraries from dependencies and sdk_version and converts them into paths +func aaptLibs(ctx android.ModuleContext, sdkContext android.SdkContext, + classLoaderContexts dexpreopt.ClassLoaderContextMap, usesLibrary *usesLibrary) ( + staticResourcesNodes, sharedResourcesNodes *android.DepSet[*resourcesNode], staticRRODirs *android.DepSet[rroDir], + staticManifests *android.DepSet[android.Path], sharedLibs android.Paths, flags []string) { if classLoaderContexts == nil { // Not all callers need to compute class loader context, those who don't just pass nil. @@ -447,6 +723,11 @@ func aaptLibs(ctx android.ModuleContext, sdkContext android.SdkContext, classLoa sharedLibs = append(sharedLibs, sdkDep.jars...) } + var staticResourcesNodeDepSets []*android.DepSet[*resourcesNode] + var sharedResourcesNodeDepSets []*android.DepSet[*resourcesNode] + rroDirsDepSetBuilder := android.NewDepSetBuilder[rroDir](android.TOPOLOGICAL) + manifestsDepSetBuilder := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL) + ctx.VisitDirectDeps(func(module android.Module) { depTag := ctx.OtherModuleDependencyTag(module) @@ -461,6 +742,7 @@ func aaptLibs(ctx android.ModuleContext, sdkContext android.SdkContext, classLoa // Nothing, instrumentationForTag is treated as libTag for javac but not for aapt2. case sdkLibTag, libTag: if exportPackage != nil { + sharedResourcesNodeDepSets = append(sharedResourcesNodeDepSets, aarDep.ResourcesNodeDepSet()) sharedLibs = append(sharedLibs, exportPackage) } case frameworkResTag: @@ -469,32 +751,34 @@ func aaptLibs(ctx android.ModuleContext, sdkContext android.SdkContext, classLoa } case staticLibTag: if exportPackage != nil { - transitiveStaticLibs = append(transitiveStaticLibs, aarDep.ExportedStaticPackages()...) - transitiveStaticLibs = append(transitiveStaticLibs, exportPackage) - transitiveStaticLibManifests = append(transitiveStaticLibManifests, aarDep.ExportedManifests()...) - if aarDep.ExportedAssets().Valid() { - assets = append(assets, aarDep.ExportedAssets().Path()) - } - - outer: - for _, d := range aarDep.ExportedRRODirs() { - for _, e := range staticRRODirs { - if d.path == e.path { - continue outer - } - } - staticRRODirs = append(staticRRODirs, d) - } + staticResourcesNodeDepSets = append(staticResourcesNodeDepSets, aarDep.ResourcesNodeDepSet()) + rroDirsDepSetBuilder.Transitive(aarDep.RRODirsDepSet()) + manifestsDepSetBuilder.Transitive(aarDep.ManifestsDepSet()) } } addCLCFromDep(ctx, module, classLoaderContexts) + if usesLibrary != nil { + addMissingOptionalUsesLibsFromDep(ctx, module, usesLibrary) + } }) - deps = append(deps, sharedLibs...) - deps = append(deps, transitiveStaticLibs...) - - if len(transitiveStaticLibs) > 0 { + // AAPT2 overlays are in lowest to highest priority order, the topological order will be reversed later. + // Reverse the dependency order now going into the depset so that it comes out in order after the second + // reverse later. + // NOTE: this is legacy and probably incorrect behavior, for most other cases (e.g. conflicting classes in + // dependencies) the highest priority dependency is listed first, but for resources the highest priority + // dependency has to be listed last. This is also inconsistent with the way manifests from the same + // transitive dependencies are merged. + staticResourcesNodes = android.NewDepSet(android.TOPOLOGICAL, nil, + android.ReverseSliceInPlace(staticResourcesNodeDepSets)) + sharedResourcesNodes = android.NewDepSet(android.TOPOLOGICAL, nil, + android.ReverseSliceInPlace(sharedResourcesNodeDepSets)) + + staticRRODirs = rroDirsDepSetBuilder.Build() + staticManifests = manifestsDepSetBuilder.Build() + + if len(staticResourcesNodes.ToList()) > 0 { flags = append(flags, "--auto-add-overlay") } @@ -502,61 +786,62 @@ func aaptLibs(ctx android.ModuleContext, sdkContext android.SdkContext, classLoa flags = append(flags, "-I "+sharedLib.String()) } - transitiveStaticLibs = android.FirstUniquePaths(transitiveStaticLibs) - transitiveStaticLibManifests = android.FirstUniquePaths(transitiveStaticLibManifests) - - return transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assets, deps, flags + return staticResourcesNodes, sharedResourcesNodes, staticRRODirs, staticManifests, sharedLibs, flags } type AndroidLibrary struct { Library aapt - android.BazelModuleBase androidLibraryProperties androidLibraryProperties aarFile android.WritablePath - - exportedStaticPackages android.Paths -} - -var _ android.OutputFileProducer = (*AndroidLibrary)(nil) - -// For OutputFileProducer interface -func (a *AndroidLibrary) OutputFiles(tag string) (android.Paths, error) { - switch tag { - case ".aar": - return []android.Path{a.aarFile}, nil - default: - return a.Library.OutputFiles(tag) - } -} - -func (a *AndroidLibrary) ExportedStaticPackages() android.Paths { - return a.exportedStaticPackages } var _ AndroidLibraryDependency = (*AndroidLibrary)(nil) func (a *AndroidLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { + a.usesLibrary.deps(ctx, false) a.Module.deps(ctx) sdkDep := decodeSdkDep(ctx, android.SdkContext(a)) if sdkDep.hasFrameworkLibs() { a.aapt.deps(ctx, sdkDep) } - a.usesLibrary.deps(ctx, false) + + for _, aconfig_declaration := range a.aaptProperties.Flags_packages { + ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration) + } } func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.aapt.isLibrary = true a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx) - a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts, nil, false) + if a.usesLibrary.shouldDisableDexpreopt { + a.dexpreopter.disableDexpreopt() + } + aconfigTextFilePaths := getAconfigFilePaths(ctx) + a.aapt.buildActions(ctx, + aaptBuildActionOptions{ + sdkContext: android.SdkContext(a), + classLoaderContexts: a.classLoaderContexts, + enforceDefaultTargetSdkVersion: false, + aconfigTextFiles: aconfigTextFilePaths, + usesLibrary: &a.usesLibrary, + }, + ) - a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform() + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) + a.hideApexVariantFromMake = !apexInfo.IsForPlatform() - ctx.CheckbuildFile(a.proguardOptionsFile) - ctx.CheckbuildFile(a.exportPackage) - ctx.CheckbuildFile(a.aaptSrcJar) + a.stem = proptools.StringDefault(a.overridableProperties.Stem, ctx.ModuleName()) + + ctx.CheckbuildFile(a.aapt.proguardOptionsFile) + ctx.CheckbuildFile(a.aapt.exportPackage) + if a.useResourceProcessorBusyBox(ctx) { + ctx.CheckbuildFile(a.aapt.rJar) + } else { + ctx.CheckbuildFile(a.aapt.aaptSrcJar) + } // apps manifests are handled by aapt, don't let Module see them a.properties.Manifest = nil @@ -565,10 +850,32 @@ func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) a.linter.manifest = a.aapt.manifestPath a.linter.resources = a.aapt.resourceFiles - a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, - a.proguardOptionsFile) + proguardSpecInfo := a.collectProguardSpecInfo(ctx) + android.SetProvider(ctx, ProguardSpecInfoProvider, proguardSpecInfo) + exportedProguardFlagsFiles := proguardSpecInfo.ProguardFlagsFiles.ToList() + a.extraProguardFlagsFiles = append(a.extraProguardFlagsFiles, exportedProguardFlagsFiles...) + a.extraProguardFlagsFiles = append(a.extraProguardFlagsFiles, a.proguardOptionsFile) + + combinedExportedProguardFlagFile := android.PathForModuleOut(ctx, "export_proguard_flags") + writeCombinedProguardFlagsFile(ctx, combinedExportedProguardFlagFile, exportedProguardFlagsFiles) + a.combinedExportedProguardFlagsFile = combinedExportedProguardFlagFile + + var extraSrcJars android.Paths + var extraCombinedJars android.Paths + var extraClasspathJars android.Paths + if a.useResourceProcessorBusyBox(ctx) { + // When building a library with ResourceProcessorBusyBox enabled ResourceProcessorBusyBox for this + // library and each of the transitive static android_library dependencies has already created an + // R.class file for the appropriate package. Add all of those R.class files to the classpath. + extraClasspathJars = a.transitiveAaptRJars + } else { + // When building a library without ResourceProcessorBusyBox the aapt2 rule creates R.srcjar containing + // R.java files for the library's package and the packages from all transitive static android_library + // dependencies. Compile the srcjar alongside the rest of the sources. + extraSrcJars = android.Paths{a.aapt.aaptSrcJar} + } - a.Module.compile(ctx, a.aaptSrcJar) + a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars) a.aarFile = android.PathForModuleOut(ctx, ctx.ModuleName()+".aar") var res android.Paths @@ -577,33 +884,39 @@ func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) ctx.CheckbuildFile(a.aarFile) } - a.exportedProguardFlagFiles = append(a.exportedProguardFlagFiles, - android.PathsForModuleSrc(ctx, a.dexProperties.Optimize.Proguard_flags_files)...) - ctx.VisitDirectDeps(func(m android.Module) { - if ctx.OtherModuleDependencyTag(m) == staticLibTag { - if lib, ok := m.(LibraryDependency); ok { - a.exportedProguardFlagFiles = append(a.exportedProguardFlagFiles, lib.ExportedProguardFlagFiles()...) - } - if alib, ok := m.(AndroidLibraryDependency); ok { - a.exportedStaticPackages = append(a.exportedStaticPackages, alib.ExportPackage()) - a.exportedStaticPackages = append(a.exportedStaticPackages, alib.ExportedStaticPackages()...) - } - } - }) - a.exportedProguardFlagFiles = android.FirstUniquePaths(a.exportedProguardFlagFiles) - a.exportedStaticPackages = android.FirstUniquePaths(a.exportedStaticPackages) - prebuiltJniPackages := android.Paths{} ctx.VisitDirectDeps(func(module android.Module) { - if info, ok := ctx.OtherModuleProvider(module, JniPackageProvider).(JniPackageInfo); ok { + if info, ok := android.OtherModuleProvider(ctx, module, JniPackageProvider); ok { prebuiltJniPackages = append(prebuiltJniPackages, info.JniPackages...) } }) if len(prebuiltJniPackages) > 0 { - ctx.SetProvider(JniPackageProvider, JniPackageInfo{ + android.SetProvider(ctx, JniPackageProvider, JniPackageInfo{ JniPackages: prebuiltJniPackages, }) } + + android.SetProvider(ctx, FlagsPackagesProvider, FlagsPackages{ + AconfigTextFiles: aconfigTextFilePaths, + }) + + a.setOutputFiles(ctx) +} + +func (a *AndroidLibrary) setOutputFiles(ctx android.ModuleContext) { + ctx.SetOutputFiles([]android.Path{a.aarFile}, ".aar") + setOutputFiles(ctx, a.Library.Module) +} + +func (a *AndroidLibrary) IDEInfo(dpInfo *android.IdeInfo) { + a.Library.IDEInfo(dpInfo) + a.aapt.IDEInfo(dpInfo) +} + +func (a *aapt) IDEInfo(dpInfo *android.IdeInfo) { + if a.rJar != nil { + dpInfo.Jars = append(dpInfo.Jars, a.rJar.String()) + } } // android_library builds and links sources into a `.jar` file for the device along with Android resources. @@ -618,14 +931,14 @@ func AndroidLibraryFactory() android.Module { module.Module.addHostAndDeviceProperties() module.AddProperties( &module.aaptProperties, - &module.androidLibraryProperties) + &module.androidLibraryProperties, + &module.sourceProperties) module.androidLibraryProperties.BuildAAR = true module.Module.linter.library = true android.InitApexModule(module) InitJavaModule(module, android.DeviceSupported) - android.InitBazelModule(module) return module } @@ -658,13 +971,15 @@ type AARImportProperties struct { // will be passed transitively through android_libraries to an android_app. //TODO(b/241138093) evaluate whether we can have this flag default to true for Bazel conversion Extract_jni *bool + + // If set, overrides the manifest extracted from the AAR with the provided path. + Manifest *string `android:"path"` } type AARImport struct { android.ModuleBase android.DefaultableModuleBase android.ApexModuleBase - android.BazelModuleBase prebuilt android.Prebuilt // Functionality common to Module and Import. @@ -674,14 +989,20 @@ type AARImport struct { properties AARImportProperties - classpathFile android.WritablePath - proguardFlags android.WritablePath - exportPackage android.WritablePath - extraAaptPackagesFile android.WritablePath - manifest android.WritablePath - assetsPackage android.WritablePath - - exportedStaticPackages android.Paths + headerJarFile android.WritablePath + implementationJarFile android.WritablePath + implementationAndResourcesJarFile android.WritablePath + proguardFlags android.WritablePath + exportPackage android.WritablePath + transitiveAaptResourcePackagesFile android.Path + extraAaptPackagesFile android.WritablePath + manifest android.Path + assetsPackage android.WritablePath + rTxt android.WritablePath + rJar android.WritablePath + + resourcesNodesDepSet *android.DepSet[*resourcesNode] + manifestsDepSet *android.DepSet[android.Path] hideApexVariantFromMake bool @@ -690,20 +1011,9 @@ type AARImport struct { sdkVersion android.SdkSpec minSdkVersion android.ApiLevel -} -var _ android.OutputFileProducer = (*AARImport)(nil) - -// For OutputFileProducer interface -func (a *AARImport) OutputFiles(tag string) (android.Paths, error) { - switch tag { - case ".aar": - return []android.Path{a.aarPath}, nil - case "": - return []android.Path{a.classpathFile}, nil - default: - return nil, fmt.Errorf("unsupported module reference tag %q", tag) - } + usesLibrary + classLoaderContexts dexpreopt.ClassLoaderContextMap } func (a *AARImport) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec { @@ -738,25 +1048,16 @@ var _ AndroidLibraryDependency = (*AARImport)(nil) func (a *AARImport) ExportPackage() android.Path { return a.exportPackage } - -func (a *AARImport) ExportedProguardFlagFiles() android.Paths { - return android.Paths{a.proguardFlags} -} - -func (a *AARImport) ExportedRRODirs() []rroDir { - return nil -} - -func (a *AARImport) ExportedStaticPackages() android.Paths { - return a.exportedStaticPackages +func (a *AARImport) ResourcesNodeDepSet() *android.DepSet[*resourcesNode] { + return a.resourcesNodesDepSet } -func (a *AARImport) ExportedManifests() android.Paths { - return android.Paths{a.manifest} +func (a *AARImport) RRODirsDepSet() *android.DepSet[rroDir] { + return android.NewDepSet[rroDir](android.TOPOLOGICAL, nil, nil) } -func (a *AARImport) ExportedAssets() android.OptionalPath { - return android.OptionalPathForPath(a.assetsPackage) +func (a *AARImport) ManifestsDepSet() *android.DepSet[android.Path] { + return a.manifestsDepSet } // RRO enforcement is not available on aar_import since its RRO dirs are not @@ -792,6 +1093,8 @@ func (a *AARImport) DepsMutator(ctx android.BottomUpMutatorContext) { ctx.AddVariationDependencies(nil, libTag, a.properties.Libs...) ctx.AddVariationDependencies(nil, staticLibTag, a.properties.Static_libs...) + + a.usesLibrary.deps(ctx, false) } type JniPackageInfo struct { @@ -800,7 +1103,7 @@ type JniPackageInfo struct { JniPackages android.Paths } -var JniPackageProvider = blueprint.NewProvider(JniPackageInfo{}) +var JniPackageProvider = blueprint.NewProvider[JniPackageInfo]() // Unzip an AAR and extract the JNI libs for $archString. var extractJNI = pctx.AndroidStaticRule("extractJNI", @@ -810,7 +1113,7 @@ var extractJNI = pctx.AndroidStaticRule("extractJNI", `jni_files=$$(find $outDir/jni -type f) && ` + // print error message if there are no JNI libs for this arch `[ -n "$$jni_files" ] || (echo "ERROR: no JNI libs found for arch ${archString}" && exit 1) && ` + - `${config.SoongZipCmd} -o $out -P 'lib/${archString}' ` + + `${config.SoongZipCmd} -o $out -L 0 -P 'lib/${archString}' ` + `-C $outDir/jni/${archString} $$(echo $$jni_files | xargs -n1 printf " -f %s")`, CommandDeps: []string{"${config.SoongZipCmd}"}, }, @@ -837,7 +1140,8 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.sdkVersion = a.SdkVersion(ctx) a.minSdkVersion = a.MinSdkVersion(ctx) - a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform() + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) + a.hideApexVariantFromMake = !apexInfo.IsForPlatform() aarName := ctx.ModuleName() + ".aar" a.aarPath = android.PathForModuleSrc(ctx, a.properties.Aars[0]) @@ -848,20 +1152,43 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { TransformJetifier(ctx, a.aarPath.(android.WritablePath), inputFile) } + jarName := ctx.ModuleName() + ".jar" extractedAARDir := android.PathForModuleOut(ctx, "aar") - a.classpathFile = extractedAARDir.Join(ctx, "classes-combined.jar") - a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt") - a.manifest = extractedAARDir.Join(ctx, "AndroidManifest.xml") + classpathFile := extractedAARDir.Join(ctx, jarName) + + extractedManifest := extractedAARDir.Join(ctx, "AndroidManifest.xml") + providedManifest := android.OptionalPathForModuleSrc(ctx, a.properties.Manifest) + if providedManifest.Valid() { + a.manifest = providedManifest.Path() + } else { + a.manifest = extractedManifest + } + + a.rTxt = extractedAARDir.Join(ctx, "R.txt") a.assetsPackage = android.PathForModuleOut(ctx, "assets.zip") + a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt") + transitiveProguardFlags, transitiveUnconditionalExportedFlags := collectDepProguardSpecInfo(ctx) + android.SetProvider(ctx, ProguardSpecInfoProvider, ProguardSpecInfo{ + ProguardFlagsFiles: android.NewDepSet[android.Path]( + android.POSTORDER, + android.Paths{a.proguardFlags}, + transitiveProguardFlags, + ), + UnconditionallyExportedProguardFlags: android.NewDepSet[android.Path]( + android.POSTORDER, + nil, + transitiveUnconditionalExportedFlags, + ), + }) ctx.Build(pctx, android.BuildParams{ Rule: unzipAAR, Input: a.aarPath, - Outputs: android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest, a.assetsPackage}, + Outputs: android.WritablePaths{classpathFile, a.proguardFlags, extractedManifest, a.assetsPackage, a.rTxt}, Description: "unzip AAR", Args: map[string]string{ "outDir": extractedAARDir.String(), - "combinedClassesJar": a.classpathFile.String(), + "combinedClassesJar": classpathFile.String(), "assetsPackage": a.assetsPackage.String(), }, }) @@ -874,57 +1201,148 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { aapt2CompileZip(ctx, flata, a.aarPath, "res", compileFlags) a.exportPackage = android.PathForModuleOut(ctx, "package-res.apk") - // the subdir "android" is required to be filtered by package names - srcJar := android.PathForModuleGen(ctx, "android", "R.srcjar") proguardOptionsFile := android.PathForModuleGen(ctx, "proguard.options") - rTxt := android.PathForModuleOut(ctx, "R.txt") + aaptRTxt := android.PathForModuleOut(ctx, "R.txt") a.extraAaptPackagesFile = android.PathForModuleOut(ctx, "extra_packages") var linkDeps android.Paths linkFlags := []string{ "--static-lib", - "--no-static-lib-packages", + "--merge-only", "--auto-add-overlay", + "--no-static-lib-packages", } linkFlags = append(linkFlags, "--manifest "+a.manifest.String()) linkDeps = append(linkDeps, a.manifest) - transitiveStaticLibs, staticLibManifests, staticRRODirs, transitiveAssets, libDeps, libFlags := - aaptLibs(ctx, android.SdkContext(a), nil) + staticResourcesNodesDepSet, sharedResourcesNodesDepSet, staticRRODirsDepSet, staticManifestsDepSet, sharedLibs, libFlags := + aaptLibs(ctx, android.SdkContext(a), nil, nil) - _ = staticLibManifests - _ = staticRRODirs + _ = sharedResourcesNodesDepSet + _ = staticRRODirsDepSet - linkDeps = append(linkDeps, libDeps...) - linkFlags = append(linkFlags, libFlags...) + staticDeps := transitiveAarDeps(staticResourcesNodesDepSet.ToList()) - overlayRes := append(android.Paths{flata}, transitiveStaticLibs...) + linkDeps = append(linkDeps, sharedLibs...) + linkDeps = append(linkDeps, staticDeps.resPackages()...) + linkFlags = append(linkFlags, libFlags...) - aapt2Link(ctx, a.exportPackage, srcJar, proguardOptionsFile, rTxt, a.extraAaptPackagesFile, - linkFlags, linkDeps, nil, overlayRes, transitiveAssets, nil) + overlayRes := android.Paths{flata} - // 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 + // Treat static library dependencies of static libraries as imports. + transitiveStaticLibs := staticDeps.resPackages() + linkDeps = append(linkDeps, transitiveStaticLibs...) + for _, staticLib := range transitiveStaticLibs { + linkFlags = append(linkFlags, "-I "+staticLib.String()) } + transitiveAssets := android.ReverseSliceInPlace(staticDeps.assets()) + aapt2Link(ctx, a.exportPackage, nil, proguardOptionsFile, aaptRTxt, + linkFlags, linkDeps, nil, overlayRes, transitiveAssets, nil, nil) + + a.rJar = android.PathForModuleOut(ctx, "busybox/R.jar") + resourceProcessorBusyBoxGenerateBinaryR(ctx, a.rTxt, a.manifest, a.rJar, nil, true, nil, false) + + aapt2ExtractExtraPackages(ctx, a.extraAaptPackagesFile, a.rJar) + + resourcesNodesDepSetBuilder := android.NewDepSetBuilder[*resourcesNode](android.TOPOLOGICAL) + resourcesNodesDepSetBuilder.Direct(&resourcesNode{ + resPackage: a.exportPackage, + manifest: a.manifest, + rTxt: a.rTxt, + rJar: a.rJar, + assets: android.OptionalPathForPath(a.assetsPackage), + + usedResourceProcessor: true, + }) + resourcesNodesDepSetBuilder.Transitive(staticResourcesNodesDepSet) + a.resourcesNodesDepSet = resourcesNodesDepSetBuilder.Build() + + manifestDepSetBuilder := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(a.manifest) + manifestDepSetBuilder.Transitive(staticManifestsDepSet) + a.manifestsDepSet = manifestDepSetBuilder.Build() + + transitiveAaptResourcePackages := staticDeps.resPackages().Strings() + transitiveAaptResourcePackages = slices.DeleteFunc(transitiveAaptResourcePackages, func(p string) bool { + return p == a.exportPackage.String() + }) + transitiveAaptResourcePackagesFile := android.PathForModuleOut(ctx, "transitive-res-packages") + android.WriteFileRule(ctx, transitiveAaptResourcePackagesFile, strings.Join(transitiveAaptResourcePackages, "\n")) + a.transitiveAaptResourcePackagesFile = transitiveAaptResourcePackagesFile + a.collectTransitiveHeaderJars(ctx) - ctx.SetProvider(JavaInfoProvider, JavaInfo{ - HeaderJars: android.PathsIfNonNil(a.classpathFile), + + a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx) + + var staticJars android.Paths + var staticHeaderJars android.Paths + var staticResourceJars android.Paths + ctx.VisitDirectDeps(func(module android.Module) { + if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok { + tag := ctx.OtherModuleDependencyTag(module) + switch tag { + case staticLibTag: + staticJars = append(staticJars, dep.ImplementationJars...) + staticHeaderJars = append(staticHeaderJars, dep.HeaderJars...) + staticResourceJars = append(staticResourceJars, dep.ResourceJars...) + } + } + addCLCFromDep(ctx, module, a.classLoaderContexts) + addMissingOptionalUsesLibsFromDep(ctx, module, &a.usesLibrary) + }) + + var implementationJarFile android.OutputPath + if len(staticJars) > 0 { + combineJars := append(android.Paths{classpathFile}, staticJars...) + implementationJarFile = android.PathForModuleOut(ctx, "combined", jarName).OutputPath + TransformJarsToJar(ctx, implementationJarFile, "combine", combineJars, android.OptionalPath{}, false, nil, nil) + } else { + implementationJarFile = classpathFile + } + + var resourceJarFile android.Path + if len(staticResourceJars) > 1 { + combinedJar := android.PathForModuleOut(ctx, "res-combined", jarName) + TransformJarsToJar(ctx, combinedJar, "for resources", staticResourceJars, android.OptionalPath{}, + false, nil, nil) + resourceJarFile = combinedJar + } else if len(staticResourceJars) == 1 { + resourceJarFile = staticResourceJars[0] + } + + // merge implementation jar with resources if necessary + implementationAndResourcesJar := implementationJarFile + if resourceJarFile != nil { + jars := android.Paths{resourceJarFile, implementationAndResourcesJar} + combinedJar := android.PathForModuleOut(ctx, "withres", jarName).OutputPath + TransformJarsToJar(ctx, combinedJar, "for resources", jars, android.OptionalPath{}, + false, nil, nil) + implementationAndResourcesJar = combinedJar + } + + a.implementationJarFile = implementationJarFile + // Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource + a.implementationAndResourcesJarFile = implementationAndResourcesJar.WithoutRel() + + if len(staticHeaderJars) > 0 { + combineJars := append(android.Paths{classpathFile}, staticHeaderJars...) + a.headerJarFile = android.PathForModuleOut(ctx, "turbine-combined", jarName) + TransformJarsToJar(ctx, a.headerJarFile, "combine header jars", combineJars, android.OptionalPath{}, false, nil, nil) + } else { + a.headerJarFile = classpathFile + } + + android.SetProvider(ctx, JavaInfoProvider, JavaInfo{ + HeaderJars: android.PathsIfNonNil(a.headerJarFile), + ResourceJars: android.PathsIfNonNil(resourceJarFile), TransitiveLibsHeaderJars: a.transitiveLibsHeaderJars, TransitiveStaticLibsHeaderJars: a.transitiveStaticLibsHeaderJars, - ImplementationAndResourcesJars: android.PathsIfNonNil(a.classpathFile), - ImplementationJars: android.PathsIfNonNil(a.classpathFile), + ImplementationAndResourcesJars: android.PathsIfNonNil(a.implementationAndResourcesJarFile), + ImplementationJars: android.PathsIfNonNil(a.implementationJarFile), + StubsLinkType: Implementation, + // TransitiveAconfigFiles: // TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts }) if proptools.Bool(a.properties.Extract_jni) { @@ -946,23 +1364,26 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { }, }) } - - ctx.SetProvider(JniPackageProvider, JniPackageInfo{ - JniPackages: a.jniPackages, - }) } + + android.SetProvider(ctx, JniPackageProvider, JniPackageInfo{ + JniPackages: a.jniPackages, + }) + + ctx.SetOutputFiles([]android.Path{a.implementationAndResourcesJarFile}, "") + ctx.SetOutputFiles([]android.Path{a.aarPath}, ".aar") } func (a *AARImport) HeaderJars() android.Paths { - return android.Paths{a.classpathFile} + return android.Paths{a.headerJarFile} } func (a *AARImport) ImplementationAndResourcesJars() android.Paths { - return android.Paths{a.classpathFile} + return android.Paths{a.implementationAndResourcesJarFile} } -func (a *AARImport) DexJarBuildPath() android.Path { - return nil +func (a *AARImport) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath { + return OptionalDexJarPath{} } func (a *AARImport) DexJarInstallPath() android.Path { @@ -970,9 +1391,11 @@ func (a *AARImport) DexJarInstallPath() android.Path { } func (a *AARImport) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap { - return nil + return a.classLoaderContexts } +var _ UsesLibraryDependency = (*AARImport)(nil) + var _ android.ApexModule = (*AARImport)(nil) // Implements android.ApexModule @@ -981,13 +1404,19 @@ func (a *AARImport) DepIsInSameApex(ctx android.BaseModuleContext, dep android.M } // Implements android.ApexModule -func (g *AARImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext, +func (a *AARImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error { return nil } var _ android.PrebuiltInterface = (*AARImport)(nil) +func (a *AARImport) UsesLibrary() *usesLibrary { + return &a.usesLibrary +} + +var _ ModuleWithUsesLibrary = (*AARImport)(nil) + // android_library_import imports an `.aar` file into the build graph as if it was built with android_library. // // This module is not suitable for installing on a device, but can be used as a `static_libs` dependency of @@ -995,140 +1424,13 @@ var _ android.PrebuiltInterface = (*AARImport)(nil) func AARImportFactory() android.Module { module := &AARImport{} - module.AddProperties(&module.properties) + module.AddProperties( + &module.properties, + &module.usesLibrary.usesLibraryProperties, + ) android.InitPrebuiltModule(module, &module.properties.Aars) android.InitApexModule(module) InitJavaModuleMultiTargets(module, android.DeviceSupported) - android.InitBazelModule(module) return module } - -type bazelAapt struct { - Manifest bazel.Label - Resource_files bazel.LabelListAttribute -} - -type bazelAndroidLibrary struct { - *javaLibraryAttributes - *bazelAapt -} - -type bazelAndroidLibraryImport struct { - Aar bazel.Label - Deps bazel.LabelListAttribute - Exports bazel.LabelListAttribute - Sdk_version bazel.StringAttribute -} - -func (a *aapt) convertAaptAttrsWithBp2Build(ctx android.TopDownMutatorContext) *bazelAapt { - 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...) - } - return &bazelAapt{ - android.BazelLabelForModuleSrcSingle(ctx, manifest), - bazel.MakeLabelListAttribute(resourceFiles), - } -} - -func (a *AARImport) ConvertWithBp2build(ctx android.TopDownMutatorContext) { - aars := android.BazelLabelForModuleSrcExcludes(ctx, a.properties.Aars, []string{}) - exportableStaticLibs := []string{} - // TODO(b/240716882): investigate and handle static_libs deps that are not imports. They are not supported for export by Bazel. - for _, depName := range a.properties.Static_libs { - if dep, ok := ctx.ModuleFromName(depName); ok { - switch dep.(type) { - case *AARImport, *Import: - exportableStaticLibs = append(exportableStaticLibs, depName) - } - } - } - name := android.RemoveOptionalPrebuiltPrefix(a.Name()) - deps := android.BazelLabelForModuleDeps(ctx, android.LastUniqueStrings(android.CopyOf(append(a.properties.Static_libs, a.properties.Libs...)))) - exports := android.BazelLabelForModuleDeps(ctx, android.LastUniqueStrings(exportableStaticLibs)) - - ctx.CreateBazelTargetModule( - bazel.BazelTargetModuleProperties{ - Rule_class: "aar_import", - Bzl_load_location: "//build/bazel/rules/android:rules.bzl", - }, - android.CommonAttributes{Name: name}, - &bazelAndroidLibraryImport{ - Aar: aars.Includes[0], - Deps: bazel.MakeLabelListAttribute(deps), - Exports: bazel.MakeLabelListAttribute(exports), - Sdk_version: bazel.StringAttribute{Value: a.properties.Sdk_version}, - }, - ) - - neverlink := true - ctx.CreateBazelTargetModule( - AndroidLibraryBazelTargetModuleProperties(), - android.CommonAttributes{Name: name + "-neverlink"}, - &bazelAndroidLibrary{ - javaLibraryAttributes: &javaLibraryAttributes{ - Neverlink: bazel.BoolAttribute{Value: &neverlink}, - Exports: bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + name}), - javaCommonAttributes: &javaCommonAttributes{ - Sdk_version: bazel.StringAttribute{Value: a.properties.Sdk_version}, - }, - }, - }, - ) - -} -func AndroidLibraryBazelTargetModuleProperties() bazel.BazelTargetModuleProperties { - return bazel.BazelTargetModuleProperties{ - Rule_class: "android_library", - Bzl_load_location: "//build/bazel/rules/android:rules.bzl", - } -} - -func (a *AndroidLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) { - commonAttrs, bp2buildInfo := a.convertLibraryAttrsBp2Build(ctx) - depLabels := bp2buildInfo.DepLabels - - 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 !depLabels.Deps.IsEmpty() { - ctx.ModuleErrorf("Module has direct dependencies but no sources. Bazel will not allow this.") - } - name := a.Name() - props := AndroidLibraryBazelTargetModuleProperties() - - ctx.CreateBazelTargetModule( - props, - android.CommonAttributes{Name: name}, - &bazelAndroidLibrary{ - &javaLibraryAttributes{ - javaCommonAttributes: commonAttrs, - Deps: deps, - Exports: depLabels.StaticDeps, - }, - a.convertAaptAttrsWithBp2Build(ctx), - }, - ) - - neverlink := true - ctx.CreateBazelTargetModule( - props, - android.CommonAttributes{Name: name + "-neverlink"}, - &bazelAndroidLibrary{ - javaLibraryAttributes: &javaLibraryAttributes{ - Neverlink: bazel.BoolAttribute{Value: &neverlink}, - Exports: bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + name}), - javaCommonAttributes: &javaCommonAttributes{ - Sdk_version: bazel.StringAttribute{Value: a.deviceProperties.Sdk_version}, - Java_version: bazel.StringAttribute{Value: a.properties.Java_version}, - }, - }, - }, - ) -} diff --git a/java/aar_test.go b/java/aar_test.go index 8afa039c4..ebad310ab 100644 --- a/java/aar_test.go +++ b/java/aar_test.go @@ -15,8 +15,9 @@ package java import ( - "android/soong/android" "testing" + + "android/soong/android" ) func TestAarImportProducesJniPackages(t *testing.T) { @@ -52,7 +53,7 @@ func TestAarImportProducesJniPackages(t *testing.T) { appMod := ctx.Module(tc.name, "android_common") appTestMod := ctx.ModuleForTests(tc.name, "android_common") - info, ok := ctx.ModuleProvider(appMod, JniPackageProvider).(JniPackageInfo) + info, ok := android.SingletonModuleProvider(ctx, appMod, JniPackageProvider) if !ok { t.Errorf("expected android_library_import to have JniPackageProvider") } @@ -81,3 +82,98 @@ func TestAarImportProducesJniPackages(t *testing.T) { }) } } + +func TestLibraryFlagsPackages(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + ).RunTestWithBp(t, ` + android_library { + name: "foo", + srcs: ["a.java"], + sdk_version: "current", + flags_packages: [ + "bar", + "baz", + ], + } + aconfig_declarations { + name: "bar", + package: "com.example.package.bar", + container: "com.android.foo", + srcs: [ + "bar.aconfig", + ], + } + aconfig_declarations { + name: "baz", + package: "com.example.package.baz", + container: "com.android.foo", + srcs: [ + "baz.aconfig", + ], + } + `) + + foo := result.ModuleForTests("foo", "android_common") + + // android_library module depends on aconfig_declarations listed in flags_packages + android.AssertBoolEquals(t, "foo expected to depend on bar", true, + CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar")) + + android.AssertBoolEquals(t, "foo expected to depend on baz", true, + CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "baz")) + + aapt2LinkRule := foo.Rule("android/soong/java.aapt2Link") + linkInFlags := aapt2LinkRule.Args["inFlags"] + android.AssertStringDoesContain(t, + "aapt2 link command expected to pass feature flags arguments", + linkInFlags, + "--feature-flags @out/soong/.intermediates/bar/intermediate.txt --feature-flags @out/soong/.intermediates/baz/intermediate.txt", + ) +} + +func TestAndroidLibraryOutputFilesRel(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + ).RunTestWithBp(t, ` + android_library { + name: "foo", + srcs: ["a.java"], + java_resources: ["foo.txt"], + } + + android_library_import { + name: "bar", + aars: ["bar_prebuilt.aar"], + + } + + android_library_import { + name: "baz", + aars: ["baz_prebuilt.aar"], + static_libs: ["foo", "bar"], + } + `) + + foo := result.ModuleForTests("foo", "android_common") + bar := result.ModuleForTests("bar", "android_common") + baz := result.ModuleForTests("baz", "android_common") + + fooOutputPaths := foo.OutputFiles(t, "") + barOutputPaths := bar.OutputFiles(t, "") + bazOutputPaths := baz.OutputFiles(t, "") + + android.AssertPathsRelativeToTopEquals(t, "foo output path", + []string{"out/soong/.intermediates/foo/android_common/withres/foo.jar"}, fooOutputPaths) + android.AssertPathsRelativeToTopEquals(t, "bar output path", + []string{"out/soong/.intermediates/bar/android_common/aar/bar.jar"}, barOutputPaths) + android.AssertPathsRelativeToTopEquals(t, "baz output path", + []string{"out/soong/.intermediates/baz/android_common/withres/baz.jar"}, bazOutputPaths) + + android.AssertStringEquals(t, "foo relative output path", + "foo.jar", fooOutputPaths[0].Rel()) + android.AssertStringEquals(t, "bar relative output path", + "bar.jar", barOutputPaths[0].Rel()) + android.AssertStringEquals(t, "baz relative output path", + "baz.jar", bazOutputPaths[0].Rel()) +} diff --git a/java/android_manifest.go b/java/android_manifest.go index f2ebfa6a2..0c77968e6 100644 --- a/java/android_manifest.go +++ b/java/android_manifest.go @@ -71,12 +71,15 @@ func shouldReturnFinalOrFutureInt(ctx android.ModuleContext, targetSdkVersionLev return targetSdkVersionLevel.IsPreview() && (ctx.Config().UnbundledBuildApps() || includedInMts(ctx.Module())) } -// Helper function that casts android.Module to java.androidTestApp -// If this type conversion is possible, it queries whether the test app is included in an MTS suite +// Helper function that returns true if android_test, android_test_helper_app, java_test are in an MTS suite. func includedInMts(module android.Module) bool { if test, ok := module.(androidTestApp); ok { return test.includedInTestSuite("mts") } + // java_test + if test, ok := module.(*Test); ok { + return android.PrefixInList(test.testProperties.Test_suites, "mts") + } return false } @@ -152,9 +155,10 @@ func ManifestFixer(ctx android.ModuleContext, manifest android.Path, if params.SdkContext != nil { targetSdkVersion := targetSdkVersionForManifestFixer(ctx, params) - if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" { - targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String()) - deps = append(deps, ApiFingerprintPath(ctx)) + if useApiFingerprint, fingerprintTargetSdkVersion, fingerprintDeps := + UseApiFingerprint(ctx); useApiFingerprint && ctx.ModuleName() != "framework-res" { + targetSdkVersion = fingerprintTargetSdkVersion + deps = append(deps, fingerprintDeps) } args = append(args, "--targetSdkVersion ", targetSdkVersion) @@ -169,9 +173,10 @@ func ManifestFixer(ctx android.ModuleContext, manifest android.Path, ctx.ModuleErrorf("invalid ReplaceMaxSdkVersionPlaceholder: %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 useApiFingerprint, fingerprintMinSdkVersion, fingerprintDeps := + UseApiFingerprint(ctx); useApiFingerprint && ctx.ModuleName() != "framework-res" { + minSdkVersion = fingerprintMinSdkVersion + deps = append(deps, fingerprintDeps) } if err != nil { @@ -200,13 +205,24 @@ func ManifestFixer(ctx android.ModuleContext, manifest android.Path, return fixedManifest.WithoutRel() } -func manifestMerger(ctx android.ModuleContext, manifest android.Path, staticLibManifests android.Paths, - isLibrary bool) android.Path { +type ManifestMergerParams struct { + staticLibManifests android.Paths + isLibrary bool + packageName string +} + +func manifestMerger(ctx android.ModuleContext, manifest android.Path, + params ManifestMergerParams) android.Path { - var args string - if !isLibrary { + var args []string + if !params.isLibrary { // Follow Gradle's behavior, only pass --remove-tools-declarations when merging app manifests. - args = "--remove-tools-declarations" + args = append(args, "--remove-tools-declarations") + } + + packageName := params.packageName + if packageName != "" { + args = append(args, "--property PACKAGE="+packageName) } mergedManifest := android.PathForModuleOut(ctx, "manifest_merger", "AndroidManifest.xml") @@ -214,11 +230,11 @@ func manifestMerger(ctx android.ModuleContext, manifest android.Path, staticLibM Rule: manifestMergerRule, Description: "merge manifest", Input: manifest, - Implicits: staticLibManifests, + Implicits: params.staticLibManifests, Output: mergedManifest, Args: map[string]string{ - "libs": android.JoinWithPrefix(staticLibManifests.Strings(), "--libs "), - "args": args, + "libs": android.JoinWithPrefix(params.staticLibManifests.Strings(), "--libs "), + "args": strings.Join(args, " "), }, }) diff --git a/java/android_manifest_test.go b/java/android_manifest_test.go new file mode 100644 index 000000000..7c9188462 --- /dev/null +++ b/java/android_manifest_test.go @@ -0,0 +1,135 @@ +// Copyright 2023 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 TestManifestMerger(t *testing.T) { + bp := ` + android_app { + name: "app", + sdk_version: "current", + srcs: ["app/app.java"], + resource_dirs: ["app/res"], + manifest: "app/AndroidManifest.xml", + additional_manifests: ["app/AndroidManifest2.xml"], + static_libs: ["direct", "direct_import"], + } + + android_library { + name: "direct", + sdk_version: "current", + srcs: ["direct/direct.java"], + resource_dirs: ["direct/res"], + manifest: "direct/AndroidManifest.xml", + additional_manifests: ["direct/AndroidManifest2.xml"], + static_libs: ["transitive", "transitive_import"], + } + + android_library { + name: "transitive", + sdk_version: "current", + srcs: ["transitive/transitive.java"], + resource_dirs: ["transitive/res"], + manifest: "transitive/AndroidManifest.xml", + additional_manifests: ["transitive/AndroidManifest2.xml"], + } + + android_library_import { + name: "direct_import", + sdk_version: "current", + aars: ["direct_import.aar"], + static_libs: ["direct_import_dep"], + } + + android_library_import { + name: "direct_import_dep", + sdk_version: "current", + aars: ["direct_import_dep.aar"], + } + + android_library_import { + name: "transitive_import", + sdk_version: "current", + aars: ["transitive_import.aar"], + static_libs: ["transitive_import_dep"], + } + + android_library_import { + name: "transitive_import_dep", + sdk_version: "current", + aars: ["transitive_import_dep.aar"], + } + ` + + result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, bp) + + manifestMergerRule := result.ModuleForTests("app", "android_common").Rule("manifestMerger") + android.AssertPathRelativeToTopEquals(t, "main manifest", + "out/soong/.intermediates/app/android_common/manifest_fixer/AndroidManifest.xml", + manifestMergerRule.Input) + android.AssertPathsRelativeToTopEquals(t, "lib manifests", + []string{ + "app/AndroidManifest2.xml", + "out/soong/.intermediates/direct/android_common/manifest_fixer/AndroidManifest.xml", + "direct/AndroidManifest2.xml", + "out/soong/.intermediates/transitive/android_common/manifest_fixer/AndroidManifest.xml", + "transitive/AndroidManifest2.xml", + "out/soong/.intermediates/transitive_import/android_common/aar/AndroidManifest.xml", + "out/soong/.intermediates/transitive_import_dep/android_common/aar/AndroidManifest.xml", + "out/soong/.intermediates/direct_import/android_common/aar/AndroidManifest.xml", + "out/soong/.intermediates/direct_import_dep/android_common/aar/AndroidManifest.xml", + }, + manifestMergerRule.Implicits) +} + +func TestManifestValuesApplicationIdSetsPackageName(t *testing.T) { + bp := ` + android_test { + name: "test", + sdk_version: "current", + srcs: ["app/app.java"], + manifest: "test/AndroidManifest.xml", + additional_manifests: ["test/AndroidManifest2.xml"], + static_libs: ["direct"], + test_suites: ["device-tests"], + manifest_values: { + applicationId: "new_package_name" + }, + } + + android_library { + name: "direct", + sdk_version: "current", + srcs: ["direct/direct.java"], + resource_dirs: ["direct/res"], + manifest: "direct/AndroidManifest.xml", + additional_manifests: ["direct/AndroidManifest2.xml"], + } + + ` + + result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, bp) + + manifestMergerRule := result.ModuleForTests("test", "android_common").Rule("manifestMerger") + android.AssertStringMatches(t, + "manifest merger args", + manifestMergerRule.Args["args"], + "--property PACKAGE=new_package_name") +} diff --git a/java/android_resources.go b/java/android_resources.go index 8c5908f69..038a260d9 100644 --- a/java/android_resources.go +++ b/java/android_resources.go @@ -21,14 +21,6 @@ import ( "android/soong/android" ) -func init() { - registerOverlayBuildComponents(android.InitRegistrationContext) -} - -func registerOverlayBuildComponents(ctx android.RegistrationContext) { - ctx.RegisterPreSingletonType("overlay", OverlaySingletonFactory) -} - var androidResourceIgnoreFilenames = []string{ ".svn", ".git", @@ -84,7 +76,38 @@ type globbedResourceDir struct { func overlayResourceGlob(ctx android.ModuleContext, a *aapt, dir android.Path) (res []globbedResourceDir, rroDirs []rroDir) { - overlayData := ctx.Config().Get(overlayDataKey).([]overlayGlobResult) + overlayData := ctx.Config().Once(overlayDataKey, func() interface{} { + var overlayData []overlayGlobResult + + appendOverlayData := func(overlayDirs []string, t overlayType) { + for i := range overlayDirs { + // Iterate backwards through the list of overlay directories so that the later, lower-priority + // directories in the list show up earlier in the command line to aapt2. + overlay := overlayDirs[len(overlayDirs)-1-i] + var result overlayGlobResult + result.dir = overlay + result.overlayType = t + + files, err := ctx.GlobWithDeps(filepath.Join(overlay, "**/*"), androidResourceIgnoreFilenames) + if err != nil { + ctx.ModuleErrorf("failed to glob resource dir %q: %s", overlay, err.Error()) + continue + } + var paths android.Paths + for _, f := range files { + if !strings.HasSuffix(f, "/") { + paths = append(paths, android.PathForSource(ctx, f)) + } + } + result.paths = android.PathsToDirectorySortedPaths(paths) + overlayData = append(overlayData, result) + } + } + + appendOverlayData(ctx.Config().DeviceResourceOverlays(), device) + appendOverlayData(ctx.Config().ProductResourceOverlays(), product) + return overlayData + }).([]overlayGlobResult) // Runtime resource overlays (RRO) may be turned on by the product config for some modules rroEnabled := a.IsRROEnforced(ctx) @@ -110,44 +133,3 @@ func overlayResourceGlob(ctx android.ModuleContext, a *aapt, dir android.Path) ( return res, rroDirs } - -func OverlaySingletonFactory() android.Singleton { - return overlaySingleton{} -} - -type overlaySingleton struct{} - -func (overlaySingleton) GenerateBuildActions(ctx android.SingletonContext) { - var overlayData []overlayGlobResult - - appendOverlayData := func(overlayDirs []string, t overlayType) { - for i := range overlayDirs { - // Iterate backwards through the list of overlay directories so that the later, lower-priority - // directories in the list show up earlier in the command line to aapt2. - overlay := overlayDirs[len(overlayDirs)-1-i] - var result overlayGlobResult - result.dir = overlay - result.overlayType = t - - files, err := ctx.GlobWithDeps(filepath.Join(overlay, "**/*"), androidResourceIgnoreFilenames) - if err != nil { - ctx.Errorf("failed to glob resource dir %q: %s", overlay, err.Error()) - continue - } - var paths android.Paths - for _, f := range files { - if !strings.HasSuffix(f, "/") { - paths = append(paths, android.PathForSource(ctx, f)) - } - } - result.paths = android.PathsToDirectorySortedPaths(paths) - overlayData = append(overlayData, result) - } - } - - appendOverlayData(ctx.Config().DeviceResourceOverlays(), device) - appendOverlayData(ctx.Config().ProductResourceOverlays(), product) - ctx.Config().Once(overlayDataKey, func() interface{} { - return overlayData - }) -} diff --git a/java/androidmk.go b/java/androidmk.go index 9c21633fb..a1bc90494 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -17,9 +17,10 @@ package java import ( "fmt" "io" - "strings" "android/soong/android" + + "github.com/google/blueprint/proptools" ) func (library *Library) AndroidMkEntriesHostDex() android.AndroidMkEntries { @@ -79,6 +80,9 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { } else if !library.ApexModuleBase.AvailableFor(android.AvailableToPlatform) { // Platform variant. If not available for the platform, we don't need Make module. entriesList = append(entriesList, android.AndroidMkEntries{Disabled: true}) + } else if proptools.Bool(library.properties.Headers_only) { + // If generating headers only then don't expose to Make. + entriesList = append(entriesList, android.AndroidMkEntries{Disabled: true}) } else { entriesList = append(entriesList, android.AndroidMkEntries{ Class: "JAVA_LIBRARIES", @@ -87,11 +91,7 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries { ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { if len(library.logtagsSrcs) > 0 { - var logtags []string - for _, l := range library.logtagsSrcs { - logtags = append(logtags, l.Rel()) - } - entries.AddStrings("LOCAL_LOGTAGS_FILES", logtags...) + entries.AddStrings("LOCAL_SOONG_LOGTAGS_FILES", library.logtagsSrcs.Strings()...) } if library.installFile == nil { @@ -200,26 +200,24 @@ func (j *TestHelperLibrary) AndroidMkEntries() []android.AndroidMkEntries { func (prebuilt *Import) AndroidMkEntries() []android.AndroidMkEntries { 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}) + return []android.AndroidMkEntries{} } return []android.AndroidMkEntries{android.AndroidMkEntries{ - Class: "JAVA_LIBRARIES", - OutputFile: android.OptionalPathForPath(prebuilt.combinedClasspathFile), - Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk", + Class: "JAVA_LIBRARIES", + OverrideName: prebuilt.BaseModuleName(), + OutputFile: android.OptionalPathForPath(prebuilt.combinedImplementationFile), + Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk", ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !Bool(prebuilt.properties.Installable)) 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) + entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.combinedHeaderFile) + entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.combinedImplementationFile) entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion.String()) entries.SetString("LOCAL_MODULE_STEM", prebuilt.Stem()) + // TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts }, }, }} @@ -244,6 +242,7 @@ func (prebuilt *DexImport) AndroidMkEntries() []android.AndroidMkEntries { entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", prebuilt.dexpreopter.builtInstalled) } entries.SetString("LOCAL_MODULE_STEM", prebuilt.Stem()) + // TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts }, }, }} @@ -257,18 +256,20 @@ func (prebuilt *AARImport) AndroidMkEntries() []android.AndroidMkEntries { } return []android.AndroidMkEntries{android.AndroidMkEntries{ Class: "JAVA_LIBRARIES", - OutputFile: android.OptionalPathForPath(prebuilt.classpathFile), + OutputFile: android.OptionalPathForPath(prebuilt.implementationJarFile), Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk", ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true) - entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.classpathFile) - entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.classpathFile) + entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.headerJarFile) + entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.implementationJarFile) entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", prebuilt.exportPackage) + entries.SetPath("LOCAL_SOONG_TRANSITIVE_RES_PACKAGES", prebuilt.transitiveAaptResourcePackagesFile) entries.SetPath("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", prebuilt.proguardFlags) entries.SetPath("LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES", prebuilt.extraAaptPackagesFile) entries.SetPath("LOCAL_FULL_MANIFEST_FILE", prebuilt.manifest) entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion.String()) + // TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts }, }, }} @@ -331,15 +332,23 @@ func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries { Disabled: true, }} } + var required []string + if proptools.Bool(app.appProperties.Generate_product_characteristics_rro) { + required = []string{app.productCharacteristicsRROPackageName()} + } return []android.AndroidMkEntries{android.AndroidMkEntries{ Class: "APPS", OutputFile: android.OptionalPathForPath(app.outputFile), Include: "$(BUILD_SYSTEM)/soong_app_prebuilt.mk", + Required: required, ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { // App module names can be overridden. entries.SetString("LOCAL_MODULE", app.installApkName) entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", app.appProperties.PreventInstall) + if app.headerJarFile != nil { + entries.SetPath("LOCAL_SOONG_HEADER_JAR", app.headerJarFile) + } entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", app.exportPackage) if app.dexJarFile.IsSet() { entries.SetPath("LOCAL_SOONG_DEX_JAR", app.dexJarFile.Path()) @@ -368,8 +377,13 @@ func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries { filterRRO := func(filter overlayType) android.Paths { var paths android.Paths - for _, d := range app.rroDirs { + seen := make(map[android.Path]bool) + for _, d := range app.rroDirsDepSet.ToList() { if d.overlayType == filter { + if seen[d.path] { + continue + } + seen[d.path] = true paths = append(paths, d.path) } } @@ -399,22 +413,11 @@ func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries { jniSymbols := app.JNISymbolsInstalls(app.installPathForJNISymbols.String()) entries.SetString("LOCAL_SOONG_JNI_LIBS_SYMBOLS", jniSymbols.String()) } else { + var names []string for _, jniLib := range app.jniLibs { - entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name) - var partitionTag string - - // Mimic the creation of partition_tag in build/make, - // which defaults to an empty string when the partition is system. - // Otherwise, capitalize with a leading _ - if jniLib.partition == "system" { - partitionTag = "" - } else { - split := strings.Split(jniLib.partition, "/") - partitionTag = "_" + strings.ToUpper(split[len(split)-1]) - } - entries.AddStrings("LOCAL_SOONG_JNI_LIBS_PARTITION_"+jniLib.target.Arch.ArchType.String(), - jniLib.name+":"+partitionTag) + names = append(names, jniLib.name) } + entries.AddStrings("LOCAL_REQUIRED_MODULES", names...) } if len(app.jniCoverageOutputs) > 0 { @@ -432,6 +435,8 @@ func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries { } entries.SetOptionalPaths("LOCAL_SOONG_LINT_REPORTS", app.linter.reports) + + entries.AddStrings("LOCAL_SOONG_LOGTAGS_FILES", app.logtagsSrcs.Strings()...) }, }, ExtraFooters: []android.AndroidMkExtraFootersFunc{ @@ -450,11 +455,6 @@ func (a *AndroidApp) getOverriddenPackages() []string { if len(a.overridableAppProperties.Overrides) > 0 { overridden = append(overridden, a.overridableAppProperties.Overrides...) } - // 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 } @@ -508,9 +508,10 @@ func (a *AndroidLibrary) AndroidMkEntries() []android.AndroidMkEntries { } entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", a.exportPackage) + entries.SetPath("LOCAL_SOONG_TRANSITIVE_RES_PACKAGES", a.transitiveAaptResourcePackagesFile) entries.SetPath("LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES", a.extraAaptPackagesFile) entries.SetPath("LOCAL_FULL_MANIFEST_FILE", a.mergedManifestFile) - entries.AddStrings("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", a.exportedProguardFlagFiles.Strings()...) + entries.SetPath("LOCAL_SOONG_EXPORT_PROGUARD_FLAGS", a.combinedExportedProguardFlagsFile) entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", true) }) @@ -527,8 +528,8 @@ func (jd *Javadoc) AndroidMkEntries() []android.AndroidMkEntries { if BoolDefault(jd.properties.Installable, true) { entries.SetPath("LOCAL_DROIDDOC_DOC_ZIP", jd.docZip) } - if jd.stubsSrcJar != nil { - entries.SetPath("LOCAL_DROIDDOC_STUBS_SRCJAR", jd.stubsSrcJar) + if jd.exportableStubsSrcJar != nil { + entries.SetPath("LOCAL_DROIDDOC_STUBS_SRCJAR", jd.exportableStubsSrcJar) } }, }, @@ -566,7 +567,7 @@ func (dstubs *Droidstubs) AndroidMkEntries() []android.AndroidMkEntries { outputFile = android.OptionalPathForPath(dstubs.apiFile) } if !outputFile.Valid() { - outputFile = android.OptionalPathForPath(dstubs.apiVersionsXml) + outputFile = android.OptionalPathForPath(dstubs.everythingArtifacts.apiVersionsXml) } return []android.AndroidMkEntries{android.AndroidMkEntries{ Class: "JAVA_LIBRARIES", @@ -574,17 +575,17 @@ func (dstubs *Droidstubs) AndroidMkEntries() []android.AndroidMkEntries { Include: "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk", ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - if dstubs.Javadoc.stubsSrcJar != nil { - entries.SetPath("LOCAL_DROIDDOC_STUBS_SRCJAR", dstubs.Javadoc.stubsSrcJar) + if dstubs.Javadoc.exportableStubsSrcJar != nil { + entries.SetPath("LOCAL_DROIDDOC_STUBS_SRCJAR", dstubs.Javadoc.exportableStubsSrcJar) } - if dstubs.apiVersionsXml != nil { - entries.SetPath("LOCAL_DROIDDOC_API_VERSIONS_XML", dstubs.apiVersionsXml) + if dstubs.everythingArtifacts.apiVersionsXml != nil { + entries.SetPath("LOCAL_DROIDDOC_API_VERSIONS_XML", dstubs.exportableArtifacts.apiVersionsXml) } - if dstubs.annotationsZip != nil { - entries.SetPath("LOCAL_DROIDDOC_ANNOTATIONS_ZIP", dstubs.annotationsZip) + if dstubs.everythingArtifacts.annotationsZip != nil { + entries.SetPath("LOCAL_DROIDDOC_ANNOTATIONS_ZIP", dstubs.exportableArtifacts.annotationsZip) } - if dstubs.metadataZip != nil { - entries.SetPath("LOCAL_DROIDDOC_METADATA_ZIP", dstubs.metadataZip) + if dstubs.everythingArtifacts.metadataZip != nil { + entries.SetPath("LOCAL_DROIDDOC_METADATA_ZIP", dstubs.exportableArtifacts.metadataZip) } }, }, @@ -669,9 +670,10 @@ func (a *AndroidAppImport) AndroidMkEntries() []android.AndroidMkEntries { return nil } return []android.AndroidMkEntries{android.AndroidMkEntries{ - Class: "APPS", - OutputFile: android.OptionalPathForPath(a.outputFile), - Include: "$(BUILD_SYSTEM)/soong_app_prebuilt.mk", + Class: "APPS", + OutputFile: android.OptionalPathForPath(a.outputFile), + OverrideName: a.BaseModuleName(), // TODO (spandandas): Add a test + Include: "$(BUILD_SYSTEM)/soong_app_prebuilt.mk", ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", a.Privileged()) @@ -684,6 +686,7 @@ func (a *AndroidAppImport) AndroidMkEntries() []android.AndroidMkEntries { if Bool(a.properties.Export_package_resources) { entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", a.outputFile) } + // TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts }, }, }} @@ -717,6 +720,7 @@ func (r *RuntimeResourceOverlay) AndroidMkEntries() []android.AndroidMkEntries { entries.SetString("LOCAL_CERTIFICATE", r.certificate.AndroidMkString()) entries.SetPath("LOCAL_MODULE_PATH", r.installDir) entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", r.properties.Overrides...) + // TODO: LOCAL_ACONFIG_FILES -- Might eventually need aconfig flags? }, }, }} @@ -734,6 +738,7 @@ func (apkSet *AndroidAppSet) AndroidMkEntries() []android.AndroidMkEntries { entries.SetPath("LOCAL_APK_SET_INSTALL_FILE", apkSet.PackedAdditionalOutputs()) entries.SetPath("LOCAL_APKCERTS_FILE", apkSet.apkcertsFile) entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", apkSet.properties.Overrides...) + // TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts -- Both declarations and values }, }, }, diff --git a/java/androidmk_test.go b/java/androidmk_test.go index 1232cd1ee..243a2791e 100644 --- a/java/androidmk_test.go +++ b/java/androidmk_test.go @@ -20,8 +20,6 @@ import ( "android/soong/android" "android/soong/cc" - - "github.com/google/blueprint/proptools" ) func TestRequired(t *testing.T) { @@ -161,8 +159,8 @@ func TestJavaSdkLibrary_RequireXmlPermissionFile(t *testing.T) { moduleName string expected []string }{ - {"foo-shared_library", []string{"foo-shared_library.xml"}}, - {"foo-no_shared_library", nil}, + {"foo-shared_library", []string{"foo-shared_library.impl", "foo-shared_library.xml"}}, + {"foo-no_shared_library", []string{"foo-no_shared_library.impl"}}, } for _, tc := range testCases { mod := result.ModuleForTests(tc.moduleName, "android_common").Module() @@ -256,148 +254,50 @@ func TestGetOverriddenPackages(t *testing.T) { } } -func TestJniPartition(t *testing.T) { - bp := ` - cc_library { - name: "libjni_system", - system_shared_libs: [], - sdk_version: "current", - stl: "none", - } - - cc_library { - name: "libjni_system_ext", - system_shared_libs: [], - sdk_version: "current", - stl: "none", - system_ext_specific: true, - } - - cc_library { - name: "libjni_odm", - system_shared_libs: [], - sdk_version: "current", - stl: "none", - device_specific: true, - } - - cc_library { - name: "libjni_product", - system_shared_libs: [], - sdk_version: "current", - stl: "none", - product_specific: true, - } - - cc_library { - name: "libjni_vendor", - system_shared_libs: [], - sdk_version: "current", - stl: "none", - soc_specific: true, - } - - android_app { - name: "test_app_system_jni_system", - privileged: true, - platform_apis: true, - certificate: "platform", - jni_libs: ["libjni_system"], - } - +func TestJniAsRequiredDeps(t *testing.T) { + ctx := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + cc.PrepareForTestWithCcDefaultModules, + android.PrepareForTestWithAndroidMk, + ).RunTestWithBp(t, ` android_app { - name: "test_app_system_jni_system_ext", - privileged: true, + name: "app", + jni_libs: ["libjni"], platform_apis: true, - certificate: "platform", - jni_libs: ["libjni_system_ext"], } android_app { - name: "test_app_system_ext_jni_system", - privileged: true, + name: "app_embedded", + jni_libs: ["libjni"], platform_apis: true, - certificate: "platform", - jni_libs: ["libjni_system"], - system_ext_specific: true - } - - android_app { - name: "test_app_system_ext_jni_system_ext", - sdk_version: "core_platform", - jni_libs: ["libjni_system_ext"], - system_ext_specific: true + use_embedded_native_libs: true, } - android_app { - name: "test_app_product_jni_product", - sdk_version: "core_platform", - jni_libs: ["libjni_product"], - product_specific: true - } - - android_app { - name: "test_app_vendor_jni_odm", - sdk_version: "core_platform", - jni_libs: ["libjni_odm"], - soc_specific: true + cc_library { + name: "libjni", + system_shared_libs: [], + stl: "none", } + `) - android_app { - name: "test_app_odm_jni_vendor", - sdk_version: "core_platform", - jni_libs: ["libjni_vendor"], - device_specific: true - } - android_app { - name: "test_app_system_jni_multiple", - privileged: true, - platform_apis: true, - certificate: "platform", - jni_libs: ["libjni_system", "libjni_system_ext"], - } - android_app { - name: "test_app_vendor_jni_multiple", - sdk_version: "core_platform", - jni_libs: ["libjni_odm", "libjni_vendor"], - soc_specific: true - } - ` - arch := "arm64" - ctx := android.GroupFixturePreparers( - PrepareForTestWithJavaDefaultModules, - cc.PrepareForTestWithCcDefaultModules, - android.PrepareForTestWithAndroidMk, - android.FixtureModifyConfig(func(config android.Config) { - config.TestProductVariables.DeviceArch = proptools.StringPtr(arch) - }), - ). - RunTestWithBp(t, bp) - testCases := []struct { - name string - partitionNames []string - partitionTags []string + testcases := []struct { + name string + expected []string }{ - {"test_app_system_jni_system", []string{"libjni_system"}, []string{""}}, - {"test_app_system_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}}, - {"test_app_system_ext_jni_system", []string{"libjni_system"}, []string{""}}, - {"test_app_system_ext_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}}, - {"test_app_product_jni_product", []string{"libjni_product"}, []string{"_PRODUCT"}}, - {"test_app_vendor_jni_odm", []string{"libjni_odm"}, []string{"_ODM"}}, - {"test_app_odm_jni_vendor", []string{"libjni_vendor"}, []string{"_VENDOR"}}, - {"test_app_system_jni_multiple", []string{"libjni_system", "libjni_system_ext"}, []string{"", "_SYSTEM_EXT"}}, - {"test_app_vendor_jni_multiple", []string{"libjni_odm", "libjni_vendor"}, []string{"_ODM", "_VENDOR"}}, + { + name: "app", + expected: []string{"libjni"}, + }, + { + name: "app_embedded", + expected: nil, + }, } - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - mod := ctx.ModuleForTests(test.name, "android_common").Module() - entry := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)[0] - for i := range test.partitionNames { - actual := entry.EntryMap["LOCAL_SOONG_JNI_LIBS_PARTITION_"+arch][i] - expected := test.partitionNames[i] + ":" + test.partitionTags[i] - android.AssertStringEquals(t, "Expected and actual differ", expected, actual) - } - }) + for _, tc := range testcases { + mod := ctx.ModuleForTests(tc.name, "android_common").Module() + entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)[0] + required := entries.EntryMap["LOCAL_REQUIRED_MODULES"] + android.AssertDeepEquals(t, "unexpected required deps", tc.expected, required) } } diff --git a/java/app.go b/java/app.go index 706f99a83..19dc8d5d1 100755..100644 --- a/java/app.go +++ b/java/app.go @@ -18,23 +18,42 @@ package java // related module types, including their override variants. import ( + "fmt" "path/filepath" "strings" + "android/soong/testing" + "github.com/google/blueprint" "github.com/google/blueprint/proptools" "android/soong/android" - "android/soong/bazel" "android/soong/cc" "android/soong/dexpreopt" + "android/soong/genrule" "android/soong/tradefed" ) func init() { RegisterAppBuildComponents(android.InitRegistrationContext) + pctx.HostBinToolVariable("ModifyAllowlistCmd", "modify_permissions_allowlist") } +var ( + modifyAllowlist = pctx.AndroidStaticRule("modifyAllowlist", + blueprint.RuleParams{ + Command: "${ModifyAllowlistCmd} $in $packageName $out", + CommandDeps: []string{"${ModifyAllowlistCmd}"}, + }, "packageName") +) + +type FlagsPackages struct { + // Paths to the aconfig dump output text files that are consumed by aapt2 + AconfigTextFiles android.Paths +} + +var FlagsPackagesProvider = blueprint.NewProvider[FlagsPackages]() + func RegisterAppBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("android_app", AndroidAppFactory) ctx.RegisterModuleType("android_test", AndroidTestFactory) @@ -115,6 +134,19 @@ type appProperties struct { // Prefer using other specific properties if build behaviour must be changed; avoid using this // flag for anything but neverallow rules (unless the behaviour change is invisible to owners). Updatable *bool + + // Specifies the file that contains the allowlist for this app. + Privapp_allowlist *string `android:"path"` + + // If set, create an RRO package which contains only resources having PRODUCT_CHARACTERISTICS + // and install the RRO package to /product partition, instead of passing --product argument + // to aapt2. Default is false. + // Setting this will make this APK identical to all targets, regardless of + // PRODUCT_CHARACTERISTICS. + Generate_product_characteristics_rro *bool + + ProductCharacteristicsRROPackageName *string `blueprint:"mutated"` + ProductCharacteristicsRROManifestModuleName *string `blueprint:"mutated"` } // android_app properties that can be overridden by override_android_app @@ -147,7 +179,6 @@ type overridableAppProperties struct { } type AndroidApp struct { - android.BazelModuleBase Library aapt android.OverridableModuleBase @@ -179,18 +210,16 @@ type AndroidApp struct { android.ApexBundleDepsInfo javaApiUsedByOutputFile android.ModuleOutPath + + privAppAllowlist android.OptionalPath } func (a *AndroidApp) IsInstallable() bool { return Bool(a.properties.Installable) } -func (a *AndroidApp) ExportedProguardFlagFiles() android.Paths { - return nil -} - -func (a *AndroidApp) ExportedStaticPackages() android.Paths { - return nil +func (a *AndroidApp) ResourcesNodeDepSet() *android.DepSet[*resourcesNode] { + return a.aapt.resourcesNodesDepSet } func (a *AndroidApp) OutputFile() android.Path { @@ -205,6 +234,10 @@ func (a *AndroidApp) JniCoverageOutputs() android.Paths { return a.jniCoverageOutputs } +func (a *AndroidApp) PrivAppAllowlist() android.OptionalPath { + return a.privAppAllowlist +} + var _ AndroidLibraryDependency = (*AndroidApp)(nil) type Certificate struct { @@ -223,13 +256,13 @@ func (c Certificate) AndroidMkString() string { } func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) { - a.Module.deps(ctx) - if String(a.appProperties.Stl) == "c++_shared" && !a.SdkVersion(ctx).Specified() { ctx.PropertyErrorf("stl", "sdk_version must be set in order to use c++_shared") } sdkDep := decodeSdkDep(ctx, android.SdkContext(a)) + a.usesLibrary.deps(ctx, sdkDep.hasFrameworkLibs()) + a.Module.deps(ctx) if sdkDep.hasFrameworkLibs() { a.aapt.deps(ctx, sdkDep) } @@ -248,19 +281,41 @@ func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) { variation := append(jniTarget.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"}) - // If the app builds against an Android SDK use the SDK variant of JNI dependencies - // unless jni_uses_platform_apis is set. - // Don't require the SDK variant for apps that are shipped on vendor, etc., as they already - // have stable APIs through the VNDK. - if (usesSDK && !a.RequiresStableAPIs(ctx) && - !Bool(a.appProperties.Jni_uses_platform_apis)) || - Bool(a.appProperties.Jni_uses_sdk_apis) { + // Test whether to use the SDK variant or the non-SDK variant of JNI dependencies. + // Many factors are considered here. + // 1. Basically, the selection follows whether the app has sdk_version set or not. + jniUsesSdkVariant := usesSDK + // 2. However, jni_uses_platform_apis and jni_uses_sdk_apis can override it + if Bool(a.appProperties.Jni_uses_sdk_apis) { + jniUsesSdkVariant = true + } + if Bool(a.appProperties.Jni_uses_platform_apis) { + jniUsesSdkVariant = false + } + // 3. Then the use of SDK variant is again prohibited for the following cases: + // 3.1. the app is shipped on unbundled partitions like vendor. Since the entire + // partition (not only the app) is considered unbudled, there's no need to use the + // SDK variant. + // 3.2. the app doesn't support embedding the JNI libs + if a.RequiresStableAPIs(ctx) || !a.shouldEmbedJnis(ctx) { + jniUsesSdkVariant = false + } + if jniUsesSdkVariant { variation = append(variation, blueprint.Variation{Mutator: "sdk", Variation: "sdk"}) } - ctx.AddFarVariationDependencies(variation, jniLibTag, a.appProperties.Jni_libs...) - } - a.usesLibrary.deps(ctx, sdkDep.hasFrameworkLibs()) + // Use the installable dep tag when the JNIs are not embedded + var tag dependencyTag + if a.shouldEmbedJnis(ctx) { + tag = jniLibTag + } else { + tag = jniInstallTag + } + ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...) + } + for _, aconfig_declaration := range a.aaptProperties.Flags_packages { + ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration) + } } func (a *AndroidApp) OverridablePropertiesDepsMutator(ctx android.BottomUpMutatorContext) { @@ -269,6 +324,16 @@ func (a *AndroidApp) OverridablePropertiesDepsMutator(ctx android.BottomUpMutato ctx.AddDependency(ctx.Module(), certificateTag, cert) } + if a.appProperties.Privapp_allowlist != nil && !Bool(a.appProperties.Privileged) { + // There are a few uids that are explicitly considered privileged regardless of their + // app's location. Bluetooth is one such app. It should arguably be moved to priv-app, + // but for now, allow it not to be in priv-app. + privilegedBecauseOfUid := ctx.ModuleName() == "Bluetooth" + if !privilegedBecauseOfUid { + ctx.PropertyErrorf("privapp_allowlist", "privileged must be set in order to use privapp_allowlist (with a few exceptions)") + } + } + for _, cert := range a.appProperties.Additional_certificates { cert = android.SrcIsModule(cert) if cert != "" { @@ -280,12 +345,52 @@ func (a *AndroidApp) OverridablePropertiesDepsMutator(ctx android.BottomUpMutato } } +// TODO(b/156476221): Remove this allowlist +var ( + missingMinSdkVersionMtsAllowlist = []string{ + "CellBroadcastReceiverGoogleUnitTests", + "CellBroadcastReceiverUnitTests", + "CtsBatterySavingTestCases", + "CtsDeviceAndProfileOwnerApp23", + "CtsDeviceAndProfileOwnerApp30", + "CtsIntentSenderApp", + "CtsJobSchedulerTestCases", + "CtsMimeMapTestCases", + "CtsTareTestCases", + "LibStatsPullTests", + "MediaProviderClientTests", + "TeleServiceTests", + "TestExternalImsServiceApp", + "TestSmsRetrieverApp", + "TetheringPrivilegedTests", + } +) + +func checkMinSdkVersionMts(ctx android.ModuleContext, minSdkVersion android.ApiLevel) { + if includedInMts(ctx.Module()) && !minSdkVersion.Specified() && !android.InList(ctx.ModuleName(), missingMinSdkVersionMtsAllowlist) { + ctx.PropertyErrorf("min_sdk_version", "min_sdk_version is a required property for tests included in MTS") + } +} + func (a *AndroidTestHelperApp) GenerateAndroidBuildActions(ctx android.ModuleContext) { + checkMinSdkVersionMts(ctx, a.MinSdkVersion(ctx)) + applicationId := a.appTestHelperAppProperties.Manifest_values.ApplicationId + if applicationId != nil { + if a.overridableAppProperties.Package_name != nil { + ctx.PropertyErrorf("manifest_values.applicationId", "property is not supported when property package_name is set.") + } + a.aapt.manifestValues.applicationId = *applicationId + } a.generateAndroidBuildActions(ctx) + android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{ + TestOnly: true, + }) + } func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.checkAppSdkVersions(ctx) + a.checkEmbedJnis(ctx) a.generateAndroidBuildActions(ctx) a.generateJavaUsedByApex(ctx) } @@ -295,7 +400,7 @@ func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) { if !a.SdkVersion(ctx).Stable() { ctx.PropertyErrorf("sdk_version", "Updatable apps must use stable SDKs, found %v", a.SdkVersion(ctx)) } - if String(a.deviceProperties.Min_sdk_version) == "" { + if String(a.overridableProperties.Min_sdk_version) == "" { ctx.PropertyErrorf("updatable", "updatable apps must set min_sdk_version.") } @@ -319,6 +424,17 @@ func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) { a.checkSdkVersions(ctx) } +// Ensures that use_embedded_native_libs are set for apk-in-apex +func (a *AndroidApp) checkEmbedJnis(ctx android.BaseModuleContext) { + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) + apkInApex := !apexInfo.IsForPlatform() + hasJnis := len(a.appProperties.Jni_libs) > 0 + + if apkInApex && hasJnis && !Bool(a.appProperties.Use_embedded_native_libs) { + ctx.ModuleErrorf("APK in APEX should have use_embedded_native_libs: true") + } +} + // 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). func (a *AndroidApp) checkJniLibsSdkVersion(ctx android.ModuleContext, minSdkVersion android.ApiLevel) { @@ -349,7 +465,7 @@ func (a *AndroidApp) useEmbeddedNativeLibs(ctx android.ModuleContext) bool { ctx.PropertyErrorf("min_sdk_version", "invalid value %q: %s", a.MinSdkVersion(ctx), err) } - apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) return (minSdkVersion.FinalOrFutureInt() >= 23 && Bool(a.appProperties.Use_embedded_native_libs)) || !apexInfo.IsForPlatform() } @@ -370,13 +486,13 @@ func (a *AndroidApp) shouldUncompressDex(ctx android.ModuleContext) bool { return false } - return shouldUncompressDex(ctx, &a.dexpreopter) + return shouldUncompressDex(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), &a.dexpreopter) } func (a *AndroidApp) shouldEmbedJnis(ctx android.BaseModuleContext) bool { - apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) return ctx.Config().UnbundledBuild() || Bool(a.appProperties.Use_embedded_native_libs) || - !apexInfo.IsForPlatform() || a.appProperties.AlwaysPackageNativeLibs + Bool(a.appProperties.Updatable) || + a.appProperties.AlwaysPackageNativeLibs } func generateAaptRenamePackageFlags(packageName string, renameResourcesPackage bool) []string { @@ -396,6 +512,30 @@ func (a *AndroidApp) renameResourcesPackage() bool { return proptools.BoolDefault(a.overridableAppProperties.Rename_resources_package, true) } +func getAconfigFilePaths(ctx android.ModuleContext) (aconfigTextFilePaths android.Paths) { + ctx.VisitDirectDeps(func(dep android.Module) { + tag := ctx.OtherModuleDependencyTag(dep) + switch tag { + case staticLibTag: + if flagPackages, ok := android.OtherModuleProvider(ctx, dep, FlagsPackagesProvider); ok { + aconfigTextFilePaths = append(aconfigTextFilePaths, flagPackages.AconfigTextFiles...) + } + + case aconfigDeclarationTag: + if provider, ok := android.OtherModuleProvider(ctx, dep, android.AconfigDeclarationsProviderKey); ok { + aconfigTextFilePaths = append(aconfigTextFilePaths, provider.IntermediateDumpOutputPath) + } else { + ctx.ModuleErrorf("Only aconfig_declarations module type is allowed for "+ + "flags_packages property, but %s is not aconfig_declarations module type", + dep.Name(), + ) + } + } + }) + + return android.FirstUniquePaths(aconfigTextFilePaths) +} + func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) { usePlatformAPI := proptools.Bool(a.Module.deviceProperties.Platform_apis) if ctx.Module().(android.SdkContext).SdkVersion(ctx).Kind == android.SdkModule { @@ -409,9 +549,11 @@ func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) { aaptLinkFlags := []string{} // Add TARGET_AAPT_CHARACTERISTICS values to AAPT link flags if they exist and --product flags were not provided. + autogenerateRRO := proptools.Bool(a.appProperties.Generate_product_characteristics_rro) hasProduct := android.PrefixInList(a.aaptProperties.Aaptflags, "--product") - if !hasProduct && len(ctx.Config().ProductAAPTCharacteristics()) > 0 { - aaptLinkFlags = append(aaptLinkFlags, "--product", ctx.Config().ProductAAPTCharacteristics()) + characteristics := ctx.Config().ProductAAPTCharacteristics() + if !autogenerateRRO && !hasProduct && len(characteristics) > 0 && characteristics != "default" { + aaptLinkFlags = append(aaptLinkFlags, "--product", characteristics) } if !Bool(a.aaptProperties.Aapt_include_all_resources) { @@ -441,27 +583,59 @@ func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) { a.aapt.splitNames = a.appProperties.Package_splits a.aapt.LoggingParent = String(a.overridableAppProperties.Logging_parent) if a.Updatable() { - a.aapt.defaultManifestVersion = android.DefaultUpdatableModuleVersion + if override := ctx.Config().Getenv("OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION"); override != "" { + a.aapt.defaultManifestVersion = override + } else { + a.aapt.defaultManifestVersion = android.DefaultUpdatableModuleVersion + } } - a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts, - a.usesLibraryProperties.Exclude_uses_libs, a.enforceDefaultTargetSdkVersion(), aaptLinkFlags...) + + // Use non final ids if we are doing optimized shrinking and are using R8. + nonFinalIds := a.dexProperties.optimizedResourceShrinkingEnabled(ctx) && a.dexer.effectiveOptimizeEnabled() + + aconfigTextFilePaths := getAconfigFilePaths(ctx) + + a.aapt.buildActions(ctx, + aaptBuildActionOptions{ + sdkContext: android.SdkContext(a), + classLoaderContexts: a.classLoaderContexts, + excludedLibs: a.usesLibraryProperties.Exclude_uses_libs, + enforceDefaultTargetSdkVersion: a.enforceDefaultTargetSdkVersion(), + forceNonFinalResourceIDs: nonFinalIds, + extraLinkFlags: aaptLinkFlags, + aconfigTextFiles: aconfigTextFilePaths, + usesLibrary: &a.usesLibrary, + }, + ) // apps manifests are handled by aapt, don't let Module see them a.properties.Manifest = nil + + android.SetProvider(ctx, FlagsPackagesProvider, FlagsPackages{ + AconfigTextFiles: aconfigTextFilePaths, + }) } func (a *AndroidApp) proguardBuildActions(ctx android.ModuleContext) { var staticLibProguardFlagFiles android.Paths ctx.VisitDirectDeps(func(m android.Module) { - if lib, ok := m.(LibraryDependency); ok && ctx.OtherModuleDependencyTag(m) == staticLibTag { - staticLibProguardFlagFiles = append(staticLibProguardFlagFiles, lib.ExportedProguardFlagFiles()...) + depProguardInfo, _ := android.OtherModuleProvider(ctx, m, ProguardSpecInfoProvider) + staticLibProguardFlagFiles = append(staticLibProguardFlagFiles, depProguardInfo.UnconditionallyExportedProguardFlags.ToList()...) + if ctx.OtherModuleDependencyTag(m) == staticLibTag { + staticLibProguardFlagFiles = append(staticLibProguardFlagFiles, depProguardInfo.ProguardFlagsFiles.ToList()...) } }) staticLibProguardFlagFiles = android.FirstUniquePaths(staticLibProguardFlagFiles) - a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, staticLibProguardFlagFiles...) - a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, a.proguardOptionsFile) + a.Module.extraProguardFlagsFiles = append(a.Module.extraProguardFlagsFiles, staticLibProguardFlagFiles...) + if !(a.dexProperties.optimizedResourceShrinkingEnabled(ctx)) { + // When using the optimized shrinking the R8 enqueuer will traverse the xml files that become + // live for code references and (transitively) mark these as live. + // In this case we explicitly don't wan't the aapt2 generated keep files (which would keep the now + // dead code alive) + a.Module.extraProguardFlagsFiles = append(a.Module.extraProguardFlagsFiles, a.proguardOptionsFile) + } } func (a *AndroidApp) installPath(ctx android.ModuleContext) android.InstallPath { @@ -478,7 +652,7 @@ func (a *AndroidApp) installPath(ctx android.ModuleContext) android.InstallPath return android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk") } -func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path { +func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) (android.Path, android.Path) { a.dexpreopter.installPath = a.installPath(ctx) a.dexpreopter.isApp = true if a.dexProperties.Uncompress_dex == nil { @@ -491,11 +665,40 @@ func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path { a.dexpreopter.manifestFile = a.mergedManifestFile a.dexpreopter.preventInstall = a.appProperties.PreventInstall + var packageResources = a.exportPackage + if ctx.ModuleName() != "framework-res" { - a.Module.compile(ctx, a.aaptSrcJar) + if a.dexProperties.resourceShrinkingEnabled(ctx) { + protoFile := android.PathForModuleOut(ctx, packageResources.Base()+".proto.apk") + aapt2Convert(ctx, protoFile, packageResources, "proto") + a.dexer.resourcesInput = android.OptionalPathForPath(protoFile) + } + + var extraSrcJars android.Paths + var extraClasspathJars android.Paths + var extraCombinedJars android.Paths + if a.useResourceProcessorBusyBox(ctx) { + // When building an app with ResourceProcessorBusyBox enabled ResourceProcessorBusyBox has already + // created R.class files that provide IDs for resources in busybox/R.jar. Pass that file in the + // classpath when compiling everything else, and add it to the final classes jar. + extraClasspathJars = android.Paths{a.aapt.rJar} + extraCombinedJars = android.Paths{a.aapt.rJar} + } else { + // When building an app without ResourceProcessorBusyBox the aapt2 rule creates R.srcjar containing + // R.java files for the app's package and the packages from all transitive static android_library + // dependencies. Compile the srcjar alongside the rest of the sources. + extraSrcJars = android.Paths{a.aapt.aaptSrcJar} + } + + a.Module.compile(ctx, extraSrcJars, extraClasspathJars, extraCombinedJars) + if a.dexProperties.resourceShrinkingEnabled(ctx) { + binaryResources := android.PathForModuleOut(ctx, packageResources.Base()+".binary.out.apk") + aapt2Convert(ctx, binaryResources, a.dexer.resourcesOutput.Path(), "binary") + packageResources = binaryResources + } } - return a.dexJarFile.PathOrNil() + return a.dexJarFile.PathOrNil(), packageResources } func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, prebuiltJniPackages android.Paths, ctx android.ModuleContext) android.WritablePath { @@ -591,7 +794,6 @@ func processMainCert(m android.ModuleBase, certPropValue string, certificates [] } } - return mainCertificate, certificates } @@ -599,18 +801,57 @@ func (a *AndroidApp) InstallApkName() string { return a.installApkName } +func (a *AndroidApp) createPrivappAllowlist(ctx android.ModuleContext) android.Path { + if a.appProperties.Privapp_allowlist == nil { + return nil + } + + isOverrideApp := a.GetOverriddenBy() != "" + if !isOverrideApp { + // if this is not an override, we don't need to rewrite the existing privapp allowlist + return android.PathForModuleSrc(ctx, *a.appProperties.Privapp_allowlist) + } + + if a.overridableAppProperties.Package_name == nil { + ctx.PropertyErrorf("privapp_allowlist", "package_name must be set to use privapp_allowlist") + } + + packageName := *a.overridableAppProperties.Package_name + fileName := "privapp_allowlist_" + packageName + ".xml" + outPath := android.PathForModuleOut(ctx, fileName).OutputPath + ctx.Build(pctx, android.BuildParams{ + Rule: modifyAllowlist, + Input: android.PathForModuleSrc(ctx, *a.appProperties.Privapp_allowlist), + Output: outPath, + Args: map[string]string{ + "packageName": packageName, + }, + }) + return &outPath +} + func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { var apkDeps android.Paths - if !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform() { + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) + if !apexInfo.IsForPlatform() { a.hideApexVariantFromMake = true } a.aapt.useEmbeddedNativeLibs = a.useEmbeddedNativeLibs(ctx) a.aapt.useEmbeddedDex = Bool(a.appProperties.Use_embedded_dex) + // Unlike installApkName, a.stem should respect base module name for override_android_app. + // Therefore, use ctx.ModuleName() instead of a.Name(). + a.stem = proptools.StringDefault(a.overridableProperties.Stem, ctx.ModuleName()) + // Check if the install APK name needs to be overridden. - a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Stem()) + // Both android_app and override_android_app module are expected to possess + // its module bound apk path. However, override_android_app inherits ctx.ModuleName() + // from the base module. Therefore, use a.Name() which represents + // the module name for both android_app and override_android_app. + a.installApkName = ctx.DeviceConfig().OverridePackageNameFor( + proptools.StringDefault(a.overridableProperties.Stem, a.Name())) if ctx.ModuleName() == "framework-res" { // framework-res.apk is installed as system/framework/framework-res.apk @@ -625,6 +866,9 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { a.onDeviceDir = android.InstallPathToOnDevicePath(ctx, a.installDir) a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx) + if a.usesLibrary.shouldDisableDexpreopt { + a.dexpreopter.disableDexpreopt() + } var noticeAssetPath android.WritablePath if Bool(a.appProperties.Embed_notices) || ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") { @@ -643,22 +887,13 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { // Process all building blocks, from AAPT to certificates. a.aaptBuildActions(ctx) - // The decision to enforce <uses-library> checks is made before adding implicit SDK libraries. a.usesLibrary.freezeEnforceUsesLibraries() - // Add implicit SDK libraries to <uses-library> list. - 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. if a.usesLibrary.enforceUsesLibraries() { - manifestCheckFile := a.usesLibrary.verifyUsesLibrariesManifest(ctx, a.mergedManifestFile) + manifestCheckFile := a.usesLibrary.verifyUsesLibrariesManifest( + ctx, a.mergedManifestFile, &a.classLoaderContexts) apkDeps = append(apkDeps, manifestCheckFile) } @@ -669,9 +904,11 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { a.linter.resources = a.aapt.resourceFiles a.linter.buildModuleReportZip = ctx.Config().UnbundledBuildApps() - dexJarFile := a.dexBuildActions(ctx) + dexJarFile, packageResources := a.dexBuildActions(ctx) - jniLibs, prebuiltJniPackages, certificates := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis)) + // No need to check the SDK version of the JNI deps unless we embed them + checkNativeSdkVersion := a.shouldEmbedJnis(ctx) && !Bool(a.appProperties.Jni_uses_platform_apis) + jniLibs, prebuiltJniPackages, certificates := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), checkNativeSdkVersion) jniJarFile := a.jniBuildActions(jniLibs, prebuiltJniPackages, ctx) if ctx.Failed() { @@ -693,7 +930,7 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { } rotationMinSdkVersion := String(a.overridableAppProperties.RotationMinSdkVersion) - CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion, Bool(a.dexProperties.Optimize.Shrink_resources)) + CreateAndSignAppPackage(ctx, packageFile, packageResources, jniJarFile, dexJarFile, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion) a.outputFile = packageFile if v4SigningRequested { a.extraOutputFiles = append(a.extraOutputFiles, v4SignatureFile) @@ -722,7 +959,7 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { if v4SigningRequested { v4SignatureFile = android.PathForModuleOut(ctx, a.installApkName+"_"+split.suffix+".apk.idsig") } - CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates, apkDeps, v4SignatureFile, lineageFile, rotationMinSdkVersion, false) + 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) @@ -734,21 +971,68 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { BuildBundleModule(ctx, bundleFile, a.exportPackage, jniJarFile, dexJarFile) a.bundleFile = bundleFile - apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + allowlist := a.createPrivappAllowlist(ctx) + if allowlist != nil { + a.privAppAllowlist = android.OptionalPathForPath(allowlist) + } // Install the app package. - if (Bool(a.Module.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() && - !a.appProperties.PreventInstall { + shouldInstallAppPackage := (Bool(a.Module.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() && !a.appProperties.PreventInstall + if shouldInstallAppPackage { + if a.privAppAllowlist.Valid() { + allowlistInstallPath := android.PathForModuleInstall(ctx, "etc", "permissions") + allowlistInstallFilename := a.installApkName + ".xml" + ctx.InstallFile(allowlistInstallPath, allowlistInstallFilename, a.privAppAllowlist.Path()) + } - var extraInstalledPaths android.Paths + var extraInstalledPaths android.InstallPaths for _, extra := range a.extraOutputFiles { installed := ctx.InstallFile(a.installDir, extra.Base(), extra) extraInstalledPaths = append(extraInstalledPaths, installed) } + // If we don't embed jni libs, make sure that those are installed along with the + // app, and also place symlinks to the installed paths under the lib/<arch> + // directory of the app installation directory. ex: + // /system/app/MyApp/lib/arm64/libfoo.so -> /system/lib64/libfoo.so + if !a.embeddedJniLibs { + for _, jniLib := range jniLibs { + archStr := jniLib.target.Arch.ArchType.String() + symlinkDir := a.installDir.Join(ctx, "lib", archStr) + for _, installedLib := range jniLib.installPaths { + // install the symlink itself + symlinkName := installedLib.Base() + symlinkTarget := android.InstallPathToOnDevicePath(ctx, installedLib) + ctx.InstallAbsoluteSymlink(symlinkDir, symlinkName, symlinkTarget) + } + } + } ctx.InstallFile(a.installDir, a.outputFile.Base(), a.outputFile, extraInstalledPaths...) } a.buildAppDependencyInfo(ctx) + + providePrebuiltInfo(ctx, + prebuiltInfoProps{ + baseModuleName: a.BaseModuleName(), + isPrebuilt: false, + }, + ) + + a.setOutputFiles(ctx) +} + +func (a *AndroidApp) setOutputFiles(ctx android.ModuleContext) { + ctx.SetOutputFiles([]android.Path{a.proguardOptionsFile}, ".aapt.proguardOptionsFile") + if a.aaptSrcJar != nil { + ctx.SetOutputFiles([]android.Path{a.aaptSrcJar}, ".aapt.srcjar") + } + if a.rJar != nil { + ctx.SetOutputFiles([]android.Path{a.rJar}, ".aapt.jar") + } + ctx.SetOutputFiles([]android.Path{a.outputFile}, ".apk") + ctx.SetOutputFiles([]android.Path{a.exportPackage}, ".export-package.apk") + ctx.SetOutputFiles([]android.Path{a.aapt.manifestPath}, ".manifest.xml") + setOutputFiles(ctx, a.Library.Module) } type appDepsInterface interface { @@ -761,15 +1045,39 @@ func collectAppDeps(ctx android.ModuleContext, app appDepsInterface, shouldCollectRecursiveNativeDeps bool, checkNativeSdkVersion bool) ([]jniLib, android.Paths, []Certificate) { - var jniLibs []jniLib - var prebuiltJniPackages android.Paths - var certificates []Certificate - seenModulePaths := make(map[string]bool) - if checkNativeSdkVersion { checkNativeSdkVersion = app.SdkVersion(ctx).Specified() && app.SdkVersion(ctx).Kind != android.SdkCorePlatform && !app.RequiresStableAPIs(ctx) } + jniLib, prebuiltJniPackages := collectJniDeps(ctx, shouldCollectRecursiveNativeDeps, + checkNativeSdkVersion, func(dep cc.LinkableInterface) bool { + return !dep.IsNdk(ctx.Config()) && !dep.IsStubs() + }) + + var certificates []Certificate + + ctx.VisitDirectDeps(func(module android.Module) { + otherName := ctx.OtherModuleName(module) + tag := ctx.OtherModuleDependencyTag(module) + + if tag == certificateTag { + if dep, ok := module.(*AndroidAppCertificate); ok { + certificates = append(certificates, dep.Certificate) + } else { + ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", otherName) + } + } + }) + return jniLib, prebuiltJniPackages, certificates +} + +func collectJniDeps(ctx android.ModuleContext, + shouldCollectRecursiveNativeDeps bool, + checkNativeSdkVersion bool, + filter func(cc.LinkableInterface) bool) ([]jniLib, android.Paths) { + var jniLibs []jniLib + var prebuiltJniPackages android.Paths + seenModulePaths := make(map[string]bool) ctx.WalkDeps(func(module android.Module, parent android.Module) bool { otherName := ctx.OtherModuleName(module) @@ -777,7 +1085,7 @@ func collectAppDeps(ctx android.ModuleContext, app appDepsInterface, if IsJniDepTag(tag) || cc.IsSharedDepTag(tag) { if dep, ok := module.(cc.LinkableInterface); ok { - if dep.IsNdk(ctx.Config()) || dep.IsStubs() { + if filter != nil && !filter(dep) { return false } @@ -801,6 +1109,7 @@ func collectAppDeps(ctx android.ModuleContext, app appDepsInterface, coverageFile: dep.CoverageOutputFile(), unstrippedFile: dep.UnstrippedOutputFile(), partition: dep.Partition(), + installPaths: dep.FilesToInstall(), }) } else if ctx.Config().AllowMissingDependencies() { ctx.AddMissingDependencies([]string{otherName}) @@ -814,22 +1123,14 @@ func collectAppDeps(ctx android.ModuleContext, app appDepsInterface, return shouldCollectRecursiveNativeDeps } - if info, ok := ctx.OtherModuleProvider(module, JniPackageProvider).(JniPackageInfo); ok { + if info, ok := android.OtherModuleProvider(ctx, module, JniPackageProvider); ok { prebuiltJniPackages = append(prebuiltJniPackages, info.JniPackages...) } - if tag == certificateTag { - if dep, ok := module.(*AndroidAppCertificate); ok { - certificates = append(certificates, dep.Certificate) - } else { - ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", otherName) - } - } - return false }) - return jniLibs, prebuiltJniPackages, certificates + return jniLibs, prebuiltJniPackages } func (a *AndroidApp) WalkPayloadDeps(ctx android.ModuleContext, do android.PayloadDepsCallback) { @@ -922,22 +1223,11 @@ func (a *AndroidApp) DepIsInSameApex(ctx android.BaseModuleContext, dep android. return a.Library.DepIsInSameApex(ctx, dep) } -// For OutputFileProducer interface -func (a *AndroidApp) OutputFiles(tag string) (android.Paths, error) { - switch tag { - case ".aapt.srcjar": - return []android.Path{a.aaptSrcJar}, nil - case ".export-package.apk": - return []android.Path{a.exportPackage}, nil - } - return a.Library.OutputFiles(tag) -} - func (a *AndroidApp) Privileged() bool { return Bool(a.appProperties.Privileged) } -func (a *AndroidApp) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool { +func (a *AndroidApp) IsNativeCoverageNeeded(ctx cc.IsNativeCoverageNeededContext) bool { return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled() } @@ -953,12 +1243,26 @@ func (a *AndroidApp) EnableCoverageIfNeeded() {} var _ cc.Coverage = (*AndroidApp)(nil) +func (a *AndroidApp) IDEInfo(dpInfo *android.IdeInfo) { + a.Library.IDEInfo(dpInfo) + a.aapt.IDEInfo(dpInfo) +} + +func (a *AndroidApp) productCharacteristicsRROPackageName() string { + return proptools.String(a.appProperties.ProductCharacteristicsRROPackageName) +} + +func (a *AndroidApp) productCharacteristicsRROManifestModuleName() string { + return proptools.String(a.appProperties.ProductCharacteristicsRROManifestModuleName) +} + // android_app compiles sources and Android resources into an Android application package `.apk` file. func AndroidAppFactory() android.Module { module := &AndroidApp{} module.Module.dexProperties.Optimize.EnabledByDefault = true module.Module.dexProperties.Optimize.Shrink = proptools.BoolPtr(true) + module.Module.dexProperties.Optimize.Proguard_compatibility = proptools.BoolPtr(false) module.Module.properties.Instrument = true module.Module.properties.Supports_static_instrumentation = true @@ -968,7 +1272,8 @@ func AndroidAppFactory() android.Module { module.AddProperties( &module.aaptProperties, &module.appProperties, - &module.overridableAppProperties) + &module.overridableAppProperties, + &module.Library.sourceProperties) module.usesLibrary.enforce = true @@ -976,11 +1281,72 @@ func AndroidAppFactory() android.Module { android.InitDefaultableModule(module) android.InitOverridableModule(module, &module.overridableAppProperties.Overrides) android.InitApexModule(module) - android.InitBazelModule(module) + + android.AddLoadHook(module, func(ctx android.LoadHookContext) { + a := ctx.Module().(*AndroidApp) + + characteristics := ctx.Config().ProductAAPTCharacteristics() + if characteristics == "default" || characteristics == "" { + module.appProperties.Generate_product_characteristics_rro = nil + // no need to create RRO + return + } + + if !proptools.Bool(module.appProperties.Generate_product_characteristics_rro) { + return + } + + rroPackageName := a.Name() + "__" + strings.ReplaceAll(characteristics, ",", "_") + "__auto_generated_characteristics_rro" + rroManifestName := rroPackageName + "_manifest" + + a.appProperties.ProductCharacteristicsRROPackageName = proptools.StringPtr(rroPackageName) + a.appProperties.ProductCharacteristicsRROManifestModuleName = proptools.StringPtr(rroManifestName) + + rroManifestProperties := struct { + Name *string + Tools []string + Out []string + Srcs []string + Cmd *string + }{ + Name: proptools.StringPtr(rroManifestName), + Tools: []string{"characteristics_rro_generator", "aapt2"}, + Out: []string{"AndroidManifest.xml"}, + Srcs: []string{":" + a.Name() + "{.apk}"}, + Cmd: proptools.StringPtr("$(location characteristics_rro_generator) $$($(location aapt2) dump packagename $(in)) $(out)"), + } + ctx.CreateModule(genrule.GenRuleFactory, &rroManifestProperties) + + rroProperties := struct { + Name *string + Filter_product *string + Aaptflags []string + Manifest *string + Resource_dirs []string + }{ + Name: proptools.StringPtr(rroPackageName), + Filter_product: proptools.StringPtr(characteristics), + Aaptflags: []string{"--auto-add-overlay"}, + Manifest: proptools.StringPtr(":" + rroManifestName), + Resource_dirs: a.aaptProperties.Resource_dirs, + } + if !Bool(a.aaptProperties.Aapt_include_all_resources) { + for _, aaptConfig := range ctx.Config().ProductAAPTConfig() { + rroProperties.Aaptflags = append(rroProperties.Aaptflags, "-c", aaptConfig) + } + } + ctx.CreateModule(RuntimeResourceOverlayFactory, &rroProperties) + }) return module } +// A dictionary of values to be overridden in the manifest. +type Manifest_values struct { + // Overrides the value of package_name in the manifest + ApplicationId *string +} + type appTestProperties struct { // The name of the android_app module that the tests will run against. Instrumentation_for *string @@ -990,6 +1356,8 @@ type appTestProperties struct { // If specified, the mainline module package name in the test config is overwritten by it. Mainline_package_name *string + + Manifest_values Manifest_values } type AndroidTest struct { @@ -1021,6 +1389,7 @@ func (a *AndroidTestHelperApp) includedInTestSuite(searchPrefix string) bool { } func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) { + checkMinSdkVersionMts(ctx, a.MinSdkVersion(ctx)) var configs []tradefed.Config if a.appTestProperties.Instrumentation_target_package != nil { a.additionalAaptFlags = append(a.additionalAaptFlags, @@ -1032,6 +1401,13 @@ func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.additionalAaptFlags = append(a.additionalAaptFlags, "--rename-instrumentation-target-package "+manifestPackageName) } } + applicationId := a.appTestProperties.Manifest_values.ApplicationId + if applicationId != nil { + if a.overridableAppProperties.Package_name != nil { + ctx.PropertyErrorf("manifest_values.applicationId", "property is not supported when property package_name is set.") + } + a.aapt.manifestValues.applicationId = *applicationId + } a.generateAndroidBuildActions(ctx) for _, module := range a.testProperties.Test_mainline_modules { @@ -1043,6 +1419,22 @@ func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.testConfig = a.FixTestConfig(ctx, testConfig) a.extraTestConfigs = android.PathsForModuleSrc(ctx, a.testProperties.Test_options.Extra_test_configs) a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data) + android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{}) + android.SetProvider(ctx, tradefed.BaseTestProviderKey, tradefed.BaseTestProviderData{ + InstalledFiles: a.data, + OutputFile: a.OutputFile(), + TestConfig: a.testConfig, + HostRequiredModuleNames: a.HostRequiredModuleNames(), + TestSuites: a.testProperties.Test_suites, + IsHost: false, + LocalCertificate: a.certificate.AndroidMkString(), + IsUnitTest: Bool(a.testProperties.Test_options.Unit_test), + }) + android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{ + TestOnly: true, + TopLevelTarget: true, + }) + } func (a *AndroidTest) FixTestConfig(ctx android.ModuleContext, testConfig android.Path) android.Path { @@ -1119,6 +1511,7 @@ func AndroidTestFactory() android.Module { android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) android.InitDefaultableModule(module) android.InitOverridableModule(module, &module.overridableAppProperties.Overrides) + return module } @@ -1134,6 +1527,8 @@ type appTestHelperAppProperties struct { // Install the test into a folder named for the module in all test suites. Per_testcase_directory *bool + + Manifest_values Manifest_values } type AndroidTestHelperApp struct { @@ -1176,7 +1571,6 @@ func AndroidTestHelperAppFactory() android.Module { type AndroidAppCertificate struct { android.ModuleBase - android.BazelModuleBase properties AndroidAppCertificateProperties Certificate Certificate @@ -1193,7 +1587,6 @@ func AndroidAppCertificateFactory() android.Module { module := &AndroidAppCertificate{} module.AddProperties(&module.properties) android.InitAndroidModule(module) - android.InitBazelModule(module) return module } @@ -1220,7 +1613,7 @@ func (i *OverrideAndroidApp) GenerateAndroidBuildActions(_ android.ModuleContext func OverrideAndroidAppModuleFactory() android.Module { m := &OverrideAndroidApp{} m.AddProperties( - &OverridableDeviceProperties{}, + &OverridableProperties{}, &overridableAppProperties{}, ) @@ -1234,9 +1627,13 @@ type OverrideAndroidTest struct { android.OverrideModuleBase } -func (i *OverrideAndroidTest) GenerateAndroidBuildActions(_ android.ModuleContext) { +func (i *OverrideAndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) { // All the overrides happen in the base module. // TODO(jungjw): Check the base module type. + android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{ + TestOnly: true, + TopLevelTarget: true, + }) } // override_android_test is used to create an android_app module based on another android_test by overriding @@ -1284,6 +1681,9 @@ type UsesLibraryProperties struct { // 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 + + // The module names of optional uses-library libraries that are missing from the source tree. + Missing_optional_uses_libs []string `blueprint:"mutated"` } // usesLibrary provides properties and helper functions for AndroidApp and AndroidAppImport to verify that the @@ -1295,22 +1695,16 @@ type usesLibrary struct { // Whether to enforce verify_uses_library check. enforce bool -} -func (u *usesLibrary) addLib(lib string, optional bool) { - if !android.InList(lib, u.usesLibraryProperties.Uses_libs) && !android.InList(lib, u.usesLibraryProperties.Optional_uses_libs) { - if optional { - u.usesLibraryProperties.Optional_uses_libs = append(u.usesLibraryProperties.Optional_uses_libs, lib) - } else { - u.usesLibraryProperties.Uses_libs = append(u.usesLibraryProperties.Uses_libs, lib) - } - } + // Whether dexpreopt should be disabled + shouldDisableDexpreopt bool } func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, addCompatDeps bool) { if !ctx.Config().UnbundledBuild() || ctx.Config().UnbundledBuildImage() { ctx.AddVariationDependencies(nil, usesLibReqTag, u.usesLibraryProperties.Uses_libs...) - ctx.AddVariationDependencies(nil, usesLibOptTag, u.presentOptionalUsesLibs(ctx)...) + presentOptionalUsesLibs := u.presentOptionalUsesLibs(ctx) + ctx.AddVariationDependencies(nil, usesLibOptTag, presentOptionalUsesLibs...) // Only add these extra dependencies if the module is an app that depends on framework // libs. This avoids creating a cyclic dependency: // e.g. framework-res -> org.apache.http.legacy -> ... -> framework-res. @@ -1321,26 +1715,24 @@ func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, addCompatDeps boo ctx.AddVariationDependencies(nil, usesLibCompat28OptTag, dexpreopt.OptionalCompatUsesLibs28...) ctx.AddVariationDependencies(nil, usesLibCompat30OptTag, dexpreopt.OptionalCompatUsesLibs30...) } + _, diff, _ := android.ListSetDifference(u.usesLibraryProperties.Optional_uses_libs, presentOptionalUsesLibs) + u.usesLibraryProperties.Missing_optional_uses_libs = diff } else { ctx.AddVariationDependencies(nil, r8LibraryJarTag, u.usesLibraryProperties.Uses_libs...) ctx.AddVariationDependencies(nil, r8LibraryJarTag, u.presentOptionalUsesLibs(ctx)...) } } -// presentOptionalUsesLibs returns optional_uses_libs after filtering out MissingUsesLibraries, which don't exist in the -// build. +// presentOptionalUsesLibs returns optional_uses_libs after filtering out libraries that don't exist in the source tree. func (u *usesLibrary) presentOptionalUsesLibs(ctx android.BaseModuleContext) []string { - optionalUsesLibs, _ := android.FilterList(u.usesLibraryProperties.Optional_uses_libs, ctx.Config().MissingUsesLibraries()) - return optionalUsesLibs -} - -// Helper function to replace string in a list. -func replaceInList(list []string, oldstr, newstr string) { - for i, str := range list { - if str == oldstr { - list[i] = newstr + optionalUsesLibs := android.FilterListPred(u.usesLibraryProperties.Optional_uses_libs, func(s string) bool { + exists := ctx.OtherModuleExists(s) + if !exists && !android.InList(ctx.ModuleName(), ctx.Config().BuildWarningBadOptionalUsesLibsAllowlist()) { + fmt.Printf("Warning: Module '%s' depends on non-existing optional_uses_libs '%s'\n", ctx.ModuleName(), s) } - } + return exists + }) + return optionalUsesLibs } // Returns a map of module names of shared library dependencies to the paths to their dex jars on @@ -1371,18 +1763,22 @@ func (u *usesLibrary) classLoaderContextForUsesLibDeps(ctx android.ModuleContext } } + // Skip java_sdk_library dependencies that provide stubs, but not an implementation. + // This will be restricted to optional_uses_libs + if sdklib, ok := m.(SdkLibraryDependency); ok { + if tag == usesLibOptTag && sdklib.DexJarBuildPath(ctx).PathOrNil() == nil { + u.shouldDisableDexpreopt = true + 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, - lib.DexJarBuildPath().PathOrNil(), lib.DexJarInstallPath(), + lib.DexJarBuildPath(ctx).PathOrNil(), lib.DexJarInstallPath(), lib.ClassLoaderContexts()) } else if ctx.Config().AllowMissingDependencies() { ctx.AddMissingDependencies([]string{dep}) @@ -1413,7 +1809,7 @@ func (u *usesLibrary) freezeEnforceUsesLibraries() { // an APK with the manifest embedded in it (manifest_check will know which one it is by the file // extension: APKs are supposed to end with '.apk'). func (u *usesLibrary) verifyUsesLibraries(ctx android.ModuleContext, inputFile android.Path, - outputFile android.WritablePath) android.Path { + outputFile android.WritablePath, classLoaderContexts *dexpreopt.ClassLoaderContextMap) android.Path { statusFile := dexpreopt.UsesLibrariesStatusFile(ctx) @@ -1422,7 +1818,7 @@ func (u *usesLibrary) verifyUsesLibraries(ctx android.ModuleContext, inputFile a // non-linux build platforms where dexpreopt is generally disabled (the check may fail due to // various unrelated reasons, such as a failure to get manifest from an APK). global := dexpreopt.GetGlobalConfig(ctx) - if global.DisablePreopt || global.OnlyPreoptBootImageAndSystemServer { + if global.DisablePreopt || global.OnlyPreoptArtBootImage { return inputFile } @@ -1441,140 +1837,37 @@ func (u *usesLibrary) verifyUsesLibraries(ctx android.ModuleContext, inputFile a cmd.Flag("--enforce-uses-libraries-relax") } - for _, lib := range u.usesLibraryProperties.Uses_libs { + requiredUsesLibs, optionalUsesLibs := classLoaderContexts.UsesLibs() + for _, lib := range requiredUsesLibs { cmd.FlagWithArg("--uses-library ", lib) } - - for _, lib := range u.usesLibraryProperties.Optional_uses_libs { + for _, lib := range optionalUsesLibs { cmd.FlagWithArg("--optional-uses-library ", lib) } + // Also add missing optional uses libs, as the manifest check expects them. + // Note that what we add here are the module names of those missing libs, not library names, while + // the manifest check actually expects library names. However, the case where a library is missing + // and the module name != the library name is too rare for us to handle. + for _, lib := range u.usesLibraryProperties.Missing_optional_uses_libs { + cmd.FlagWithArg("--missing-optional-uses-library ", lib) + } + rule.Build("verify_uses_libraries", "verify <uses-library>") return outputFile } // verifyUsesLibrariesManifest checks the <uses-library> tags in an AndroidManifest.xml against // the build system and returns the path to a copy of the manifest. -func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path) android.Path { +func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path, + classLoaderContexts *dexpreopt.ClassLoaderContextMap) android.Path { outputFile := android.PathForModuleOut(ctx, "manifest_check", "AndroidManifest.xml") - return u.verifyUsesLibraries(ctx, manifest, outputFile) + return u.verifyUsesLibraries(ctx, manifest, outputFile, classLoaderContexts) } // verifyUsesLibrariesAPK checks the <uses-library> tags in the manifest of an APK against the build // system and returns the path to a copy of the APK. -func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk android.Path) android.Path { - u.verifyUsesLibraries(ctx, apk, nil) // for APKs manifest_check does not write output file - 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:rules.bzl", - } - - ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs) -} - -type manifestValueAttribute struct { - MinSdkVersion *string -} - -type bazelAndroidAppAttributes struct { - *javaCommonAttributes - *bazelAapt - Deps bazel.LabelListAttribute - Custom_package *string - Certificate bazel.LabelAttribute - Certificate_name bazel.StringAttribute - Manifest_values *manifestValueAttribute -} - -// ConvertWithBp2build is used to convert android_app to Bazel. -func (a *AndroidApp) ConvertWithBp2build(ctx android.TopDownMutatorContext) { - commonAttrs, bp2BuildInfo := a.convertLibraryAttrsBp2Build(ctx) - depLabels := bp2BuildInfo.DepLabels - - deps := depLabels.Deps - deps.Append(depLabels.StaticDeps) - - aapt := a.convertAaptAttrsWithBp2Build(ctx) - - certificate, certificateName := android.BazelStringOrLabelFromProp(ctx, a.overridableAppProperties.Certificate) - - manifestValues := &manifestValueAttribute{} - // TODO(b/274474008 ): Directly convert deviceProperties.Min_sdk_version in bp2build - // MinSdkVersion(ctx) calls SdkVersion(ctx) if no value for min_sdk_version is set - minSdkVersion := a.MinSdkVersion(ctx) - if !minSdkVersion.IsPreview() && !minSdkVersion.IsInvalid() { - minSdkStr, err := minSdkVersion.EffectiveVersionString(ctx) - if err == nil { - manifestValues.MinSdkVersion = &minSdkStr - } - } - - appAttrs := &bazelAndroidAppAttributes{ - // TODO(b/209576404): handle package name override by product variable PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES - Custom_package: a.overridableAppProperties.Package_name, - Certificate: certificate, - Certificate_name: certificateName, - Manifest_values: manifestValues, - } - - props := bazel.BazelTargetModuleProperties{ - Rule_class: "android_binary", - Bzl_load_location: "//build/bazel/rules/android:rules.bzl", - } - - if !bp2BuildInfo.hasKotlin { - appAttrs.javaCommonAttributes = commonAttrs - appAttrs.bazelAapt = aapt - appAttrs.Deps = deps - } else { - ktName := a.Name() + "_kt" - ctx.CreateBazelTargetModule( - AndroidLibraryBazelTargetModuleProperties(), - android.CommonAttributes{Name: ktName}, - &bazelAndroidLibrary{ - javaLibraryAttributes: &javaLibraryAttributes{ - javaCommonAttributes: commonAttrs, - Deps: deps, - }, - bazelAapt: aapt, - }, - ) - - appAttrs.bazelAapt = &bazelAapt{Manifest: aapt.Manifest} - appAttrs.Deps = bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + ktName}) - appAttrs.javaCommonAttributes = &javaCommonAttributes{ - Sdk_version: commonAttrs.Sdk_version, - } - } - - ctx.CreateBazelTargetModule( - props, - android.CommonAttributes{Name: a.Name()}, - appAttrs, - ) - +func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk android.Path, + classLoaderContexts *dexpreopt.ClassLoaderContextMap) { + u.verifyUsesLibraries(ctx, apk, nil, classLoaderContexts) // for APKs manifest_check does not write output file } diff --git a/java/app_builder.go b/java/app_builder.go index d20a6bfe4..943ce317b 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, rotationMinSdkVersion string, shrinkResources bool) { + 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) @@ -71,12 +71,6 @@ func CreateAndSignAppPackage(ctx android.ModuleContext, outputFile android.Writa Output: unsignedApk, Implicits: deps, }) - - if shrinkResources { - shrunkenApk := android.PathForModuleOut(ctx, "resource-shrunken", unsignedApk.Base()) - ShrinkResources(ctx, unsignedApk, shrunkenApk) - unsignedApk = shrunkenApk - } SignAppPackage(ctx, outputFile, unsignedApk, certificates, v4SignatureFile, lineageFile, rotationMinSdkVersion) } @@ -225,8 +219,6 @@ func BuildBundleModule(ctx android.ModuleContext, outputFile android.WritablePat }) } -const jniJarOutputPathString = "jniJarOutput.zip" - func TransformJniLibsToJar( ctx android.ModuleContext, outputFile android.WritablePath, @@ -258,7 +250,10 @@ func TransformJniLibsToJar( rule = zipRE args["implicits"] = strings.Join(deps.Strings(), ",") } - jniJarPath := android.PathForModuleOut(ctx, jniJarOutputPathString) + var jniJarPath android.WritablePath = android.PathForModuleOut(ctx, "jniJarOutput.zip") + if len(prebuiltJniPackages) == 0 { + jniJarPath = outputFile + } ctx.Build(pctx, android.BuildParams{ Rule: rule, Description: "zip jni libs", @@ -266,12 +261,26 @@ func TransformJniLibsToJar( Implicits: deps, Args: args, }) - ctx.Build(pctx, android.BuildParams{ - Rule: mergeAssetsRule, - Description: "merge prebuilt JNI packages", - Inputs: append(prebuiltJniPackages, jniJarPath), - Output: outputFile, - }) + if len(prebuiltJniPackages) > 0 { + var mergeJniJarPath android.WritablePath = android.PathForModuleOut(ctx, "mergeJniJarOutput.zip") + if !uncompressJNI { + mergeJniJarPath = outputFile + } + ctx.Build(pctx, android.BuildParams{ + Rule: mergeAssetsRule, + Description: "merge prebuilt JNI packages", + Inputs: append(prebuiltJniPackages, jniJarPath), + Output: mergeJniJarPath, + }) + + if uncompressJNI { + ctx.Build(pctx, android.BuildParams{ + Rule: uncompressEmbeddedJniLibsRule, + Input: mergeJniJarPath, + Output: outputFile, + }) + } + } } func (a *AndroidApp) generateJavaUsedByApex(ctx android.ModuleContext) { diff --git a/java/app_import.go b/java/app_import.go index 85b35ebaa..fa87997cf 100644 --- a/java/app_import.go +++ b/java/app_import.go @@ -18,6 +18,7 @@ package java import ( "reflect" + "strings" "github.com/google/blueprint" @@ -49,6 +50,12 @@ var ( CommandDeps: []string{"${config.Zip2ZipCmd}"}, Description: "Uncompress dex files", }) + + checkPresignedApkRule = pctx.AndroidStaticRule("check-presigned-apk", blueprint.RuleParams{ + Command: "build/soong/scripts/check_prebuilt_presigned_apk.py --aapt2 ${config.Aapt2Cmd} --zipalign ${config.ZipAlign} $extraArgs $in $out", + CommandDeps: []string{"build/soong/scripts/check_prebuilt_presigned_apk.py", "${config.Aapt2Cmd}", "${config.ZipAlign}"}, + Description: "Check presigned apk", + }, "extraArgs") ) func RegisterAppImportBuildComponents(ctx android.RegistrationContext) { @@ -62,9 +69,10 @@ type AndroidAppImport struct { android.ApexModuleBase prebuilt android.Prebuilt - properties AndroidAppImportProperties - dpiVariants interface{} - archVariants interface{} + properties AndroidAppImportProperties + dpiVariants interface{} + archVariants interface{} + arch_dpiVariants interface{} outputFile android.Path certificate Certificate @@ -73,8 +81,6 @@ type AndroidAppImport struct { usesLibrary usesLibrary - preprocessed bool - installPath android.InstallPath hideApexVariantFromMake bool @@ -128,6 +134,23 @@ type AndroidAppImportProperties struct { // Optional. Install to a subdirectory of the default install path for the module Relative_install_path *string + + // Whether the prebuilt apk can be installed without additional processing. Default is false. + Preprocessed *bool + + // Whether or not to skip checking the preprocessed apk for proper alignment and uncompressed + // JNI libs and dex files. Default is false + Skip_preprocessed_apk_checks *bool + + // Name of the source soong module that gets shadowed by this prebuilt + // If unspecified, follows the naming convention that the source module of + // the prebuilt is Name() without "prebuilt_" prefix + Source_module_name *string + + // Path to the .prebuilt_info file of the prebuilt app. + // In case of mainline modules, the .prebuilt_info file contains the build_id that was used + // to generate the prebuilt. + Prebuilt_info *string `android:"path"` } func (a *AndroidAppImport) IsInstallable() bool { @@ -135,10 +158,12 @@ func (a *AndroidAppImport) IsInstallable() bool { } // Updates properties with variant-specific values. -func (a *AndroidAppImport) processVariants(ctx android.LoadHookContext) { +// This happens as a DefaultableHook instead of a LoadHook because we want to run it after +// soong config variables are applied. +func (a *AndroidAppImport) processVariants(ctx android.DefaultableHookContext) { config := ctx.Config() + dpiProps := reflect.ValueOf(a.dpiVariants).Elem().FieldByName(DpiGroupName) - dpiProps := reflect.ValueOf(a.dpiVariants).Elem().FieldByName("Dpi_variants") // Try DPI variant matches in the reverse-priority order so that the highest priority match // overwrites everything else. // TODO(jungjw): Can we optimize this by making it priority order? @@ -148,11 +173,27 @@ func (a *AndroidAppImport) processVariants(ctx android.LoadHookContext) { if config.ProductAAPTPreferredConfig() != "" { MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPreferredConfig()) } - - archProps := reflect.ValueOf(a.archVariants).Elem().FieldByName("Arch") + archProps := reflect.ValueOf(a.archVariants).Elem().FieldByName(ArchGroupName) archType := ctx.Config().AndroidFirstDeviceTarget.Arch.ArchType MergePropertiesFromVariant(ctx, &a.properties, archProps, archType.Name) + // Process "arch" includes "dpi_variants" + archStructPtr := reflect.ValueOf(a.arch_dpiVariants).Elem().FieldByName(ArchGroupName) + if archStruct := archStructPtr.Elem(); archStruct.IsValid() { + archPartPropsPtr := archStruct.FieldByName(proptools.FieldNameForProperty(archType.Name)) + if archPartProps := archPartPropsPtr.Elem(); archPartProps.IsValid() { + archDpiPropsPtr := archPartProps.FieldByName(DpiGroupName) + if archDpiProps := archDpiPropsPtr.Elem(); archDpiProps.IsValid() { + for i := len(config.ProductAAPTPrebuiltDPI()) - 1; i >= 0; i-- { + MergePropertiesFromVariant(ctx, &a.properties, archDpiProps, config.ProductAAPTPrebuiltDPI()[i]) + } + if config.ProductAAPTPreferredConfig() != "" { + MergePropertiesFromVariant(ctx, &a.properties, archDpiProps, config.ProductAAPTPreferredConfig()) + } + } + } + } + if String(a.properties.Apk) == "" { // Disable this module since the apk property is still empty after processing all matching // variants. This likely means there is no matching variant, and the default variant doesn't @@ -201,7 +242,7 @@ func (a *AndroidAppImport) uncompressEmbeddedJniLibs( ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) { // Test apps don't need their JNI libraries stored uncompressed. As a matter of fact, messing // with them may invalidate pre-existing signature data. - if ctx.InstallInTestcases() && (Bool(a.properties.Presigned) || a.preprocessed) { + if ctx.InstallInTestcases() && (Bool(a.properties.Presigned) || Bool(a.properties.Preprocessed)) { ctx.Build(pctx, android.BuildParams{ Rule: android.Cp, Output: outputPath, @@ -219,7 +260,7 @@ func (a *AndroidAppImport) uncompressEmbeddedJniLibs( // Returns whether this module should have the dex file stored uncompressed in the APK. func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool { - if ctx.Config().UnbundledBuild() || a.preprocessed { + if ctx.Config().UnbundledBuild() || proptools.Bool(a.properties.Preprocessed) { return false } @@ -228,7 +269,7 @@ func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool { return ctx.Config().UncompressPrivAppDex() } - return shouldUncompressDex(ctx, &a.dexpreopter) + return shouldUncompressDex(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), &a.dexpreopter) } func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -239,16 +280,28 @@ func (a *AndroidAppImport) InstallApkName() string { return a.BaseModuleName() } +func (a *AndroidAppImport) BaseModuleName() string { + return proptools.StringDefault(a.properties.Source_module_name, a.ModuleBase.Name()) +} + func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext) { if a.Name() == "prebuilt_framework-res" { ctx.ModuleErrorf("prebuilt_framework-res found. This used to have special handling in soong, but was removed due to prebuilt_framework-res no longer existing. This check is to ensure it doesn't come back without readding the special handling.") } - apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) if !apexInfo.IsForPlatform() { a.hideApexVariantFromMake = true } + if Bool(a.properties.Preprocessed) { + if a.properties.Presigned != nil && !*a.properties.Presigned { + ctx.ModuleErrorf("Setting preprocessed: true implies presigned: true, so you cannot set presigned to false") + } + t := true + a.properties.Presigned = &t + } + numCertPropsSet := 0 if String(a.properties.Certificate) != "" { numCertPropsSet++ @@ -260,11 +313,9 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext numCertPropsSet++ } if numCertPropsSet != 1 { - ctx.ModuleErrorf("One and only one of certficate, presigned, and default_dev_cert properties must be set") + ctx.ModuleErrorf("One and only one of certficate, presigned (implied by preprocessed), and default_dev_cert properties must be set") } - _, _, certificates := collectAppDeps(ctx, a, false, false) - // TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK // TODO: LOCAL_PACKAGE_SPLITS @@ -295,12 +346,15 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries() a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx) + if a.usesLibrary.shouldDisableDexpreopt { + a.dexpreopter.disableDexpreopt() + } if a.usesLibrary.enforceUsesLibraries() { - srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk) + a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk, &a.dexpreopter.classLoaderContexts) } - a.dexpreopter.dexpreopt(ctx, jnisUncompressed) + a.dexpreopter.dexpreopt(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), jnisUncompressed) if a.dexpreopter.uncompressedDex { dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk") ctx.Build(pctx, android.BuildParams{ @@ -317,12 +371,21 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext // Sign or align the package if package has not been preprocessed - if a.preprocessed { - a.outputFile = srcApk + if proptools.Bool(a.properties.Preprocessed) { + validationStamp := a.validatePresignedApk(ctx, srcApk) + output := android.PathForModuleOut(ctx, apkFilename) + ctx.Build(pctx, android.BuildParams{ + Rule: android.Cp, + Input: srcApk, + Output: output, + Validation: validationStamp, + }) + a.outputFile = output a.certificate = PresignedCertificate } else if !Bool(a.properties.Presigned) { // If the certificate property is empty at this point, default_dev_cert must be set to true. // Which makes processMainCert's behavior for the empty cert string WAI. + _, _, certificates := collectAppDeps(ctx, a, false, false) a.certificate, certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx) signed := android.PathForModuleOut(ctx, "signed", apkFilename) var lineageFile android.Path @@ -335,8 +398,9 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext SignAppPackage(ctx, signed, jnisUncompressed, certificates, nil, lineageFile, rotationMinSdkVersion) a.outputFile = signed } else { + validationStamp := a.validatePresignedApk(ctx, srcApk) alignedApk := android.PathForModuleOut(ctx, "zip-aligned", apkFilename) - TransformZipAlign(ctx, alignedApk, jnisUncompressed) + TransformZipAlign(ctx, alignedApk, jnisUncompressed, []android.Path{validationStamp}) a.outputFile = alignedApk a.certificate = PresignedCertificate } @@ -349,9 +413,43 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext a.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, artifactPath, a.installPath) } + providePrebuiltInfo(ctx, + prebuiltInfoProps{ + baseModuleName: a.BaseModuleName(), + isPrebuilt: true, + prebuiltInfo: a.properties.Prebuilt_info, + }, + ) + + ctx.SetOutputFiles([]android.Path{a.outputFile}, "") + // TODO: androidmk converter jni libs } +func (a *AndroidAppImport) validatePresignedApk(ctx android.ModuleContext, srcApk android.Path) android.Path { + stamp := android.PathForModuleOut(ctx, "validated-prebuilt", "check.stamp") + var extraArgs []string + if a.Privileged() { + extraArgs = append(extraArgs, "--privileged") + } + if proptools.Bool(a.properties.Skip_preprocessed_apk_checks) { + extraArgs = append(extraArgs, "--skip-preprocessed-apk-checks") + } + if proptools.Bool(a.properties.Preprocessed) { + extraArgs = append(extraArgs, "--preprocessed") + } + + ctx.Build(pctx, android.BuildParams{ + Rule: checkPresignedApkRule, + Input: srcApk, + Output: stamp, + Args: map[string]string{ + "extraArgs": strings.Join(extraArgs, " "), + }, + }) + return stamp +} + func (a *AndroidAppImport) Prebuilt() *android.Prebuilt { return &a.prebuilt } @@ -376,18 +474,29 @@ func (a *AndroidAppImport) ProvenanceMetaDataFile() android.OutputPath { return a.provenanceMetaDataFile } +func (a *AndroidAppImport) PrivAppAllowlist() android.OptionalPath { + return android.OptionalPath{} +} + +const ( + ArchGroupName = "Arch" + DpiGroupName = "Dpi_variants" +) + var dpiVariantGroupType reflect.Type var archVariantGroupType reflect.Type +var archdpiVariantGroupType reflect.Type var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"} func initAndroidAppImportVariantGroupTypes() { - dpiVariantGroupType = createVariantGroupType(supportedDpis, "Dpi_variants") + dpiVariantGroupType = createVariantGroupType(supportedDpis, DpiGroupName) archNames := make([]string, len(android.ArchTypeList())) for i, archType := range android.ArchTypeList() { archNames[i] = archType.Name } - archVariantGroupType = createVariantGroupType(archNames, "Arch") + archVariantGroupType = createVariantGroupType(archNames, ArchGroupName) + archdpiVariantGroupType = createArchDpiVariantGroupType(archNames, supportedDpis) } // Populates all variant struct properties at creation time. @@ -397,6 +506,9 @@ func (a *AndroidAppImport) populateAllVariantStructs() { a.archVariants = reflect.New(archVariantGroupType).Interface() a.AddProperties(a.archVariants) + + a.arch_dpiVariants = reflect.New(archdpiVariantGroupType).Interface() + a.AddProperties(a.arch_dpiVariants) } func (a *AndroidAppImport) Privileged() bool { @@ -451,6 +563,48 @@ func createVariantGroupType(variants []string, variantGroupName string) reflect. }) } +func createArchDpiVariantGroupType(archNames []string, dpiNames []string) reflect.Type { + props := reflect.TypeOf((*AndroidAppImportProperties)(nil)) + + dpiVariantFields := make([]reflect.StructField, len(dpiNames)) + for i, variant_dpi := range dpiNames { + dpiVariantFields[i] = reflect.StructField{ + Name: proptools.FieldNameForProperty(variant_dpi), + Type: props, + } + } + dpiVariantGroupStruct := reflect.StructOf(dpiVariantFields) + dpi_struct := reflect.StructOf([]reflect.StructField{ + { + Name: DpiGroupName, + Type: reflect.PointerTo(dpiVariantGroupStruct), + }, + }) + + archVariantFields := make([]reflect.StructField, len(archNames)) + for i, variant_arch := range archNames { + archVariantFields[i] = reflect.StructField{ + Name: proptools.FieldNameForProperty(variant_arch), + Type: reflect.PointerTo(dpi_struct), + } + } + archVariantGroupStruct := reflect.StructOf(archVariantFields) + + return_struct := reflect.StructOf([]reflect.StructField{ + { + Name: ArchGroupName, + Type: reflect.PointerTo(archVariantGroupStruct), + }, + }) + return return_struct +} + +func (a *AndroidAppImport) UsesLibrary() *usesLibrary { + return &a.usesLibrary +} + +var _ ModuleWithUsesLibrary = (*AndroidAppImport)(nil) + // android_app_import imports a prebuilt apk with additional processing specified in the module. // DPI-specific apk source files can be specified using dpi_variants. Example: // @@ -473,7 +627,7 @@ func AndroidAppImportFactory() android.Module { module.AddProperties(&module.dexpreoptProperties) module.AddProperties(&module.usesLibrary.usesLibraryProperties) module.populateAllVariantStructs() - android.AddLoadHook(module, func(ctx android.LoadHookContext) { + module.SetDefaultableHook(func(ctx android.DefaultableHookContext) { module.processVariants(ctx) }) @@ -487,11 +641,6 @@ func AndroidAppImportFactory() android.Module { return module } -type androidTestImportProperties struct { - // Whether the prebuilt apk can be installed without additional processing. Default is false. - Preprocessed *bool -} - type AndroidTestImport struct { AndroidAppImport @@ -508,14 +657,10 @@ type AndroidTestImport struct { Per_testcase_directory *bool } - testImportProperties androidTestImportProperties - data android.Paths } func (a *AndroidTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { - a.preprocessed = Bool(a.testImportProperties.Preprocessed) - a.generateAndroidBuildActions(ctx) a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data) @@ -532,9 +677,8 @@ func AndroidTestImportFactory() android.Module { module.AddProperties(&module.properties) module.AddProperties(&module.dexpreoptProperties) module.AddProperties(&module.testProperties) - module.AddProperties(&module.testImportProperties) module.populateAllVariantStructs() - android.AddLoadHook(module, func(ctx android.LoadHookContext) { + module.SetDefaultableHook(func(ctx android.DefaultableHookContext) { module.processVariants(ctx) }) diff --git a/java/app_import_test.go b/java/app_import_test.go index 80930248e..496fc1308 100644 --- a/java/app_import_test.go +++ b/java/app_import_test.go @@ -40,8 +40,8 @@ func TestAndroidAppImport(t *testing.T) { variant := ctx.ModuleForTests("foo", "android_common") // Check dexpreopt outputs. - if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil || - variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil { + if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule == nil || + variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.odex").Rule == nil { t.Errorf("can't find dexpreopt outputs") } @@ -74,8 +74,8 @@ func TestAndroidAppImport_NoDexPreopt(t *testing.T) { variant := ctx.ModuleForTests("foo", "android_common") // Check dexpreopt outputs. They shouldn't exist. - if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule != nil || - variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule != nil { + if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule != nil || + variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.odex").Rule != nil { t.Errorf("dexpreopt shouldn't have run.") } @@ -101,8 +101,8 @@ func TestAndroidAppImport_Presigned(t *testing.T) { variant := ctx.ModuleForTests("foo", "android_common") // Check dexpreopt outputs. - if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil || - variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil { + if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule == nil || + variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.odex").Rule == nil { t.Errorf("can't find dexpreopt outputs") } // Make sure signing was skipped and aligning was done. @@ -210,8 +210,8 @@ func TestAndroidAppImport_DefaultDevCert(t *testing.T) { variant := ctx.ModuleForTests("foo", "android_common") // Check dexpreopt outputs. - if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil || - variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil { + if variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.vdex").Rule == nil || + variant.MaybeOutput("dexpreopt/foo/oat/arm64/package.odex").Rule == nil { t.Errorf("can't find dexpreopt outputs") } @@ -411,6 +411,27 @@ func TestAndroidAppImport_ArchVariants(t *testing.T) { installPath: "/system/app/foo/foo.apk", }, { + name: "matching arch without default", + bp: ` + android_app_import { + name: "foo", + apk: "prebuilts/apk/app.apk", + arch: { + arm64: { + apk: "prebuilts/apk/app_arm64.apk", + }, + }, + presigned: true, + dex_preopt: { + enabled: true, + }, + } + `, + expected: "verify_uses_libraries/apk/app_arm64.apk", + artifactPath: "prebuilts/apk/app_arm64.apk", + installPath: "/system/app/foo/foo.apk", + }, + { name: "no matching arch", bp: ` android_app_import { @@ -451,29 +472,137 @@ func TestAndroidAppImport_ArchVariants(t *testing.T) { artifactPath: "prebuilts/apk/app_arm.apk", installPath: "/system/app/foo/foo.apk", }, + { + name: "matching arch and dpi_variants", + bp: ` + android_app_import { + name: "foo", + apk: "prebuilts/apk/app.apk", + arch: { + arm64: { + apk: "prebuilts/apk/app_arm64.apk", + dpi_variants: { + mdpi: { + apk: "prebuilts/apk/app_arm64_mdpi.apk", + }, + xhdpi: { + apk: "prebuilts/apk/app_arm64_xhdpi.apk", + }, + }, + }, + }, + presigned: true, + dex_preopt: { + enabled: true, + }, + } + `, + expected: "verify_uses_libraries/apk/app_arm64_xhdpi.apk", + artifactPath: "prebuilts/apk/app_arm64_xhdpi.apk", + installPath: "/system/app/foo/foo.apk", + }, } for _, test := range testCases { - ctx, _ := testJava(t, test.bp) + t.Run(test.name, func(t *testing.T) { + ctx, _ := testJava(t, test.bp) - variant := ctx.ModuleForTests("foo", "android_common") - if test.expected == "" { - if variant.Module().Enabled() { - t.Error("module should have been disabled, but wasn't") + variant := ctx.ModuleForTests("foo", "android_common") + if test.expected == "" { + if variant.Module().Enabled(android.PanickingConfigAndErrorContext(ctx)) { + 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) + return } - rule := variant.MaybeRule("genProvenanceMetaData") - android.AssertDeepEquals(t, "Provenance metadata is not empty", android.TestingBuildParams{}, rule) - continue - } - input := variant.Output("jnis-uncompressed/foo.apk").Input.String() - if strings.HasSuffix(input, test.expected) { - t.Errorf("wrong src apk, expected: %q got: %q", test.expected, input) - } - 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"]) + input := variant.Output("jnis-uncompressed/foo.apk").Input.String() + if strings.HasSuffix(input, test.expected) { + t.Errorf("wrong src apk, expected: %q got: %q", test.expected, input) + } + 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"]) + }) + } +} + +func TestAndroidAppImport_SoongConfigVariables(t *testing.T) { + testCases := []struct { + name string + bp string + expected string + artifactPath string + metaDataPath string + installPath string + }{ + { + name: "matching arch", + bp: ` + soong_config_module_type { + name: "my_android_app_import", + module_type: "android_app_import", + config_namespace: "my_namespace", + value_variables: ["my_apk_var"], + properties: ["apk"], + } + soong_config_value_variable { + name: "my_apk_var", + } + my_android_app_import { + name: "foo", + soong_config_variables: { + my_apk_var: { + apk: "prebuilts/apk/%s.apk", + }, + }, + presigned: true, + dex_preopt: { + enabled: true, + }, + } + `, + expected: "verify_uses_libraries/apk/name_from_soong_config.apk", + artifactPath: "prebuilts/apk/name_from_soong_config.apk", + installPath: "/system/app/foo/foo.apk", + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + ctx := android.GroupFixturePreparers( + prepareForJavaTest, + android.PrepareForTestWithSoongConfigModuleBuildComponents, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.VendorVars = map[string]map[string]string{ + "my_namespace": { + "my_apk_var": "name_from_soong_config", + }, + } + }), + ).RunTestWithBp(t, test.bp).TestContext + + variant := ctx.ModuleForTests("foo", "android_common") + if test.expected == "" { + if variant.Module().Enabled(android.PanickingConfigAndErrorContext(ctx)) { + 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) + return + } + input := variant.Output("jnis-uncompressed/foo.apk").Input.String() + if strings.HasSuffix(input, test.expected) { + t.Errorf("wrong src apk, expected: %q got: %q", test.expected, input) + } + 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"]) + }) } } @@ -500,7 +629,7 @@ func TestAndroidAppImport_overridesDisabledAndroidApp(t *testing.T) { if !a.prebuilt.UsePrebuilt() { t.Errorf("prebuilt foo module is not active") } - if !a.Enabled() { + if !a.Enabled(android.PanickingConfigAndErrorContext(ctx)) { t.Errorf("prebuilt foo module is disabled") } } @@ -629,31 +758,49 @@ func TestAndroidTestImport_Preprocessed(t *testing.T) { presigned: true, preprocessed: true, } + `) - android_test_import { - name: "foo_cert", + apkName := "foo.apk" + variant := ctx.ModuleForTests("foo", "android_common") + jniRule := variant.Output("jnis-uncompressed/" + apkName).BuildParams.Rule.String() + if jniRule != android.Cp.String() { + t.Errorf("Unexpected JNI uncompress rule: " + jniRule) + } + + // Make sure signing and aligning were skipped. + if variant.MaybeOutput("signed/"+apkName).Rule != nil { + t.Errorf("signing rule shouldn't be included for preprocessed.") + } + if variant.MaybeOutput("zip-aligned/"+apkName).Rule != nil { + t.Errorf("aligning rule shouldn't be for preprocessed") + } +} + +func TestAndroidAppImport_Preprocessed(t *testing.T) { + ctx, _ := testJava(t, ` + android_app_import { + name: "foo", apk: "prebuilts/apk/app.apk", - certificate: "cert/new_cert", + presigned: true, preprocessed: true, } `) - testModules := []string{"foo", "foo_cert"} - for _, m := range testModules { - apkName := m + ".apk" - variant := ctx.ModuleForTests(m, "android_common") - jniRule := variant.Output("jnis-uncompressed/" + apkName).BuildParams.Rule.String() - if jniRule != android.Cp.String() { - t.Errorf("Unexpected JNI uncompress rule: " + jniRule) - } + apkName := "foo.apk" + variant := ctx.ModuleForTests("foo", "android_common") + outputBuildParams := variant.Output(apkName).BuildParams + if outputBuildParams.Rule.String() != android.Cp.String() { + t.Errorf("Unexpected prebuilt android_app_import rule: " + outputBuildParams.Rule.String()) + } - // Make sure signing and aligning were skipped. - if variant.MaybeOutput("signed/"+apkName).Rule != nil { - t.Errorf("signing rule shouldn't be included for preprocessed.") - } - if variant.MaybeOutput("zip-aligned/"+apkName).Rule != nil { - t.Errorf("aligning rule shouldn't be for preprocessed") - } + // Make sure compression and aligning were validated. + if outputBuildParams.Validation == nil { + t.Errorf("Expected validation rule, but was not found") + } + + validationBuildParams := variant.Output("validated-prebuilt/check.stamp").BuildParams + if validationBuildParams.Rule.String() != checkPresignedApkRule.String() { + t.Errorf("Unexpected validation rule: " + validationBuildParams.Rule.String()) } } diff --git a/java/app_set.go b/java/app_set.go index d2d3b06ba..33d3adec2 100644 --- a/java/app_set.go +++ b/java/app_set.go @@ -48,6 +48,11 @@ type AndroidAppSetProperties struct { // Names of modules to be overridden. Listed modules can only be other apps // (in Make or Soong). Overrides []string + + // Path to the .prebuilt_info file of the prebuilt app. + // In case of mainline modules, the .prebuilt_info file contains the build_id that was used + // to generate the prebuilt. + Prebuilt_info *string `android:"path"` } type AndroidAppSet struct { @@ -117,6 +122,27 @@ func SupportedAbis(ctx android.ModuleContext, excludeNativeBridgeAbis bool) []st return result } +type prebuiltInfoProps struct { + baseModuleName string + isPrebuilt bool + prebuiltInfo *string +} + +// Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file +// with information about whether source or prebuilt of an apex was used during the build. +func providePrebuiltInfo(ctx android.ModuleContext, p prebuiltInfoProps) { + info := android.PrebuiltInfo{ + Name: p.baseModuleName, + Is_prebuilt: p.isPrebuilt, + } + // If Prebuilt_info information is available in the soong module definition, add it to prebuilt_info.json. + if p.prebuiltInfo != nil { + prebuiltInfoFile := android.PathForModuleSrc(ctx, *p.prebuiltInfo) + info.Prebuilt_info_file_path = prebuiltInfoFile.String() + } + android.SetProvider(ctx, android.PrebuiltInfoProvider, info) +} + func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) { as.packedOutput = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip") as.primaryOutput = android.PathForModuleOut(ctx, as.BaseModuleName()+".apk") @@ -157,6 +183,15 @@ func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) installDir = android.PathForModuleInstall(ctx, "app", as.BaseModuleName()) } ctx.InstallFileWithExtraFilesZip(installDir, as.BaseModuleName()+".apk", as.primaryOutput, as.packedOutput) + + providePrebuiltInfo(ctx, + prebuiltInfoProps{ + baseModuleName: as.BaseModuleName(), + isPrebuilt: true, + prebuiltInfo: as.properties.Prebuilt_info, + }, + ) + } func (as *AndroidAppSet) InstallBypassMake() bool { return true } diff --git a/java/app_test.go b/java/app_test.go index b154bc990..e878ccf6d 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -119,10 +119,7 @@ func TestAppSplits(t *testing.T) { foo.Output(expectedOutput) } - outputFiles, err := foo.Module().(*AndroidApp).OutputFiles("") - if err != nil { - t.Fatal(err) - } + outputFiles := foo.OutputFiles(t, "") android.AssertPathsRelativeToTopEquals(t, `OutputFiles("")`, expectedOutputs, outputFiles) } @@ -443,9 +440,9 @@ func TestUpdatableApps_JniLibShouldBeBuiltAgainstMinSdkVersion(t *testing.T) { inputs := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared").Description("link").Implicits var crtbeginFound, crtendFound bool expectedCrtBegin := ctx.ModuleForTests("crtbegin_so", - "android_arm64_armv8-a_sdk_29").Rule("partialLd").Output + "android_arm64_armv8-a_sdk_29").Rule("noAddrSig").Output expectedCrtEnd := ctx.ModuleForTests("crtend_so", - "android_arm64_armv8-a_sdk_29").Rule("partialLd").Output + "android_arm64_armv8-a_sdk_29").Rule("noAddrSig").Output implicits := []string{} for _, input := range inputs { implicits = append(implicits, input.String()) @@ -519,6 +516,49 @@ func TestUpdatableApps_ErrorIfDepMinSdkVersionIsHigher(t *testing.T) { testJavaError(t, `"libjni" .*: links "libbar" built against newer API version "current"`, bp) } +func TestUpdatableApps_ApplyDefaultUpdatableModuleVersion(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + ).RunTestWithBp(t, ` + android_app { + name: "com.android.foo", + srcs: ["a.java"], + sdk_version: "current", + min_sdk_version: "31", + updatable: true, + } + `) + foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer") + android.AssertStringDoesContain(t, + "com.android.foo: expected manifest fixer to set override-placeholder-version to android.DefaultUpdatableModuleVersion", + foo.BuildParams.Args["args"], + fmt.Sprintf("--override-placeholder-version %s", android.DefaultUpdatableModuleVersion), + ) +} + +func TestUpdatableApps_ApplyOverrideApexManifestDefaultVersion(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + android.FixtureMergeEnv(map[string]string{ + "OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION": "1234", + }), + ).RunTestWithBp(t, ` + android_app { + name: "com.android.foo", + srcs: ["a.java"], + sdk_version: "current", + min_sdk_version: "31", + updatable: true, + } + `) + foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer") + android.AssertStringDoesContain(t, + "com.android.foo: expected manifest fixer to set override-placeholder-version to 1234", + foo.BuildParams.Args["args"], + "--override-placeholder-version 1234", + ) +} + func TestResourceDirs(t *testing.T) { testCases := []struct { name string @@ -558,7 +598,6 @@ func TestResourceDirs(t *testing.T) { t.Run(testCase.name, func(t *testing.T) { result := android.GroupFixturePreparers( PrepareForTestWithJavaDefaultModules, - PrepareForTestWithOverlayBuildComponents, fs.AddToFixture(), ).RunTestWithBp(t, fmt.Sprintf(bp, testCase.prop)) @@ -599,7 +638,7 @@ func TestLibraryAssets(t *testing.T) { android_library { name: "lib3", sdk_version: "current", - static_libs: ["lib4"], + static_libs: ["lib4", "import"], } android_library { @@ -607,20 +646,38 @@ func TestLibraryAssets(t *testing.T) { sdk_version: "current", asset_dirs: ["assets_b"], } + + android_library { + name: "lib5", + sdk_version: "current", + assets: [ + "path/to/asset_file_1", + "path/to/asset_file_2", + ], + } + + android_library_import { + name: "import", + sdk_version: "current", + aars: ["import.aar"], + } ` testCases := []struct { - name string - assetFlag string - assetPackages []string + name string + assetFlag string + assetPackages []string + tmpAssetDirInputs []string + tmpAssetDirOutputs []string }{ { name: "foo", - // lib1 has its own asset. lib3 doesn't have any, but provides lib4's transitively. + // lib1 has its own assets. lib3 doesn't have any, but lib4 and import have assets. assetPackages: []string{ "out/soong/.intermediates/foo/android_common/aapt2/package-res.apk", "out/soong/.intermediates/lib1/android_common/assets.zip", - "out/soong/.intermediates/lib3/android_common/assets.zip", + "out/soong/.intermediates/lib4/android_common/assets.zip", + "out/soong/.intermediates/import/android_common/assets.zip", }, }, { @@ -632,15 +689,23 @@ func TestLibraryAssets(t *testing.T) { }, { name: "lib3", - assetPackages: []string{ - "out/soong/.intermediates/lib3/android_common/aapt2/package-res.apk", - "out/soong/.intermediates/lib4/android_common/assets.zip", - }, }, { name: "lib4", assetFlag: "-A assets_b", }, + { + name: "lib5", + assetFlag: "-A out/soong/.intermediates/lib5/android_common/tmp_asset_dir", + tmpAssetDirInputs: []string{ + "path/to/asset_file_1", + "path/to/asset_file_2", + }, + tmpAssetDirOutputs: []string{ + "out/soong/.intermediates/lib5/android_common/tmp_asset_dir/path/to/asset_file_1", + "out/soong/.intermediates/lib5/android_common/tmp_asset_dir/path/to/asset_file_2", + }, + }, } ctx := testApp(t, bp) @@ -668,6 +733,14 @@ func TestLibraryAssets(t *testing.T) { mergeAssets := m.Output("package-res.apk") android.AssertPathsRelativeToTopEquals(t, "mergeAssets inputs", test.assetPackages, mergeAssets.Inputs) } + + if len(test.tmpAssetDirInputs) > 0 { + rule := m.Rule("tmp_asset_dir") + inputs := rule.Implicits + outputs := append(android.WritablePaths{rule.Output}, rule.ImplicitOutputs...).Paths() + android.AssertPathsRelativeToTopEquals(t, "tmp_asset_dir inputs", test.tmpAssetDirInputs, inputs) + android.AssertPathsRelativeToTopEquals(t, "tmp_asset_dir outputs", test.tmpAssetDirOutputs, outputs) + } }) } } @@ -717,7 +790,635 @@ func TestAppJavaResources(t *testing.T) { } } -func TestAndroidResources(t *testing.T) { +func TestAndroidResourceProcessor(t *testing.T) { + testCases := []struct { + name string + appUsesRP bool + directLibUsesRP bool + transitiveLibUsesRP bool + sharedLibUsesRP bool + sharedTransitiveStaticLibUsesRP bool + sharedTransitiveSharedLibUsesRP bool + + dontVerifyApp bool + appResources []string + appOverlays []string + appImports []string + appSrcJars []string + appClasspath []string + appCombined []string + + dontVerifyDirect bool + directResources []string + directOverlays []string + directImports []string + directSrcJars []string + directClasspath []string + directCombined []string + + dontVerifyTransitive bool + transitiveResources []string + transitiveOverlays []string + transitiveImports []string + transitiveSrcJars []string + transitiveClasspath []string + transitiveCombined []string + + dontVerifyDirectImport bool + directImportResources []string + directImportOverlays []string + directImportImports []string + + dontVerifyTransitiveImport bool + transitiveImportResources []string + transitiveImportOverlays []string + transitiveImportImports []string + + dontVerifyShared bool + sharedResources []string + sharedOverlays []string + sharedImports []string + sharedSrcJars []string + sharedClasspath []string + sharedCombined []string + }{ + { + // Test with all modules set to use_resource_processor: false (except android_library_import modules, + // which always use resource processor). + name: "legacy", + appUsesRP: false, + directLibUsesRP: false, + transitiveLibUsesRP: false, + + appResources: nil, + appOverlays: []string{ + "out/soong/.intermediates/transitive/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import/android_common/package-res.apk", + "out/soong/.intermediates/direct/android_common/package-res.apk", + "out/soong/.intermediates/direct_import_dep/android_common/package-res.apk", + "out/soong/.intermediates/direct_import/android_common/package-res.apk", + "out/soong/.intermediates/app/android_common/aapt2/app/res/values_strings.arsc.flat", + }, + appImports: []string{ + "out/soong/.intermediates/shared/android_common/package-res.apk", + "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk", + }, + appSrcJars: []string{"out/soong/.intermediates/app/android_common/gen/android/R.srcjar"}, + appClasspath: []string{ + "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar", + "out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar", + "out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar", + "out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar", + }, + appCombined: []string{ + "out/soong/.intermediates/app/android_common/javac/app.jar", + "out/soong/.intermediates/direct/android_common/combined/direct.jar", + "out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar", + }, + + directResources: nil, + directOverlays: []string{ + "out/soong/.intermediates/transitive/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import/android_common/package-res.apk", + "out/soong/.intermediates/direct/android_common/aapt2/direct/res/values_strings.arsc.flat", + }, + directImports: []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"}, + directSrcJars: []string{"out/soong/.intermediates/direct/android_common/gen/android/R.srcjar"}, + directClasspath: []string{ + "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar", + "out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar", + "out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar", + }, + directCombined: []string{ + "out/soong/.intermediates/direct/android_common/javac/direct.jar", + "out/soong/.intermediates/transitive/android_common/javac/transitive.jar", + "out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar", + }, + + transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"}, + transitiveOverlays: nil, + transitiveImports: []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"}, + transitiveSrcJars: []string{"out/soong/.intermediates/transitive/android_common/gen/android/R.srcjar"}, + transitiveClasspath: []string{"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar"}, + transitiveCombined: nil, + + sharedResources: nil, + sharedOverlays: []string{ + "out/soong/.intermediates/shared_transitive_static/android_common/package-res.apk", + "out/soong/.intermediates/shared/android_common/aapt2/shared/res/values_strings.arsc.flat", + }, + sharedImports: []string{ + "out/soong/.intermediates/shared_transitive_shared/android_common/package-res.apk", + "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk", + }, + sharedSrcJars: []string{"out/soong/.intermediates/shared/android_common/gen/android/R.srcjar"}, + sharedClasspath: []string{ + "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar", + "out/soong/.intermediates/shared_transitive_shared/android_common/turbine-combined/shared_transitive_shared.jar", + "out/soong/.intermediates/shared_transitive_static/android_common/turbine-combined/shared_transitive_static.jar", + }, + sharedCombined: []string{ + "out/soong/.intermediates/shared/android_common/javac/shared.jar", + "out/soong/.intermediates/shared_transitive_static/android_common/javac/shared_transitive_static.jar", + }, + + directImportResources: nil, + directImportOverlays: []string{"out/soong/.intermediates/direct_import/android_common/flat-res/gen_res.flata"}, + directImportImports: []string{ + "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk", + "out/soong/.intermediates/direct_import_dep/android_common/package-res.apk", + }, + + transitiveImportResources: nil, + transitiveImportOverlays: []string{"out/soong/.intermediates/transitive_import/android_common/flat-res/gen_res.flata"}, + transitiveImportImports: []string{ + "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk", + }, + }, + { + // Test with all modules set to use_resource_processor: true. + name: "resource_processor", + appUsesRP: true, + directLibUsesRP: true, + transitiveLibUsesRP: true, + sharedLibUsesRP: true, + sharedTransitiveSharedLibUsesRP: true, + sharedTransitiveStaticLibUsesRP: true, + + appResources: nil, + appOverlays: []string{ + "out/soong/.intermediates/transitive/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import/android_common/package-res.apk", + "out/soong/.intermediates/direct/android_common/package-res.apk", + "out/soong/.intermediates/direct_import_dep/android_common/package-res.apk", + "out/soong/.intermediates/direct_import/android_common/package-res.apk", + "out/soong/.intermediates/app/android_common/aapt2/app/res/values_strings.arsc.flat", + }, + appImports: []string{ + "out/soong/.intermediates/shared/android_common/package-res.apk", + "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk", + }, + appSrcJars: nil, + appClasspath: []string{ + "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar", + "out/soong/.intermediates/app/android_common/busybox/R.jar", + "out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar", + "out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar", + "out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar", + }, + appCombined: []string{ + "out/soong/.intermediates/app/android_common/javac/app.jar", + "out/soong/.intermediates/app/android_common/busybox/R.jar", + "out/soong/.intermediates/direct/android_common/combined/direct.jar", + "out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar", + }, + + directResources: nil, + directOverlays: []string{"out/soong/.intermediates/direct/android_common/aapt2/direct/res/values_strings.arsc.flat"}, + directImports: []string{ + "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk", + "out/soong/.intermediates/transitive/android_common/package-res.apk", + }, + directSrcJars: nil, + directClasspath: []string{ + "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar", + "out/soong/.intermediates/direct/android_common/busybox/R.jar", + "out/soong/.intermediates/transitive/android_common/busybox/R.jar", + "out/soong/.intermediates/transitive_import_dep/android_common/busybox/R.jar", + "out/soong/.intermediates/transitive_import/android_common/busybox/R.jar", + "out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar", + "out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar", + }, + directCombined: []string{ + "out/soong/.intermediates/direct/android_common/javac/direct.jar", + "out/soong/.intermediates/transitive/android_common/javac/transitive.jar", + "out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar", + }, + + transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"}, + transitiveOverlays: nil, + transitiveImports: []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"}, + transitiveSrcJars: nil, + transitiveClasspath: []string{ + "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar", + "out/soong/.intermediates/transitive/android_common/busybox/R.jar", + }, + transitiveCombined: nil, + + sharedResources: nil, + sharedOverlays: []string{"out/soong/.intermediates/shared/android_common/aapt2/shared/res/values_strings.arsc.flat"}, + sharedImports: []string{ + "out/soong/.intermediates/shared_transitive_shared/android_common/package-res.apk", + "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk", + "out/soong/.intermediates/shared_transitive_static/android_common/package-res.apk", + }, + sharedSrcJars: nil, + sharedClasspath: []string{ + "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar", + "out/soong/.intermediates/shared/android_common/busybox/R.jar", + "out/soong/.intermediates/shared_transitive_static/android_common/busybox/R.jar", + "out/soong/.intermediates/shared_transitive_shared/android_common/busybox/R.jar", + "out/soong/.intermediates/shared_transitive_shared/android_common/turbine-combined/shared_transitive_shared.jar", + "out/soong/.intermediates/shared_transitive_static/android_common/turbine-combined/shared_transitive_static.jar", + }, + sharedCombined: []string{ + "out/soong/.intermediates/shared/android_common/javac/shared.jar", + "out/soong/.intermediates/shared_transitive_static/android_common/javac/shared_transitive_static.jar", + }, + + directImportResources: nil, + directImportOverlays: []string{"out/soong/.intermediates/direct_import/android_common/flat-res/gen_res.flata"}, + directImportImports: []string{ + "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk", + "out/soong/.intermediates/direct_import_dep/android_common/package-res.apk", + }, + + transitiveImportResources: nil, + transitiveImportOverlays: []string{"out/soong/.intermediates/transitive_import/android_common/flat-res/gen_res.flata"}, + transitiveImportImports: []string{ + "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk", + }, + }, { + // Test an app building with resource processor enabled but with dependencies built without + // resource processor. + name: "app_resource_processor", + appUsesRP: true, + directLibUsesRP: false, + transitiveLibUsesRP: false, + + appResources: nil, + appOverlays: []string{ + "out/soong/.intermediates/transitive/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import/android_common/package-res.apk", + "out/soong/.intermediates/direct/android_common/package-res.apk", + "out/soong/.intermediates/direct_import_dep/android_common/package-res.apk", + "out/soong/.intermediates/direct_import/android_common/package-res.apk", + "out/soong/.intermediates/app/android_common/aapt2/app/res/values_strings.arsc.flat", + }, + appImports: []string{ + "out/soong/.intermediates/shared/android_common/package-res.apk", + "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk", + }, + appSrcJars: nil, + appClasspath: []string{ + "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar", + // R.jar has to come before direct.jar + "out/soong/.intermediates/app/android_common/busybox/R.jar", + "out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar", + "out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar", + "out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar", + }, + appCombined: []string{ + "out/soong/.intermediates/app/android_common/javac/app.jar", + "out/soong/.intermediates/app/android_common/busybox/R.jar", + "out/soong/.intermediates/direct/android_common/combined/direct.jar", + "out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar", + }, + + dontVerifyDirect: true, + dontVerifyTransitive: true, + dontVerifyShared: true, + dontVerifyDirectImport: true, + dontVerifyTransitiveImport: true, + }, + { + // Test an app building without resource processor enabled but with a dependency built with + // resource processor. + name: "app_dependency_lib_resource_processor", + appUsesRP: false, + directLibUsesRP: true, + transitiveLibUsesRP: false, + + appOverlays: []string{ + "out/soong/.intermediates/transitive/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import/android_common/package-res.apk", + "out/soong/.intermediates/direct/android_common/package-res.apk", + "out/soong/.intermediates/direct_import_dep/android_common/package-res.apk", + "out/soong/.intermediates/direct_import/android_common/package-res.apk", + "out/soong/.intermediates/app/android_common/aapt2/app/res/values_strings.arsc.flat", + }, + appImports: []string{ + "out/soong/.intermediates/shared/android_common/package-res.apk", + "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk", + }, + appSrcJars: []string{"out/soong/.intermediates/app/android_common/gen/android/R.srcjar"}, + appClasspath: []string{ + "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar", + "out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar", + "out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar", + "out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar", + }, + appCombined: []string{ + "out/soong/.intermediates/app/android_common/javac/app.jar", + "out/soong/.intermediates/direct/android_common/combined/direct.jar", + "out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar", + }, + + directResources: nil, + directOverlays: []string{"out/soong/.intermediates/direct/android_common/aapt2/direct/res/values_strings.arsc.flat"}, + directImports: []string{ + "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk", + "out/soong/.intermediates/transitive/android_common/package-res.apk", + }, + directSrcJars: nil, + directClasspath: []string{ + "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar", + "out/soong/.intermediates/direct/android_common/busybox/R.jar", + "out/soong/.intermediates/transitive_import_dep/android_common/busybox/R.jar", + "out/soong/.intermediates/transitive_import/android_common/busybox/R.jar", + "out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar", + "out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar", + }, + directCombined: []string{ + "out/soong/.intermediates/direct/android_common/javac/direct.jar", + "out/soong/.intermediates/transitive/android_common/javac/transitive.jar", + "out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar", + }, + + dontVerifyTransitive: true, + dontVerifyShared: true, + dontVerifyDirectImport: true, + dontVerifyTransitiveImport: true, + }, + { + // Test a library building without resource processor enabled but with a dependency built with + // resource processor. + name: "lib_dependency_lib_resource_processor", + appUsesRP: false, + directLibUsesRP: false, + transitiveLibUsesRP: true, + + appOverlays: []string{ + "out/soong/.intermediates/transitive/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import/android_common/package-res.apk", + "out/soong/.intermediates/direct/android_common/package-res.apk", + "out/soong/.intermediates/direct_import_dep/android_common/package-res.apk", + "out/soong/.intermediates/direct_import/android_common/package-res.apk", + "out/soong/.intermediates/app/android_common/aapt2/app/res/values_strings.arsc.flat", + }, + appImports: []string{ + "out/soong/.intermediates/shared/android_common/package-res.apk", + "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk", + }, + appSrcJars: []string{"out/soong/.intermediates/app/android_common/gen/android/R.srcjar"}, + appClasspath: []string{ + "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar", + "out/soong/.intermediates/shared/android_common/turbine-combined/shared.jar", + "out/soong/.intermediates/direct/android_common/turbine-combined/direct.jar", + "out/soong/.intermediates/direct_import/android_common/turbine-combined/direct_import.jar", + }, + appCombined: []string{ + "out/soong/.intermediates/app/android_common/javac/app.jar", + "out/soong/.intermediates/direct/android_common/combined/direct.jar", + "out/soong/.intermediates/direct_import/android_common/combined/direct_import.jar", + }, + + directResources: nil, + directOverlays: []string{ + "out/soong/.intermediates/transitive/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk", + "out/soong/.intermediates/transitive_import/android_common/package-res.apk", + "out/soong/.intermediates/direct/android_common/aapt2/direct/res/values_strings.arsc.flat", + }, + directImports: []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"}, + directSrcJars: []string{"out/soong/.intermediates/direct/android_common/gen/android/R.srcjar"}, + directClasspath: []string{ + "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar", + "out/soong/.intermediates/transitive/android_common/turbine-combined/transitive.jar", + "out/soong/.intermediates/transitive_import/android_common/turbine-combined/transitive_import.jar", + }, + directCombined: []string{ + "out/soong/.intermediates/direct/android_common/javac/direct.jar", + "out/soong/.intermediates/transitive/android_common/javac/transitive.jar", + "out/soong/.intermediates/transitive_import/android_common/combined/transitive_import.jar", + }, + + transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"}, + transitiveOverlays: nil, + transitiveImports: []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"}, + transitiveSrcJars: nil, + transitiveClasspath: []string{ + "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine-combined/android_stubs_current.jar", + "out/soong/.intermediates/transitive/android_common/busybox/R.jar", + }, + transitiveCombined: nil, + + dontVerifyShared: true, + dontVerifyDirectImport: true, + dontVerifyTransitiveImport: true, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + bp := fmt.Sprintf(` + android_app { + name: "app", + sdk_version: "current", + srcs: ["app/app.java"], + resource_dirs: ["app/res"], + manifest: "app/AndroidManifest.xml", + libs: ["shared"], + static_libs: ["direct", "direct_import"], + use_resource_processor: %v, + } + + android_library { + name: "direct", + sdk_version: "current", + srcs: ["direct/direct.java"], + resource_dirs: ["direct/res"], + manifest: "direct/AndroidManifest.xml", + static_libs: ["transitive", "transitive_import"], + use_resource_processor: %v, + } + + android_library { + name: "transitive", + sdk_version: "current", + srcs: ["transitive/transitive.java"], + resource_dirs: ["transitive/res"], + manifest: "transitive/AndroidManifest.xml", + use_resource_processor: %v, + } + + android_library { + name: "shared", + sdk_version: "current", + srcs: ["shared/shared.java"], + resource_dirs: ["shared/res"], + manifest: "shared/AndroidManifest.xml", + use_resource_processor: %v, + libs: ["shared_transitive_shared"], + static_libs: ["shared_transitive_static"], + } + + android_library { + name: "shared_transitive_shared", + sdk_version: "current", + srcs: ["shared_transitive_shared/shared_transitive_shared.java"], + resource_dirs: ["shared_transitive_shared/res"], + manifest: "shared_transitive_shared/AndroidManifest.xml", + use_resource_processor: %v, + } + + android_library { + name: "shared_transitive_static", + sdk_version: "current", + srcs: ["shared_transitive_static/shared.java"], + resource_dirs: ["shared_transitive_static/res"], + manifest: "shared_transitive_static/AndroidManifest.xml", + use_resource_processor: %v, + } + + android_library_import { + name: "direct_import", + sdk_version: "current", + aars: ["direct_import.aar"], + static_libs: ["direct_import_dep"], + } + + android_library_import { + name: "direct_import_dep", + sdk_version: "current", + aars: ["direct_import_dep.aar"], + } + + android_library_import { + name: "transitive_import", + sdk_version: "current", + aars: ["transitive_import.aar"], + static_libs: ["transitive_import_dep"], + } + + android_library_import { + name: "transitive_import_dep", + sdk_version: "current", + aars: ["transitive_import_dep.aar"], + } + `, testCase.appUsesRP, testCase.directLibUsesRP, testCase.transitiveLibUsesRP, + testCase.sharedLibUsesRP, testCase.sharedTransitiveSharedLibUsesRP, testCase.sharedTransitiveStaticLibUsesRP) + + fs := android.MockFS{ + "app/res/values/strings.xml": nil, + "direct/res/values/strings.xml": nil, + "transitive/res/values/strings.xml": nil, + "shared/res/values/strings.xml": nil, + "shared_transitive_static/res/values/strings.xml": nil, + "shared_transitive_shared/res/values/strings.xml": nil, + } + + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + fs.AddToFixture(), + ).RunTestWithBp(t, bp) + + type aaptInfo struct { + resources, overlays, imports, srcJars, classpath, combined android.Paths + } + + getAaptInfo := func(moduleName string) (aaptInfo aaptInfo) { + mod := result.ModuleForTests(moduleName, "android_common") + resourceListRule := mod.MaybeOutput("aapt2/res.list") + overlayListRule := mod.MaybeOutput("aapt2/overlay.list") + aaptRule := mod.Rule("aapt2Link") + javacRule := mod.MaybeRule("javac") + combinedRule := mod.MaybeOutput("combined/" + moduleName + ".jar") + + aaptInfo.resources = resourceListRule.Inputs + aaptInfo.overlays = overlayListRule.Inputs + + aaptFlags := strings.Split(aaptRule.Args["flags"], " ") + for i, flag := range aaptFlags { + if flag == "-I" && i+1 < len(aaptFlags) { + aaptInfo.imports = append(aaptInfo.imports, android.PathForTesting(aaptFlags[i+1])) + } + } + + if len(javacRule.Args["srcJars"]) > 0 { + aaptInfo.srcJars = android.PathsForTesting(strings.Split(javacRule.Args["srcJars"], " ")...) + } + + if len(javacRule.Args["classpath"]) > 0 { + classpathArg := strings.TrimPrefix(javacRule.Args["classpath"], "-classpath ") + aaptInfo.classpath = android.PathsForTesting(strings.Split(classpathArg, ":")...) + } + + aaptInfo.combined = combinedRule.Inputs + return + } + + app := getAaptInfo("app") + direct := getAaptInfo("direct") + transitive := getAaptInfo("transitive") + shared := getAaptInfo("shared") + directImport := getAaptInfo("direct_import") + transitiveImport := getAaptInfo("transitive_import") + + if !testCase.dontVerifyApp { + android.AssertPathsRelativeToTopEquals(t, "app resources", testCase.appResources, app.resources) + android.AssertPathsRelativeToTopEquals(t, "app overlays", testCase.appOverlays, app.overlays) + android.AssertPathsRelativeToTopEquals(t, "app imports", testCase.appImports, app.imports) + android.AssertPathsRelativeToTopEquals(t, "app srcjars", testCase.appSrcJars, app.srcJars) + android.AssertPathsRelativeToTopEquals(t, "app classpath", testCase.appClasspath, app.classpath) + android.AssertPathsRelativeToTopEquals(t, "app combined", testCase.appCombined, app.combined) + } + + if !testCase.dontVerifyDirect { + android.AssertPathsRelativeToTopEquals(t, "direct resources", testCase.directResources, direct.resources) + android.AssertPathsRelativeToTopEquals(t, "direct overlays", testCase.directOverlays, direct.overlays) + android.AssertPathsRelativeToTopEquals(t, "direct imports", testCase.directImports, direct.imports) + android.AssertPathsRelativeToTopEquals(t, "direct srcjars", testCase.directSrcJars, direct.srcJars) + android.AssertPathsRelativeToTopEquals(t, "direct classpath", testCase.directClasspath, direct.classpath) + android.AssertPathsRelativeToTopEquals(t, "direct combined", testCase.directCombined, direct.combined) + } + + if !testCase.dontVerifyTransitive { + android.AssertPathsRelativeToTopEquals(t, "transitive resources", testCase.transitiveResources, transitive.resources) + android.AssertPathsRelativeToTopEquals(t, "transitive overlays", testCase.transitiveOverlays, transitive.overlays) + android.AssertPathsRelativeToTopEquals(t, "transitive imports", testCase.transitiveImports, transitive.imports) + android.AssertPathsRelativeToTopEquals(t, "transitive srcjars", testCase.transitiveSrcJars, transitive.srcJars) + android.AssertPathsRelativeToTopEquals(t, "transitive classpath", testCase.transitiveClasspath, transitive.classpath) + android.AssertPathsRelativeToTopEquals(t, "transitive combined", testCase.transitiveCombined, transitive.combined) + } + + if !testCase.dontVerifyShared { + android.AssertPathsRelativeToTopEquals(t, "shared resources", testCase.sharedResources, shared.resources) + android.AssertPathsRelativeToTopEquals(t, "shared overlays", testCase.sharedOverlays, shared.overlays) + android.AssertPathsRelativeToTopEquals(t, "shared imports", testCase.sharedImports, shared.imports) + android.AssertPathsRelativeToTopEquals(t, "shared srcjars", testCase.sharedSrcJars, shared.srcJars) + android.AssertPathsRelativeToTopEquals(t, "shared classpath", testCase.sharedClasspath, shared.classpath) + android.AssertPathsRelativeToTopEquals(t, "shared combined", testCase.sharedCombined, shared.combined) + } + + if !testCase.dontVerifyDirectImport { + android.AssertPathsRelativeToTopEquals(t, "direct_import resources", testCase.directImportResources, directImport.resources) + android.AssertPathsRelativeToTopEquals(t, "direct_import overlays", testCase.directImportOverlays, directImport.overlays) + android.AssertPathsRelativeToTopEquals(t, "direct_import imports", testCase.directImportImports, directImport.imports) + } + + if !testCase.dontVerifyTransitiveImport { + android.AssertPathsRelativeToTopEquals(t, "transitive_import resources", testCase.transitiveImportResources, transitiveImport.resources) + android.AssertPathsRelativeToTopEquals(t, "transitive_import overlays", testCase.transitiveImportOverlays, transitiveImport.overlays) + android.AssertPathsRelativeToTopEquals(t, "transitive_import imports", testCase.transitiveImportImports, transitiveImport.imports) + } + }) + } +} + +func TestAndroidResourceOverlays(t *testing.T) { testCases := []struct { name string enforceRROTargets []string @@ -903,7 +1604,6 @@ func TestAndroidResources(t *testing.T) { t.Run(testCase.name, func(t *testing.T) { result := android.GroupFixturePreparers( PrepareForTestWithJavaDefaultModules, - PrepareForTestWithOverlayBuildComponents, fs.AddToFixture(), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.DeviceResourceOverlays = deviceResourceOverlays @@ -943,7 +1643,7 @@ func TestAndroidResources(t *testing.T) { overlayFiles = resourceListToFiles(module, android.PathsRelativeToTop(overlayList.Inputs)) } - for _, d := range module.Module().(AndroidLibraryDependency).ExportedRRODirs() { + for _, d := range module.Module().(AndroidLibraryDependency).RRODirsDepSet().ToList() { var prefix string if d.overlayType == device { prefix = "device:" @@ -1232,7 +1932,7 @@ func TestJNIABI(t *testing.T) { for _, test := range testCases { t.Run(test.name, func(t *testing.T) { app := ctx.ModuleForTests(test.name, "android_common") - jniLibZip := app.Output(jniJarOutputPathString) + jniLibZip := app.Output("jnilibs.zip") var abis []string args := strings.Fields(jniLibZip.Args["jarArgs"]) for i := 0; i < len(args); i++ { @@ -1365,7 +2065,7 @@ func TestJNIPackaging(t *testing.T) { for _, test := range testCases { t.Run(test.name, func(t *testing.T) { app := ctx.ModuleForTests(test.name, "android_common") - jniLibZip := app.MaybeOutput(jniJarOutputPathString) + jniLibZip := app.MaybeOutput("jnilibs.zip") if g, w := (jniLibZip.Rule != nil), test.packaged; g != w { t.Errorf("expected jni packaged %v, got %v", w, g) } @@ -1449,14 +2149,14 @@ func TestJNISDK(t *testing.T) { Output("libjni.so").Output.String() sdkJNI := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared"). Output("libjni.so").Output.String() - vendorJNI := ctx.ModuleForTests("libvendorjni", "android_arm64_armv8-a_shared"). + vendorJNI := ctx.ModuleForTests("libvendorjni", "android_vendor_arm64_armv8-a_shared"). Output("libvendorjni.so").Output.String() for _, test := range testCases { t.Run(test.name, func(t *testing.T) { app := ctx.ModuleForTests(test.name, "android_common") - jniLibZip := app.MaybeOutput(jniJarOutputPathString) + jniLibZip := app.MaybeOutput("jnilibs.zip") if len(jniLibZip.Implicits) != 1 { t.Fatalf("expected exactly one jni library, got %q", jniLibZip.Implicits.Strings()) } @@ -2476,7 +3176,7 @@ func TestStl(t *testing.T) { for _, test := range testCases { t.Run(test.name, func(t *testing.T) { app := ctx.ModuleForTests(test.name, "android_common") - jniLibZip := app.Output(jniJarOutputPathString) + jniLibZip := app.Output("jnilibs.zip") var jnis []string args := strings.Fields(jniLibZip.Args["jarArgs"]) for i := 0; i < len(args); i++ { @@ -2584,7 +3284,10 @@ func TestUsesLibraries(t *testing.T) { name: "static-y", srcs: ["a.java"], uses_libs: ["runtime-required-y"], - optional_uses_libs: ["runtime-optional-y"], + optional_uses_libs: [ + "runtime-optional-y", + "missing-lib-a", + ], sdk_version: "current", } @@ -2620,7 +3323,7 @@ func TestUsesLibraries(t *testing.T) { sdk_version: "current", optional_uses_libs: [ "bar", - "baz", + "missing-lib-b", ], } @@ -2635,7 +3338,7 @@ func TestUsesLibraries(t *testing.T) { ], optional_uses_libs: [ "bar", - "baz", + "missing-lib-b", ], } ` @@ -2645,7 +3348,7 @@ func TestUsesLibraries(t *testing.T) { PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("runtime-library", "foo", "quuz", "qux", "bar", "fred"), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.MissingUsesLibraries = []string{"baz"} + variables.BuildWarningBadOptionalUsesLibsAllowlist = []string{"app", "prebuilt"} }), ).RunTestWithBp(t, bp) @@ -2657,10 +3360,10 @@ func TestUsesLibraries(t *testing.T) { // propagated from dependencies. actualManifestFixerArgs := app.Output("manifest_fixer/AndroidManifest.xml").Args["args"] expectManifestFixerArgs := `--extract-native-libs=true ` + - `--uses-library qux ` + - `--uses-library quuz ` + `--uses-library foo ` + `--uses-library com.non.sdk.lib ` + + `--uses-library qux ` + + `--uses-library quuz ` + `--uses-library runtime-library ` + `--uses-library runtime-required-x ` + `--uses-library runtime-required-y ` + @@ -2679,9 +3382,10 @@ func TestUsesLibraries(t *testing.T) { `--uses-library runtime-required-x ` + `--uses-library runtime-required-y ` + `--optional-uses-library bar ` + - `--optional-uses-library baz ` + `--optional-uses-library runtime-optional-x ` + - `--optional-uses-library runtime-optional-y ` + `--optional-uses-library runtime-optional-y ` + + `--missing-optional-uses-library missing-lib-b ` + + `--missing-optional-uses-library missing-lib-a` android.AssertStringDoesContain(t, "verify cmd args", verifyCmd, verifyArgs) // Test that all libraries are verified for an APK (library order matters). @@ -2690,55 +3394,14 @@ func TestUsesLibraries(t *testing.T) { `--uses-library com.non.sdk.lib ` + `--uses-library android.test.runner ` + `--optional-uses-library bar ` + - `--optional-uses-library baz ` + `--missing-optional-uses-library missing-lib-b ` android.AssertStringDoesContain(t, "verify apk cmd args", verifyApkCmd, verifyApkArgs) - // Test that all present libraries are preopted, including implicit SDK dependencies, possibly stubs + // Test that necessary args are passed for constructing CLC in Ninja phase. cmd := app.Rule("dexpreopt").RuleParams.Command - w := `--target-context-for-sdk any ` + - `PCL[/system/framework/qux.jar]#` + - `PCL[/system/framework/quuz.jar]#` + - `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-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. - android.AssertStringDoesContain(t, "dexpreopt app cmd 28", cmd, - `--target-context-for-sdk 28`+ - ` PCL[/system/framework/org.apache.http.legacy.jar] `) - - // Test conditional context for target SDK version 29. - android.AssertStringDoesContain(t, "dexpreopt app cmd 29", cmd, - `--target-context-for-sdk 29`+ - ` PCL[/system/framework/android.hidl.manager-V1.0-java.jar]`+ - `#PCL[/system/framework/android.hidl.base-V1.0-java.jar] `) - - // Test conditional context for target SDK version 30. - // "android.test.mock" is absent because "android.test.runner" is not used. - android.AssertStringDoesContain(t, "dexpreopt app cmd 30", cmd, - `--target-context-for-sdk 30`+ - ` PCL[/system/framework/android.test.base.jar] `) - - cmd = prebuilt.Rule("dexpreopt").RuleParams.Command - android.AssertStringDoesContain(t, "dexpreopt prebuilt cmd", cmd, - `--target-context-for-sdk any`+ - ` PCL[/system/framework/foo.jar]`+ - `#PCL[/system/framework/non-sdk-lib.jar]`+ - `#PCL[/system/framework/android.test.runner.jar]`+ - `#PCL[/system/framework/bar.jar] `) - - // Test conditional context for target SDK version 30. - // "android.test.mock" is present because "android.test.runner" is used. - android.AssertStringDoesContain(t, "dexpreopt prebuilt cmd 30", cmd, - `--target-context-for-sdk 30`+ - ` PCL[/system/framework/android.test.base.jar]`+ - `#PCL[/system/framework/android.test.mock.jar] `) + android.AssertStringDoesContain(t, "dexpreopt app cmd context", cmd, "--context-json=") + android.AssertStringDoesContain(t, "dexpreopt app cmd product_packages", cmd, + "--product-packages=out/soong/.intermediates/app/android_common/dexpreopt/app/product_packages.txt") } func TestDexpreoptBcp(t *testing.T) { @@ -3006,7 +3669,10 @@ func TestExportedProguardFlagFiles(t *testing.T) { android_app { name: "foo", sdk_version: "current", - static_libs: ["lib1"], + static_libs: [ + "lib1", + "lib3", + ], } android_library { @@ -3014,22 +3680,49 @@ func TestExportedProguardFlagFiles(t *testing.T) { sdk_version: "current", optimize: { proguard_flags_files: ["lib1proguard.cfg"], + }, + static_libs: ["lib2"], + } + + android_library { + name: "lib2", + sdk_version: "current", + optimize: { + proguard_flags_files: ["lib2proguard.cfg"], } } - `) - m := ctx.ModuleForTests("foo", "android_common") - hasLib1Proguard := false - for _, s := range m.Rule("java.r8").Implicits.Strings() { - if s == "lib1proguard.cfg" { - hasLib1Proguard = true - break + android_library_import { + name: "lib3", + sdk_version: "current", + aars: ["lib3.aar"], + static_libs: ["lib4"], } - } - if !hasLib1Proguard { - t.Errorf("App does not use library proguard config") - } + android_library { + name: "lib4", + sdk_version: "current", + optimize: { + proguard_flags_files: ["lib4proguard.cfg"], + } + } + + + `) + + m := ctx.ModuleForTests("foo", "android_common") + r8 := m.Rule("java.r8") + implicits := r8.Implicits.RelativeToTop().Strings() + android.AssertStringListContains(t, "r8 implicits", implicits, "lib1proguard.cfg") + android.AssertStringListContains(t, "r8 implicits", implicits, "lib2proguard.cfg") + android.AssertStringListContains(t, "r8 implicits", implicits, "lib4proguard.cfg") + android.AssertStringListContains(t, "r8 implicits", implicits, "out/soong/.intermediates/lib3/android_common/aar/proguard.txt") + + flags := r8.Args["r8Flags"] + android.AssertStringDoesContain(t, "r8 flags", flags, "-include lib1proguard.cfg") + android.AssertStringDoesContain(t, "r8 flags", flags, "-include lib2proguard.cfg") + android.AssertStringDoesContain(t, "r8 flags", flags, "-include lib4proguard.cfg") + android.AssertStringDoesContain(t, "r8 flags", flags, "-include out/soong/.intermediates/lib3/android_common/aar/proguard.txt") } func TestTargetSdkVersionManifestFixer(t *testing.T) { @@ -3493,6 +4186,7 @@ func TestTargetSdkVersionMtsTests(t *testing.T) { bpTemplate := ` %v { name: "mytest", + min_sdk_version: "34", target_sdk_version: "%v", test_suites: ["othersuite", "%v"], } @@ -3547,3 +4241,399 @@ func TestTargetSdkVersionMtsTests(t *testing.T) { android.AssertStringDoesContain(t, testCase.desc, manifestFixerArgs, "--targetSdkVersion "+testCase.targetSdkVersionExpected) } } + +func TestPrivappAllowlist(t *testing.T) { + testJavaError(t, "privileged must be set in order to use privapp_allowlist", ` + android_app { + name: "foo", + srcs: ["a.java"], + privapp_allowlist: "perms.xml", + } + `) + + result := PrepareForTestWithJavaDefaultModules.RunTestWithBp( + t, + ` + android_app { + name: "foo", + srcs: ["a.java"], + privapp_allowlist: "privapp_allowlist_com.android.foo.xml", + privileged: true, + sdk_version: "current", + } + override_android_app { + name: "bar", + base: "foo", + package_name: "com.google.android.foo", + } + `, + ) + app := result.ModuleForTests("foo", "android_common") + overrideApp := result.ModuleForTests("foo", "android_common_bar") + + // verify that privapp allowlist is created for override apps + overrideApp.Output("out/soong/.intermediates/foo/android_common_bar/privapp_allowlist_com.google.android.foo.xml") + expectedAllowlistInput := "privapp_allowlist_com.android.foo.xml" + overrideActualAllowlistInput := overrideApp.Rule("modifyAllowlist").Input.String() + if expectedAllowlistInput != overrideActualAllowlistInput { + t.Errorf("expected override allowlist to be %q; got %q", expectedAllowlistInput, overrideActualAllowlistInput) + } + + // verify that permissions are copied to device + app.Output("out/soong/target/product/test_device/system/etc/permissions/foo.xml") + overrideApp.Output("out/soong/target/product/test_device/system/etc/permissions/bar.xml") +} + +func TestPrivappAllowlistAndroidMk(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + android.PrepareForTestWithAndroidMk, + ).RunTestWithBp( + t, + ` + android_app { + name: "foo", + srcs: ["a.java"], + privapp_allowlist: "privapp_allowlist_com.android.foo.xml", + privileged: true, + sdk_version: "current", + } + override_android_app { + name: "bar", + base: "foo", + package_name: "com.google.android.foo", + } + `, + ) + baseApp := result.ModuleForTests("foo", "android_common") + overrideApp := result.ModuleForTests("foo", "android_common_bar") + + baseAndroidApp := baseApp.Module().(*AndroidApp) + baseEntries := android.AndroidMkEntriesForTest(t, result.TestContext, baseAndroidApp)[0] + android.AssertStringMatches( + t, + "androidmk has incorrect LOCAL_SOONG_INSTALLED_MODULE; expected to find foo.apk", + baseEntries.EntryMap["LOCAL_SOONG_INSTALLED_MODULE"][0], + "\\S+foo.apk", + ) + android.AssertStringMatches( + t, + "androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include foo.apk", + baseEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0], + "\\S+foo.apk", + ) + android.AssertStringMatches( + t, + "androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include app", + baseEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0], + "\\S+foo.apk:\\S+/target/product/test_device/system/priv-app/foo/foo.apk", + ) + android.AssertStringMatches( + t, + "androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include privapp_allowlist", + baseEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0], + "privapp_allowlist_com.android.foo.xml:\\S+/target/product/test_device/system/etc/permissions/foo.xml", + ) + + overrideAndroidApp := overrideApp.Module().(*AndroidApp) + overrideEntries := android.AndroidMkEntriesForTest(t, result.TestContext, overrideAndroidApp)[0] + android.AssertStringMatches( + t, + "androidmk has incorrect LOCAL_SOONG_INSTALLED_MODULE; expected to find bar.apk", + overrideEntries.EntryMap["LOCAL_SOONG_INSTALLED_MODULE"][0], + "\\S+bar.apk", + ) + android.AssertStringMatches( + t, + "androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include bar.apk", + overrideEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0], + "\\S+bar.apk", + ) + android.AssertStringMatches( + t, + "androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include app", + overrideEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0], + "\\S+bar.apk:\\S+/target/product/test_device/system/priv-app/bar/bar.apk", + ) + android.AssertStringMatches( + t, + "androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include privapp_allowlist", + overrideEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0], + "\\S+soong/.intermediates/foo/android_common_bar/privapp_allowlist_com.google.android.foo.xml:\\S+/target/product/test_device/system/etc/permissions/bar.xml", + ) +} + +func TestAppFlagsPackages(t *testing.T) { + ctx := testApp(t, ` + android_app { + name: "foo", + srcs: ["a.java"], + sdk_version: "current", + flags_packages: [ + "bar", + "baz", + ], + } + aconfig_declarations { + name: "bar", + package: "com.example.package.bar", + container: "com.android.foo", + srcs: [ + "bar.aconfig", + ], + } + aconfig_declarations { + name: "baz", + package: "com.example.package.baz", + container: "com.android.foo", + srcs: [ + "baz.aconfig", + ], + } + `) + + foo := ctx.ModuleForTests("foo", "android_common") + + // android_app module depends on aconfig_declarations listed in flags_packages + android.AssertBoolEquals(t, "foo expected to depend on bar", true, + CheckModuleHasDependency(t, ctx, "foo", "android_common", "bar")) + + android.AssertBoolEquals(t, "foo expected to depend on baz", true, + CheckModuleHasDependency(t, ctx, "foo", "android_common", "baz")) + + aapt2LinkRule := foo.Rule("android/soong/java.aapt2Link") + linkInFlags := aapt2LinkRule.Args["inFlags"] + android.AssertStringDoesContain(t, + "aapt2 link command expected to pass feature flags arguments", + linkInFlags, + "--feature-flags @out/soong/.intermediates/bar/intermediate.txt --feature-flags @out/soong/.intermediates/baz/intermediate.txt", + ) +} + +func TestAppFlagsPackagesPropagation(t *testing.T) { + ctx := testApp(t, ` + aconfig_declarations { + name: "foo", + package: "com.example.package.foo", + container: "com.android.foo", + srcs: [ + "foo.aconfig", + ], + } + aconfig_declarations { + name: "bar", + package: "com.example.package.bar", + container: "com.android.bar", + srcs: [ + "bar.aconfig", + ], + } + aconfig_declarations { + name: "baz", + package: "com.example.package.baz", + container: "com.android.baz", + srcs: [ + "baz.aconfig", + ], + } + android_library { + name: "foo_lib", + srcs: ["a.java"], + sdk_version: "current", + flags_packages: [ + "foo", + ], + } + android_library { + name: "bar_lib", + srcs: ["a.java"], + sdk_version: "current", + flags_packages: [ + "bar", + ], + } + android_app { + name: "baz_app", + srcs: ["a.java"], + sdk_version: "current", + flags_packages: [ + "baz", + ], + static_libs: [ + "bar_lib", + ], + libs: [ + "foo_lib", + ], + } + `) + + bazApp := ctx.ModuleForTests("baz_app", "android_common") + + // android_app module depends on aconfig_declarations listed in flags_packages + // and that of static libs, but not libs + aapt2LinkRule := bazApp.Rule("android/soong/java.aapt2Link") + linkInFlags := aapt2LinkRule.Args["inFlags"] + android.AssertStringDoesContain(t, + "aapt2 link command expected to pass feature flags arguments of flags_packages and that of its static libs", + linkInFlags, + "--feature-flags @out/soong/.intermediates/bar/intermediate.txt --feature-flags @out/soong/.intermediates/baz/intermediate.txt", + ) + android.AssertStringDoesNotContain(t, + "aapt2 link command expected to not pass feature flags arguments of flags_packages of its libs", + linkInFlags, + "--feature-flags @out/soong/.intermediates/foo/intermediate.txt", + ) +} + +// Test that dexpreopt is disabled if an optional_uses_libs exists, but does not provide an implementation. +func TestNoDexpreoptOptionalUsesLibDoesNotHaveImpl(t *testing.T) { + bp := ` + java_sdk_library_import { + name: "sdklib_noimpl", + public: { + jars: ["stub.jar"], + }, + } + android_app { + name: "app", + srcs: ["a.java"], + sdk_version: "current", + optional_uses_libs: [ + "sdklib_noimpl", + ], + } + ` + result := prepareForJavaTest.RunTestWithBp(t, bp) + dexpreopt := result.ModuleForTests("app", "android_common").MaybeRule("dexpreopt").Rule + android.AssertBoolEquals(t, "dexpreopt should be disabled if optional_uses_libs does not have an implementation", true, dexpreopt == nil) +} + +func TestTestOnlyApp(t *testing.T) { + t.Parallel() + ctx := android.GroupFixturePreparers( + prepareForJavaTest, + ).RunTestWithBp(t, ` + // These should be test-only + android_test { + name: "android-test", + } + android_test_helper_app { + name: "helper-app", + } + override_android_test { + name: "override-test", + base: "android-app", + } + // And these should not be + android_app { + name: "android-app", + srcs: ["b.java"], + sdk_version: "current", + } + `) + + expectedTestOnly := []string{ + "android-test", + "helper-app", + "override-test", + } + + expectedTopLevel := []string{ + "android-test", + "override-test", + } + + assertTestOnlyAndTopLevel(t, ctx, expectedTestOnly, expectedTopLevel) +} + +func TestAppStem(t *testing.T) { + ctx := testApp(t, ` + android_app { + name: "foo", + srcs: ["a.java"], + stem: "foo-new", + sdk_version: "current", + }`) + + foo := ctx.ModuleForTests("foo", "android_common") + + outputs := fmt.Sprint(foo.AllOutputs()) + if !strings.Contains(outputs, "foo-new.apk") { + t.Errorf("Module output does not contain expected apk %s", "foo-new.apk") + } +} + +func TestAppMinSdkVersionOverride(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + ).RunTestWithBp(t, ` + android_app { + name: "com.android.foo", + srcs: ["a.java"], + sdk_version: "current", + min_sdk_version: "31", + updatable: true, + } + override_android_app { + name: "com.android.go.foo", + base: "com.android.foo", + min_sdk_version: "33", + } + `) + foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer") + fooOverride := result.ModuleForTests("com.android.foo", "android_common_com.android.go.foo").Rule("manifestFixer") + + android.AssertStringDoesContain(t, + "com.android.foo: expected manifest fixer to set minSdkVersion to T", + foo.BuildParams.Args["args"], + "--minSdkVersion 31", + ) + android.AssertStringDoesContain(t, + "com.android.go.foo: expected manifest fixer to set minSdkVersion to T", + fooOverride.BuildParams.Args["args"], + "--minSdkVersion 33", + ) + +} + +func TestNotApplyDefaultUpdatableModuleVersion(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + ).RunTestWithBp(t, ` + android_app { + name: "com.android.foo", + srcs: ["a.java"], + sdk_version: "current", + min_sdk_version: "31", + } + `) + foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer") + android.AssertStringDoesNotContain(t, + "com.android.foo: expected manifest fixer to not set override-placeholder-version", + foo.BuildParams.Args["args"], + "--override-placeholder-version", + ) +} + +func TestNotApplyOverrideApexManifestDefaultVersion(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + android.FixtureMergeEnv(map[string]string{ + "OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION": "1234", + }), + ).RunTestWithBp(t, ` + android_app { + name: "com.android.foo", + srcs: ["a.java"], + sdk_version: "current", + min_sdk_version: "31", + } + `) + foo := result.ModuleForTests("com.android.foo", "android_common").Rule("manifestFixer") + android.AssertStringDoesNotContain(t, + "com.android.foo: expected manifest fixer to not set override-placeholder-version", + foo.BuildParams.Args["args"], + "--override-placeholder-version", + ) +} diff --git a/java/base.go b/java/base.go index 991132321..caac98abc 100644 --- a/java/base.go +++ b/java/base.go @@ -17,9 +17,12 @@ package java import ( "fmt" "path/filepath" + "reflect" + "slices" "strconv" "strings" + "github.com/google/blueprint" "github.com/google/blueprint/pathtools" "github.com/google/blueprint/proptools" @@ -79,12 +82,18 @@ type CommonProperties struct { // list of java libraries that will be compiled into the resulting jar Static_libs []string `android:"arch_variant"` + // list of java libraries that should not be used to build this module + Exclude_static_libs []string `android:"arch_variant"` + // manifest file to be included in resulting jar Manifest *string `android:"path"` // if not blank, run jarjar using the specified rules file Jarjar_rules *string `android:"path,arch_variant"` + // if not blank, used as prefix to generate repackage rule + Jarjar_prefix *string + // If not blank, set the java version passed to javac as -source and -target Java_version *string @@ -129,7 +138,7 @@ type CommonProperties struct { // supported at compile time. It should only be needed to compile tests in // packages that exist in libcore and which are inconvenient to move // elsewhere. - Patch_module *string `android:"arch_variant"` + Patch_module *string Jacoco struct { // List of classes to include for instrumentation with jacoco to collect coverage @@ -184,6 +193,28 @@ type CommonProperties struct { // A list of java_library instances that provide additional hiddenapi annotations for the library. Hiddenapi_additional_annotations []string + + // Additional srcJars tacked in by GeneratedJavaLibraryModule + Generated_srcjars []android.Path `android:"mutated"` + + // intermediate aconfig cache file tacked in by GeneratedJavaLibraryModule + Aconfig_Cache_files []android.Path `android:"mutated"` + + // If true, then only the headers are built and not the implementation jar. + Headers_only *bool + + // A list of files or dependencies to make available to the build sandbox. This is + // useful if source files are symlinks, the targets of the symlinks must be listed here. + // Note that currently not all actions implemented by android_apps are sandboxed, so you + // may only see this being necessary in lint builds. + Compile_data []string `android:"path"` + + // Property signifying whether the module compiles stubs or not. + // Should be set to true when srcs of this module are stub files. + // This property does not need to be set to true when the module depends on + // the stubs via libs, but should be set to true when the module depends on + // the stubs via static libs. + Is_stubs_module *bool } // Properties that are specific to device modules. Host module factories should not add these when @@ -198,10 +229,6 @@ type DeviceProperties struct { // 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 - // 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 @@ -274,13 +301,17 @@ type DeviceProperties struct { HiddenAPIFlagFileProperties } -// Device properties that can be overridden by overriding module (e.g. override_android_app) -type OverridableDeviceProperties struct { +// Properties that can be overridden by overriding module (e.g. override_android_app) +type OverridableProperties 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 + + // 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 } // Functionality common to Module and Import @@ -395,7 +426,6 @@ type Module struct { android.ModuleBase android.DefaultableModuleBase android.ApexModuleBase - android.BazelModuleBase // Functionality common to Module and Import. embeddableInModuleAndImport @@ -404,12 +434,15 @@ type Module struct { protoProperties android.ProtoProperties deviceProperties DeviceProperties - overridableDeviceProperties OverridableDeviceProperties + overridableProperties OverridableProperties + sourceProperties android.SourceProperties // jar file containing header classes including static library dependencies, suitable for // inserting into the bootclasspath/classpath of another compile headerJarFile android.Path + repackagedHeaderJarFile android.Path + // jar file containing implementation classes including static library dependencies but no // resources implementationJarFile android.Path @@ -421,6 +454,9 @@ type Module struct { srcJarArgs []string srcJarDeps android.Paths + // the source files of this module and all its static dependencies + transitiveSrcFiles *android.DepSet[android.Path] + // jar file containing implementation classes and resources including static library // dependencies implementationAndResourcesJar android.Path @@ -471,6 +507,9 @@ type Module struct { // expanded Jarjar_rules expandJarjarRules android.Path + // jarjar rule for inherited jarjar rules + repackageJarjarRules android.Path + // Extra files generated by the module type to be added as java resources. extraResources android.Paths @@ -483,9 +522,6 @@ type Module struct { // list of the xref extraction files kytheFiles android.Paths - // Collect the module directory for IDE info in java/jdeps.go. - modulePaths []string - hideApexVariantFromMake bool sdkVersion android.SdkSpec @@ -493,6 +529,36 @@ type Module struct { maxSdkVersion android.ApiLevel sourceExtensions []string + + annoSrcJars android.Paths + + // output file name based on Stem property. + // This should be set in every ModuleWithStem's GenerateAndroidBuildActions + // or the module should override Stem(). + stem string + + // Values that will be set in the JarJarProvider data for jarjar repackaging, + // and merged with our dependencies' rules. + jarjarRenameRules map[string]string + + stubsLinkType StubsLinkType + + // Paths to the aconfig intermediate cache files that are provided by the + // java_aconfig_library or java_library modules that are statically linked + // to this module. Does not contain cache files from all transitive dependencies. + aconfigCacheFiles android.Paths +} + +var _ android.InstallableModule = (*Module)(nil) + +// To satisfy the InstallableModule interface +func (j *Module) EnforceApiContainerChecks() bool { + return true +} + +// Overrides android.ModuleBase.InstallInProduct() +func (j *Module) InstallInProduct() bool { + return j.ProductSpecific() } func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error { @@ -551,9 +617,21 @@ func (j *Module) checkPlatformAPI(ctx android.ModuleContext) { } } +func (j *Module) checkHeadersOnly(ctx android.ModuleContext) { + if _, ok := ctx.Module().(android.SdkContext); ok { + headersOnly := proptools.Bool(j.properties.Headers_only) + installable := proptools.Bool(j.properties.Installable) + + if headersOnly && installable { + ctx.PropertyErrorf("headers_only", "This module has conflicting settings. headers_only is true which, which means this module doesn't generate an implementation jar. However installable is set to true.") + } + } +} + func (j *Module) addHostProperties() { j.AddProperties( &j.properties, + &j.overridableProperties, &j.protoProperties, &j.usesLibraryProperties, ) @@ -563,7 +641,6 @@ func (j *Module) addHostAndDeviceProperties() { j.addHostProperties() j.AddProperties( &j.deviceProperties, - &j.overridableDeviceProperties, &j.dexer.dexProperties, &j.dexpreoptProperties, &j.linter.properties, @@ -581,31 +658,24 @@ func (j *Module) provideHiddenAPIPropertyInfo(ctx android.ModuleContext) { // Populate with package rules from the properties. hiddenAPIInfo.extractPackageRulesFromProperties(&j.deviceProperties.HiddenAPIPackageProperties) - ctx.SetProvider(hiddenAPIPropertyInfoProvider, hiddenAPIInfo) + android.SetProvider(ctx, hiddenAPIPropertyInfoProvider, hiddenAPIInfo) } -func (j *Module) OutputFiles(tag string) (android.Paths, error) { - switch tag { - case "": - return append(android.Paths{j.outputFile}, j.extraOutputFiles...), nil - case android.DefaultDistTag: - return android.Paths{j.outputFile}, nil - case ".jar": - return android.Paths{j.implementationAndResourcesJar}, nil - case ".hjar": - return android.Paths{j.headerJarFile}, nil - case ".proguard_map": - if j.dexer.proguardDictionary.Valid() { - return android.Paths{j.dexer.proguardDictionary.Path()}, nil - } - return nil, fmt.Errorf("%q was requested, but no output file was found.", tag) - default: - return nil, fmt.Errorf("unsupported module reference tag %q", tag) +// helper method for java modules to set OutputFilesProvider +func setOutputFiles(ctx android.ModuleContext, m Module) { + ctx.SetOutputFiles(append(android.Paths{m.outputFile}, m.extraOutputFiles...), "") + ctx.SetOutputFiles(android.Paths{m.outputFile}, android.DefaultDistTag) + ctx.SetOutputFiles(android.Paths{m.implementationAndResourcesJar}, ".jar") + ctx.SetOutputFiles(android.Paths{m.headerJarFile}, ".hjar") + if m.dexer.proguardDictionary.Valid() { + ctx.SetOutputFiles(android.Paths{m.dexer.proguardDictionary.Path()}, ".proguard_map") + } + ctx.SetOutputFiles(m.properties.Generated_srcjars, ".generated_srcjars") + if m.linter.outputs.xml != nil { + ctx.SetOutputFiles(android.Paths{m.linter.outputs.xml}, ".lint") } } -var _ android.OutputFileProducer = (*Module)(nil) - func InitJavaModule(module android.DefaultableModule, hod android.HostOrDeviceSupported) { initJavaModule(module, hod, false) } @@ -641,8 +711,9 @@ func (j *Module) shouldInstrumentInApex(ctx android.BaseModuleContext) bool { // Force enable the instrumentation for java code that is built for APEXes ... // except for the jacocoagent itself (because instrumenting jacocoagent using jacocoagent // doesn't make sense) or framework libraries (e.g. libraries found in the InstrumentFrameworkModules list) unless EMMA_INSTRUMENT_FRAMEWORK is true. - apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) isJacocoAgent := ctx.ModuleName() == "jacocoagent" + if j.DirectlyInAnyApex() && !isJacocoAgent && !apexInfo.IsForPlatform() { if !inList(ctx.ModuleName(), config.InstrumentFrameworkModules) { return true @@ -666,12 +737,16 @@ func (j *Module) SystemModules() string { } func (j *Module) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel { - if j.deviceProperties.Min_sdk_version != nil { - return android.ApiLevelFrom(ctx, *j.deviceProperties.Min_sdk_version) + if j.overridableProperties.Min_sdk_version != nil { + return android.ApiLevelFrom(ctx, *j.overridableProperties.Min_sdk_version) } return j.SdkVersion(ctx).ApiLevel } +func (j *Module) GetDeviceProperties() *DeviceProperties { + return &j.deviceProperties +} + func (j *Module) MaxSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel { if j.deviceProperties.Max_sdk_version != nil { return android.ApiLevelFrom(ctx, *j.deviceProperties.Max_sdk_version) @@ -724,12 +799,14 @@ func (j *Module) deps(ctx android.BottomUpMutatorContext) { } libDeps := ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...) + + j.properties.Static_libs = android.RemoveListFromList(j.properties.Static_libs, j.properties.Exclude_static_libs) ctx.AddVariationDependencies(nil, staticLibTag, j.properties.Static_libs...) // Add dependency on libraries that provide additional hidden api annotations. ctx.AddVariationDependencies(nil, hiddenApiAnnotationsTag, j.properties.Hiddenapi_additional_annotations...) - if ctx.DeviceConfig().VndkVersion() != "" && ctx.Config().EnforceInterPartitionJavaSdkLibrary() { + if ctx.Config().EnforceInterPartitionJavaSdkLibrary() { // Require java_sdk_library at inter-partition java dependency to ensure stable // interface between partitions. If inter-partition java_library dependency is detected, // raise build error because java_library doesn't have a stable interface. @@ -762,9 +839,11 @@ func (j *Module) deps(ctx android.BottomUpMutatorContext) { if dep != nil { if component, ok := dep.(SdkLibraryComponentDependency); ok { if lib := component.OptionalSdkLibraryImplementation(); lib != nil { - // Add library as optional if it's one of the optional compatibility libs. + // Add library as optional if it's one of the optional compatibility libs or it's + // explicitly listed in the optional_uses_libs property. tag := usesLibReqTag - if android.InList(*lib, dexpreopt.OptionalCompatUsesLibs) { + if android.InList(*lib, dexpreopt.OptionalCompatUsesLibs) || + android.InList(*lib, j.usesLibrary.usesLibraryProperties.Optional_uses_libs) { tag = usesLibOptTag } ctx.AddVariationDependencies(nil, tag, *lib) @@ -961,8 +1040,16 @@ func (j *Module) collectJavacFlags( ctx android.ModuleContext, flags javaBuilderFlags, srcFiles android.Paths) javaBuilderFlags { // javac flags. javacFlags := j.properties.Javacflags + var needsDebugInfo bool + + needsDebugInfo = false + for _, flag := range javacFlags { + if strings.HasPrefix(flag, "-g") { + needsDebugInfo = true + } + } - if ctx.Config().MinimizeJavaDebugInfo() && !ctx.Host() { + if ctx.Config().MinimizeJavaDebugInfo() && !ctx.Host() && !needsDebugInfo { // For non-host binaries, override the -g flag passed globally to remove // local variable debug info to reduce disk and memory usage. javacFlags = append(javacFlags, "-g:source,lines") @@ -971,44 +1058,18 @@ func (j *Module) collectJavacFlags( if flags.javaVersion.usesJavaModules() { javacFlags = append(javacFlags, j.properties.Openjdk9.Javacflags...) + } else if len(j.properties.Openjdk9.Javacflags) > 0 { + // java version defaults higher than openjdk 9, these conditionals should no longer be necessary + ctx.PropertyErrorf("openjdk9.javacflags", "JDK version defaults to higher than 9") + } + if flags.javaVersion.usesJavaModules() { if j.properties.Patch_module != nil { // 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().SoongOutDir()} - // b/150878007 - // - // Workaround to support *Bazel-executed* JDK9 javac in Bazel's - // execution root for --patch-module. If this javac command line is - // invoked within Bazel's execution root working directory, the top - // level directories (e.g. libcore/, tools/, frameworks/) are all - // symlinks. JDK9 javac does not traverse into symlinks, which causes - // --patch-module to fail source file lookups when invoked in the - // execution root. - // - // Short of patching javac or enumerating *all* directories as possible - // input dirs, manually add the top level dir of the source files to be - // compiled. - topLevelDirs := map[string]bool{} - for _, srcFilePath := range srcFiles { - srcFileParts := strings.Split(srcFilePath.String(), "/") - // Ignore source files that are already in the top level directory - // as well as generated files in the out directory. The out - // directory may be an absolute path, which means srcFileParts[0] is the - // empty string, so check that as well. Note that "out" in Bazel's execution - // root is *not* a symlink, which doesn't cause problems for --patch-modules - // anyway, so it's fine to not apply this workaround for generated - // source files. - if len(srcFileParts) > 1 && - srcFileParts[0] != "" && - srcFileParts[0] != "out" { - topLevelDirs[srcFileParts[0]] = true - } - } - patchPaths = append(patchPaths, android.SortedKeys(topLevelDirs)...) - classPath := flags.classpath.FormJavaClassPath("") if classPath != "" { patchPaths = append(patchPaths, classPath) @@ -1036,7 +1097,24 @@ func (j *Module) AddJSONData(d *map[string]interface{}) { } -func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { +func (j *Module) addGeneratedSrcJars(path android.Path) { + j.properties.Generated_srcjars = append(j.properties.Generated_srcjars, path) +} + +func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspathJars, extraCombinedJars android.Paths) { + + // Auto-propagating jarjar rules + jarjarProviderData := j.collectJarJarRules(ctx) + if jarjarProviderData != nil { + android.SetProvider(ctx, JarJarProvider, *jarjarProviderData) + text := getJarJarRuleText(jarjarProviderData) + if text != "" { + ruleTextFile := android.PathForModuleOut(ctx, "repackaged-jarjar", "repackaging.txt") + android.WriteFileRule(ctx, ruleTextFile, text) + j.repackageJarjarRules = ruleTextFile + } + } + j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs) deps := j.collectDeps(ctx) @@ -1044,6 +1122,9 @@ 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...) + } else if len(j.properties.Openjdk9.Javacflags) > 0 { + // java version defaults higher than openjdk 9, these conditionals should no longer be necessary + ctx.PropertyErrorf("openjdk9.srcs", "JDK version defaults to higher than 9") } srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs) @@ -1074,16 +1155,15 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { srcJars := srcFiles.FilterByExt(".srcjar") srcJars = append(srcJars, deps.srcJars...) - if aaptSrcJar != nil { - srcJars = append(srcJars, aaptSrcJar) - } + srcJars = append(srcJars, extraSrcJars...) + srcJars = append(srcJars, j.properties.Generated_srcjars...) srcFiles = srcFiles.FilterOutByExt(".srcjar") if j.properties.Jarjar_rules != nil { j.expandJarjarRules = android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules) } - jarName := ctx.ModuleName() + ".jar" + jarName := j.Stem() + ".jar" var uniqueJavaFiles android.Paths set := make(map[string]bool) @@ -1105,6 +1185,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { uniqueSrcFiles = append(uniqueSrcFiles, uniqueJavaFiles...) uniqueSrcFiles = append(uniqueSrcFiles, uniqueKtFiles...) j.uniqueSrcFiles = uniqueSrcFiles + android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: uniqueSrcFiles.Strings()}) // 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 @@ -1118,6 +1199,45 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { var kotlinJars android.Paths var kotlinHeaderJars android.Paths + // Prepend extraClasspathJars to classpath so that the resource processor R.jar comes before + // any dependencies so that it can override any non-final R classes from dependencies with the + // final R classes from the app. + flags.classpath = append(android.CopyOf(extraClasspathJars), flags.classpath...) + + j.aconfigCacheFiles = append(deps.aconfigProtoFiles, j.properties.Aconfig_Cache_files...) + + // If compiling headers then compile them and skip the rest + if proptools.Bool(j.properties.Headers_only) { + if srcFiles.HasExt(".kt") { + ctx.ModuleErrorf("Compiling headers_only with .kt not supported") + } + if ctx.Config().IsEnvFalse("TURBINE_ENABLED") || disableTurbine { + ctx.ModuleErrorf("headers_only is enabled but Turbine is disabled.") + } + + _, j.headerJarFile, _ = + j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName, + extraCombinedJars) + if ctx.Failed() { + return + } + + android.SetProvider(ctx, JavaInfoProvider, JavaInfo{ + HeaderJars: android.PathsIfNonNil(j.headerJarFile), + TransitiveLibsHeaderJars: j.transitiveLibsHeaderJars, + TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars, + AidlIncludeDirs: j.exportAidlIncludeDirs, + ExportedPlugins: j.exportedPluginJars, + ExportedPluginClasses: j.exportedPluginClasses, + ExportedPluginDisableTurbine: j.exportedDisableTurbine, + StubsLinkType: j.stubsLinkType, + AconfigIntermediateCacheOutputPaths: deps.aconfigProtoFiles, + }) + + j.outputFile = j.headerJarFile + return + } + 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 @@ -1178,10 +1298,12 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { return } + kotlinJarPath := j.repackageFlagsIfNecessary(ctx, kotlinJar.OutputPath, jarName, "kotlinc") + // Make javac rule depend on the kotlinc rule flags.classpath = append(classpath{kotlinHeaderJar}, flags.classpath...) - kotlinJars = append(kotlinJars, kotlinJar) + kotlinJars = append(kotlinJars, kotlinJarPath) kotlinHeaderJars = append(kotlinHeaderJars, kotlinHeaderJar) // Jar kotlin classes into the final jar after javac @@ -1196,7 +1318,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { } } - jars := append(android.Paths(nil), kotlinJars...) + jars := slices.Clone(kotlinJars) j.compiledSrcJars = srcJars @@ -1211,8 +1333,9 @@ 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. } - headerJarFileWithoutDepsOrJarjar, j.headerJarFile = - j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName, kotlinHeaderJars) + extraJars := append(slices.Clone(kotlinHeaderJars), extraCombinedJars...) + headerJarFileWithoutDepsOrJarjar, j.headerJarFile, j.repackagedHeaderJarFile = + j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName, extraJars) if ctx.Failed() { return } @@ -1241,8 +1364,9 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { // this module, or else we could have duplicated errorprone messages. errorproneFlags := enableErrorproneFlags(flags) errorprone := android.PathForModuleOut(ctx, "errorprone", jarName) + errorproneAnnoSrcJar := android.PathForModuleOut(ctx, "errorprone", "anno.srcjar") - transformJavaToClasses(ctx, errorprone, -1, uniqueJavaFiles, srcJars, errorproneFlags, nil, + transformJavaToClasses(ctx, errorprone, -1, uniqueJavaFiles, srcJars, errorproneAnnoSrcJar, errorproneFlags, nil, "errorprone", "errorprone") extraJarDeps = append(extraJarDeps, errorprone) @@ -1259,16 +1383,26 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { for idx, shardSrc := range shardSrcs { classes := j.compileJavaClasses(ctx, jarName, idx, shardSrc, nil, flags, extraJarDeps) + classes = j.repackageFlagsIfNecessary(ctx, classes, jarName, "javac-"+strconv.Itoa(idx)) jars = append(jars, classes) } } + // Assume approximately 5 sources per srcjar. + // For framework-minus-apex in AOSP at the time this was written, there are 266 srcjars, with a mean + // of 5.8 sources per srcjar, but a median of 1, a standard deviation of 10, and a max of 48 source files. if len(srcJars) > 0 { - classes := j.compileJavaClasses(ctx, jarName, len(shardSrcs), - nil, srcJars, flags, extraJarDeps) - jars = append(jars, classes) + startIdx := len(shardSrcs) + shardSrcJarsList := android.ShardPaths(srcJars, shardSize/5) + for idx, shardSrcJars := range shardSrcJarsList { + classes := j.compileJavaClasses(ctx, jarName, startIdx+idx, + nil, shardSrcJars, flags, extraJarDeps) + classes = j.repackageFlagsIfNecessary(ctx, classes, jarName, "javac-"+strconv.Itoa(startIdx+idx)) + jars = append(jars, classes) + } } } else { classes := j.compileJavaClasses(ctx, jarName, -1, uniqueJavaFiles, srcJars, flags, extraJarDeps) + classes = j.repackageFlagsIfNecessary(ctx, classes, jarName, "javac") jars = append(jars, classes) } if ctx.Failed() { @@ -1276,6 +1410,8 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { } } + jars = append(jars, extraCombinedJars...) + j.srcJarArgs, j.srcJarDeps = resourcePathsToJarArgs(srcFiles), srcFiles var includeSrcJar android.WritablePath @@ -1372,12 +1508,20 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { // prebuilt dependencies until we support modules in the platform build, so there shouldn't be // any if len(jars) == 1. + // moduleStubLinkType determines if the module is the TopLevelStubLibrary generated + // from sdk_library. The TopLevelStubLibrary contains only one static lib, + // either with .from-source or .from-text suffix. + // outputFile should be agnostic to the build configuration, + // thus "combine" the single static lib in order to prevent the static lib from being exposed + // to the copy rules. + stub, _ := moduleStubLinkType(ctx.ModuleName()) + // Transform the single path to the jar into an OutputPath as that is required by the following // code. - if moduleOutPath, ok := jars[0].(android.ModuleOutPath); ok { + if moduleOutPath, ok := jars[0].(android.ModuleOutPath); ok && !stub { // The path contains an embedded OutputPath so reuse that. outputFile = moduleOutPath.OutputPath - } else if outputPath, ok := jars[0].(android.OutputPath); ok { + } else if outputPath, ok := jars[0].(android.OutputPath); ok && !stub { // The path is an OutputPath so reuse it directly. outputFile = outputPath } else { @@ -1446,7 +1590,13 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { j.implementationJarFile = outputFile if j.headerJarFile == nil { - j.headerJarFile = j.implementationJarFile + // If this module couldn't generate a header jar (for example due to api generating annotation processors) + // then use the implementation jar. Run it through zip2zip first to remove any files in META-INF/services + // so that javac on modules that depend on this module don't pick up annotation processors (which may be + // missing their implementations) from META-INF/services/javax.annotation.processing.Processor. + headerJarFile := android.PathForModuleOut(ctx, "javac-header", jarName) + convertImplementationJarToHeaderJar(ctx, j.implementationJarFile, headerJarFile) + j.headerJarFile = headerJarFile } // enforce syntax check to jacoco filters for any build (http://b/183622051) @@ -1473,7 +1623,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { // Enable dex compilation for the APEX variants, unless it is disabled explicitly compileDex := j.dexProperties.Compile_dex - apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) if j.DirectlyInAnyApex() && !apexInfo.IsForPlatform() { if compileDex == nil { compileDex = proptools.BoolPtr(true) @@ -1486,7 +1636,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { if ctx.Device() && (Bool(j.properties.Installable) || Bool(compileDex)) { if j.hasCode(ctx) { if j.shouldInstrumentStatic(ctx) { - j.dexer.extraProguardFlagFiles = append(j.dexer.extraProguardFlagFiles, + j.dexer.extraProguardFlagsFiles = append(j.dexer.extraProguardFlagsFiles, android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags")) } // Dex compilation @@ -1498,11 +1648,28 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { classesJar: implementationAndResourcesJar, jarName: jarName, } - dexOutputFile = j.dexer.compileDex(ctx, params) + if j.GetProfileGuided() && j.optimizeOrObfuscateEnabled() && !j.EnableProfileRewriting() { + ctx.PropertyErrorf("enable_profile_rewriting", + "Enable_profile_rewriting must be true when profile_guided dexpreopt and R8 optimization/obfuscation is turned on. The attached profile should be sourced from an unoptimized/unobfuscated APK.", + ) + } + if j.EnableProfileRewriting() { + profile := j.GetProfile() + if profile == "" || !j.GetProfileGuided() { + ctx.PropertyErrorf("enable_profile_rewriting", "Profile and Profile_guided must be set when enable_profile_rewriting is true") + } + params.artProfileInput = &profile + } + dexOutputFile, dexArtProfileOutput := j.dexer.compileDex(ctx, params) if ctx.Failed() { return } + // If r8/d8 provides a profile that matches the optimized dex, use that for dexpreopt. + if dexArtProfileOutput != nil { + j.dexpreopter.SetRewrittenProfile(*dexArtProfileOutput) + } + // merge dex jar with resources if necessary if j.resourceJar != nil { jars := android.Paths{dexOutputFile, j.resourceJar} @@ -1511,7 +1678,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { false, nil, nil) if *j.dexProperties.Uncompress_dex { combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName).OutputPath - TransformZipAlign(ctx, combinedAlignedJar, combinedJar) + TransformZipAlign(ctx, combinedAlignedJar, combinedJar, nil) dexOutputFile = combinedAlignedJar } else { dexOutputFile = combinedJar @@ -1528,7 +1695,11 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { j.dexJarFile = makeDexJarPathFromPath(dexOutputFile) // Dexpreopting - j.dexpreopt(ctx, dexOutputFile) + libName := android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()) + if j.SdkLibraryName() != nil && strings.HasSuffix(ctx.ModuleName(), ".impl") { + libName = strings.TrimSuffix(libName, ".impl") + } + j.dexpreopt(ctx, libName, dexOutputFile) outputFile = dexOutputFile } else { @@ -1546,30 +1717,11 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { } if ctx.Device() { - lintSDKVersion := func(apiLevel android.ApiLevel) int { + lintSDKVersion := func(apiLevel android.ApiLevel) android.ApiLevel { if !apiLevel.IsPreview() { - return apiLevel.FinalInt() + return apiLevel } else { - // When running metalava, we pass --version-codename. When that value - // is not REL, metalava will add 1 to the --current-version argument. - // On old branches, PLATFORM_SDK_VERSION is the latest version (for that - // branch) and the codename is REL, except potentially on the most - // recent non-master branch. On that branch, it goes through two other - // phases before it gets to the phase previously described: - // - PLATFORM_SDK_VERSION has not been updated yet, and the codename - // is not rel. This happens for most of the internal branch's life - // while the branch has been cut but is still under active development. - // - PLATFORM_SDK_VERSION has been set, but the codename is still not - // REL. This happens briefly during the release process. During this - // state the code to add --current-version is commented out, and then - // that commenting out is reverted after the codename is set to REL. - // On the master branch, the PLATFORM_SDK_VERSION always represents a - // prior version and the codename is always non-REL. - // - // We need to add one here to match metalava adding 1. Technically - // this means that in the state described in the second bullet point - // above, this number is 1 higher than it should be. - return ctx.Config().PlatformSdkVersion().FinalInt() + 1 + return ctx.Config().DefaultAppTargetSdk(ctx) } } @@ -1584,28 +1736,35 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { j.linter.compileSdkKind = j.SdkVersion(ctx).Kind j.linter.javaLanguageLevel = flags.javaVersion.String() j.linter.kotlinLanguageLevel = "1.3" + j.linter.compile_data = android.PathsForModuleSrc(ctx, j.properties.Compile_data) if !apexInfo.IsForPlatform() && ctx.Config().UnbundledBuildApps() { j.linter.buildModuleReportZip = true } j.linter.lint(ctx) } + j.collectTransitiveSrcFiles(ctx, srcFiles) + ctx.CheckbuildFile(outputFile) - ctx.SetProvider(JavaInfoProvider, JavaInfo{ - HeaderJars: android.PathsIfNonNil(j.headerJarFile), - TransitiveLibsHeaderJars: j.transitiveLibsHeaderJars, - TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars, - ImplementationAndResourcesJars: android.PathsIfNonNil(j.implementationAndResourcesJar), - ImplementationJars: android.PathsIfNonNil(j.implementationJarFile), - ResourceJars: android.PathsIfNonNil(j.resourceJar), - AidlIncludeDirs: j.exportAidlIncludeDirs, - SrcJarArgs: j.srcJarArgs, - SrcJarDeps: j.srcJarDeps, - ExportedPlugins: j.exportedPluginJars, - ExportedPluginClasses: j.exportedPluginClasses, - ExportedPluginDisableTurbine: j.exportedDisableTurbine, - JacocoReportClassesFile: j.jacocoReportClassesFile, + android.SetProvider(ctx, JavaInfoProvider, JavaInfo{ + HeaderJars: android.PathsIfNonNil(j.headerJarFile), + RepackagedHeaderJars: android.PathsIfNonNil(j.repackagedHeaderJarFile), + TransitiveLibsHeaderJars: j.transitiveLibsHeaderJars, + TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars, + ImplementationAndResourcesJars: android.PathsIfNonNil(j.implementationAndResourcesJar), + ImplementationJars: android.PathsIfNonNil(j.implementationJarFile), + ResourceJars: android.PathsIfNonNil(j.resourceJar), + AidlIncludeDirs: j.exportAidlIncludeDirs, + SrcJarArgs: j.srcJarArgs, + SrcJarDeps: j.srcJarDeps, + TransitiveSrcFiles: j.transitiveSrcFiles, + ExportedPlugins: j.exportedPluginJars, + ExportedPluginClasses: j.exportedPluginClasses, + ExportedPluginDisableTurbine: j.exportedDisableTurbine, + JacocoReportClassesFile: j.jacocoReportClassesFile, + StubsLinkType: j.stubsLinkType, + AconfigIntermediateCacheOutputPaths: j.aconfigCacheFiles, }) // Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource @@ -1616,6 +1775,52 @@ func (j *Module) useCompose() bool { return android.InList("androidx.compose.runtime_runtime", j.properties.Static_libs) } +func collectDepProguardSpecInfo(ctx android.ModuleContext) (transitiveProguardFlags, transitiveUnconditionalExportedFlags []*android.DepSet[android.Path]) { + ctx.VisitDirectDeps(func(m android.Module) { + depProguardInfo, _ := android.OtherModuleProvider(ctx, m, ProguardSpecInfoProvider) + depTag := ctx.OtherModuleDependencyTag(m) + + if depProguardInfo.UnconditionallyExportedProguardFlags != nil { + transitiveUnconditionalExportedFlags = append(transitiveUnconditionalExportedFlags, depProguardInfo.UnconditionallyExportedProguardFlags) + transitiveProguardFlags = append(transitiveProguardFlags, depProguardInfo.UnconditionallyExportedProguardFlags) + } + + if depTag == staticLibTag && depProguardInfo.ProguardFlagsFiles != nil { + transitiveProguardFlags = append(transitiveProguardFlags, depProguardInfo.ProguardFlagsFiles) + } + }) + + return transitiveProguardFlags, transitiveUnconditionalExportedFlags +} + +func (j *Module) collectProguardSpecInfo(ctx android.ModuleContext) ProguardSpecInfo { + transitiveProguardFlags, transitiveUnconditionalExportedFlags := collectDepProguardSpecInfo(ctx) + + directUnconditionalExportedFlags := android.Paths{} + proguardFlagsForThisModule := android.PathsForModuleSrc(ctx, j.dexProperties.Optimize.Proguard_flags_files) + exportUnconditionally := proptools.Bool(j.dexProperties.Optimize.Export_proguard_flags_files) + if exportUnconditionally { + // if we explicitly export, then our unconditional exports are the same as our transitive flags + transitiveUnconditionalExportedFlags = transitiveProguardFlags + directUnconditionalExportedFlags = proguardFlagsForThisModule + } + + return ProguardSpecInfo{ + Export_proguard_flags_files: exportUnconditionally, + ProguardFlagsFiles: android.NewDepSet[android.Path]( + android.POSTORDER, + proguardFlagsForThisModule, + transitiveProguardFlags, + ), + UnconditionallyExportedProguardFlags: android.NewDepSet[android.Path]( + android.POSTORDER, + directUnconditionalExportedFlags, + transitiveUnconditionalExportedFlags, + ), + } + +} + // 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 { @@ -1635,20 +1840,26 @@ func (j *Module) compileJavaClasses(ctx android.ModuleContext, jarName string, i srcFiles, srcJars android.Paths, flags javaBuilderFlags, extraJarDeps android.Paths) android.WritablePath { kzipName := pathtools.ReplaceExtension(jarName, "kzip") + annoSrcJar := android.PathForModuleOut(ctx, "javac", "anno.srcjar") if idx >= 0 { kzipName = strings.TrimSuffix(jarName, filepath.Ext(jarName)) + strconv.Itoa(idx) + ".kzip" + annoSrcJar = android.PathForModuleOut(ctx, "javac", "anno-"+strconv.Itoa(idx)+".srcjar") jarName += strconv.Itoa(idx) } classes := android.PathForModuleOut(ctx, "javac", jarName).OutputPath - TransformJavaToClasses(ctx, classes, idx, srcFiles, srcJars, flags, extraJarDeps) + TransformJavaToClasses(ctx, classes, idx, srcFiles, srcJars, annoSrcJar, flags, extraJarDeps) - if ctx.Config().EmitXrefRules() { + if ctx.Config().EmitXrefRules() && ctx.Module() == ctx.PrimaryModule() { extractionFile := android.PathForModuleOut(ctx, kzipName) emitXrefRule(ctx, extractionFile, idx, srcFiles, srcJars, flags, extraJarDeps) j.kytheFiles = append(j.kytheFiles, extractionFile) } + if len(flags.processorPath) > 0 { + j.annoSrcJars = append(j.annoSrcJars, annoSrcJar) + } + return classes } @@ -1679,7 +1890,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, jarjarAndDepsHeaderJar android.Path) { + extraJars android.Paths) (headerJar, jarjarAndDepsHeaderJar, jarjarAndDepsRepackagedHeaderJar android.Path) { var jars android.Paths if len(srcFiles) > 0 || len(srcJars) > 0 { @@ -1687,7 +1898,7 @@ func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars turbineJar := android.PathForModuleOut(ctx, "turbine", jarName) TransformJavaToHeaderClasses(ctx, turbineJar, srcFiles, srcJars, flags) if ctx.Failed() { - return nil, nil + return nil, nil, nil } jars = append(jars, turbineJar) headerJar = turbineJar @@ -1712,11 +1923,22 @@ func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars TransformJarJar(ctx, jarjarFile, jarjarAndDepsHeaderJar, j.expandJarjarRules) jarjarAndDepsHeaderJar = jarjarFile if ctx.Failed() { - return nil, nil + return nil, nil, nil } } - return headerJar, jarjarAndDepsHeaderJar + if j.repackageJarjarRules != nil { + repackagedJarjarFile := android.PathForModuleOut(ctx, "repackaged-turbine-jarjar", jarName) + TransformJarJar(ctx, repackagedJarjarFile, jarjarAndDepsHeaderJar, j.repackageJarjarRules) + jarjarAndDepsRepackagedHeaderJar = repackagedJarjarFile + if ctx.Failed() { + return nil, nil, nil + } + } else { + jarjarAndDepsRepackagedHeaderJar = jarjarAndDepsHeaderJar + } + + return headerJar, jarjarAndDepsHeaderJar, jarjarAndDepsRepackagedHeaderJar } func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags, @@ -1734,44 +1956,47 @@ func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags, type providesTransitiveHeaderJars struct { // set of header jars for all transitive libs deps - transitiveLibsHeaderJars *android.DepSet + transitiveLibsHeaderJars *android.DepSet[android.Path] // set of header jars for all transitive static libs deps - transitiveStaticLibsHeaderJars *android.DepSet + transitiveStaticLibsHeaderJars *android.DepSet[android.Path] } -func (j *providesTransitiveHeaderJars) TransitiveLibsHeaderJars() *android.DepSet { +func (j *providesTransitiveHeaderJars) TransitiveLibsHeaderJars() *android.DepSet[android.Path] { return j.transitiveLibsHeaderJars } -func (j *providesTransitiveHeaderJars) TransitiveStaticLibsHeaderJars() *android.DepSet { +func (j *providesTransitiveHeaderJars) TransitiveStaticLibsHeaderJars() *android.DepSet[android.Path] { return j.transitiveStaticLibsHeaderJars } func (j *providesTransitiveHeaderJars) collectTransitiveHeaderJars(ctx android.ModuleContext) { directLibs := android.Paths{} directStaticLibs := android.Paths{} - transitiveLibs := []*android.DepSet{} - transitiveStaticLibs := []*android.DepSet{} + transitiveLibs := []*android.DepSet[android.Path]{} + transitiveStaticLibs := []*android.DepSet[android.Path]{} ctx.VisitDirectDeps(func(module android.Module) { // don't add deps of the prebuilt version of the same library if ctx.ModuleName() == android.RemoveOptionalPrebuiltPrefix(module.Name()) { return } - dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo) - if dep.TransitiveLibsHeaderJars != nil { - transitiveLibs = append(transitiveLibs, dep.TransitiveLibsHeaderJars) - } - if dep.TransitiveStaticLibsHeaderJars != nil { - transitiveStaticLibs = append(transitiveStaticLibs, dep.TransitiveStaticLibsHeaderJars) - } - + dep, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider) tag := ctx.OtherModuleDependencyTag(module) _, isUsesLibDep := tag.(usesLibraryDependencyTag) if tag == libTag || tag == r8LibraryJarTag || isUsesLibDep { directLibs = append(directLibs, dep.HeaderJars...) } else if tag == staticLibTag { directStaticLibs = append(directStaticLibs, dep.HeaderJars...) + } else { + // Don't propagate transitive libs for other kinds of dependencies. + return + } + + if dep.TransitiveLibsHeaderJars != nil { + transitiveLibs = append(transitiveLibs, dep.TransitiveLibsHeaderJars) + } + if dep.TransitiveStaticLibsHeaderJars != nil { + transitiveStaticLibs = append(transitiveStaticLibs, dep.TransitiveStaticLibsHeaderJars) } }) j.transitiveLibsHeaderJars = android.NewDepSet(android.POSTORDER, directLibs, transitiveLibs) @@ -1792,7 +2017,7 @@ func (j *Module) ImplementationJars() android.Paths { return android.Paths{j.implementationJarFile} } -func (j *Module) DexJarBuildPath() OptionalDexJarPath { +func (j *Module) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath { return j.dexJarFile } @@ -1825,9 +2050,9 @@ func (j *Module) IDEInfo(dpInfo *android.IdeInfo) { if j.expandJarjarRules != nil { 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...) + dpInfo.SrcJars = append(dpInfo.SrcJars, j.annoSrcJars.Strings()...) } func (j *Module) CompilerDeps() []string { @@ -1865,13 +2090,31 @@ func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersi } func (j *Module) Stem() string { - return proptools.StringDefault(j.overridableDeviceProperties.Stem, j.Name()) + if j.stem == "" { + panic("Stem() called before stem property was set") + } + return j.stem } func (j *Module) JacocoReportClassesFile() android.Path { return j.jacocoReportClassesFile } +func (j *Module) collectTransitiveSrcFiles(ctx android.ModuleContext, mine android.Paths) { + var fromDeps []*android.DepSet[android.Path] + ctx.VisitDirectDeps(func(module android.Module) { + tag := ctx.OtherModuleDependencyTag(module) + if tag == staticLibTag { + depInfo, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider) + if depInfo.TransitiveSrcFiles != nil { + fromDeps = append(fromDeps, depInfo.TransitiveSrcFiles) + } + } + }) + + j.transitiveSrcFiles = android.NewDepSet(android.POSTORDER, mine, fromDeps) +} + func (j *Module) IsInstallable() bool { return Bool(j.properties.Installable) } @@ -1923,19 +2166,22 @@ type moduleWithSdkDep interface { func (m *Module) getSdkLinkType(ctx android.BaseModuleContext, name string) (ret sdkLinkType, stubs bool) { switch name { - case android.SdkCore.JavaLibraryName(ctx.Config()), "legacy.core.platform.api.stubs", "stable.core.platform.api.stubs", + case android.SdkCore.DefaultJavaLibraryName(), + "legacy.core.platform.api.stubs", + "stable.core.platform.api.stubs", "stub-annotations", "private-stub-annotations-jar", - "core-lambda-stubs", "core-generated-annotation-stubs": + "core-lambda-stubs", + "core-generated-annotation-stubs": return javaCore, true - case android.SdkPublic.JavaLibraryName(ctx.Config()): + case android.SdkPublic.DefaultJavaLibraryName(): return javaSdk, true - case android.SdkSystem.JavaLibraryName(ctx.Config()): + case android.SdkSystem.DefaultJavaLibraryName(): return javaSystem, true - case android.SdkModule.JavaLibraryName(ctx.Config()): + case android.SdkModule.DefaultJavaLibraryName(): return javaModule, true - case android.SdkSystemServer.JavaLibraryName(ctx.Config()): + case android.SdkSystemServer.DefaultJavaLibraryName(): return javaSystemServer, true - case android.SdkTest.JavaLibraryName(ctx.Config()): + case android.SdkTest.DefaultJavaLibraryName(): return javaSystem, true } @@ -2031,15 +2277,14 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { case staticLibTag: ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName) } - } else if ctx.OtherModuleHasProvider(module, JavaInfoProvider) { - dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo) - if sdkLinkType != javaPlatform && - ctx.OtherModuleHasProvider(module, SyspropPublicStubInfoProvider) { - // dep is a sysprop implementation library, but this module is not linking against - // the platform, so it gets the sysprop public stubs library instead. Replace - // dep with the JavaInfo from the SyspropPublicStubInfoProvider. - syspropDep := ctx.OtherModuleProvider(module, SyspropPublicStubInfoProvider).(SyspropPublicStubInfo) - dep = syspropDep.JavaInfo + } else if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok { + if sdkLinkType != javaPlatform { + if syspropDep, ok := android.OtherModuleProvider(ctx, module, SyspropPublicStubInfoProvider); ok { + // dep is a sysprop implementation library, but this module is not linking against + // the platform, so it gets the sysprop public stubs library instead. Replace + // dep with the JavaInfo from the SyspropPublicStubInfoProvider. + dep = syspropDep.JavaInfo + } } switch tag { case bootClasspathTag: @@ -2050,6 +2295,10 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { } deps.classpath = append(deps.classpath, dep.HeaderJars...) deps.dexClasspath = append(deps.dexClasspath, dep.HeaderJars...) + if len(dep.RepackagedHeaderJars) == 1 && !slices.Contains(dep.HeaderJars, dep.RepackagedHeaderJars[0]) { + deps.classpath = append(deps.classpath, dep.RepackagedHeaderJars...) + deps.dexClasspath = append(deps.dexClasspath, dep.RepackagedHeaderJars...) + } deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...) addPlugins(&deps, dep.ExportedPlugins, dep.ExportedPluginClasses...) deps.disableTurbine = deps.disableTurbine || dep.ExportedPluginDisableTurbine @@ -2069,6 +2318,7 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { // annotation processor that generates API is incompatible with the turbine // optimization. deps.disableTurbine = deps.disableTurbine || dep.ExportedPluginDisableTurbine + deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.AconfigIntermediateCacheOutputPaths...) case pluginTag: if plugin, ok := module.(*Plugin); ok { if plugin.pluginProperties.Processor_class != nil { @@ -2111,7 +2361,7 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { case syspropPublicStubDepTag: // This is a sysprop implementation library, forward the JavaInfoProvider from // the corresponding sysprop public stub library as SyspropPublicStubInfoProvider. - ctx.SetProvider(SyspropPublicStubInfoProvider, SyspropPublicStubInfo{ + android.SetProvider(ctx, SyspropPublicStubInfoProvider, SyspropPublicStubInfo{ JavaInfo: dep, }) } @@ -2127,6 +2377,11 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { deps.staticJars = append(deps.staticJars, dep.Srcs()...) deps.staticHeaderJars = append(deps.staticHeaderJars, dep.Srcs()...) } + } else if dep, ok := android.OtherModuleProvider(ctx, module, android.CodegenInfoProvider); ok { + switch tag { + case staticLibTag: + deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPaths...) + } } else { switch tag { case bootClasspathTag: @@ -2149,11 +2404,320 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { } addCLCFromDep(ctx, module, j.classLoaderContexts) + addMissingOptionalUsesLibsFromDep(ctx, module, &j.usesLibrary) }) return deps } +// Provider for jarjar renaming rules. +// +// Modules can set their jarjar renaming rules with addJarJarRenameRule, and those renamings will be +// passed to all rdeps. The typical way that these renamings will NOT be inherited is when a module +// links against stubs -- these are not passed through stubs. The classes will remain unrenamed on +// classes until a module with jarjar_prefix is reached, and all as yet unrenamed classes will then +// be renamed from that module. +// TODO: Add another property to suppress the forwarding of +type DependencyUse int + +const ( + RenameUseInvalid DependencyUse = iota + RenameUseInclude + RenameUseExclude +) + +type RenameUseElement struct { + DepName string + RenameUse DependencyUse + Why string // token for determining where in the logic the decision was made. +} + +type JarJarProviderData struct { + // Mapping of class names: original --> renamed. If the value is "", the class will be + // renamed by the next rdep that has the jarjar_prefix attribute (or this module if it has + // attribute). Rdeps of that module will inherit the renaming. + Rename map[string]string + RenameUse []RenameUseElement +} + +func (this JarJarProviderData) GetDebugString() string { + result := "" + for _, k := range android.SortedKeys(this.Rename) { + v := this.Rename[k] + if strings.Contains(k, "android.companion.virtual.flags.FakeFeatureFlagsImpl") { + result += k + "-->" + v + ";" + } + } + return result +} + +var JarJarProvider = blueprint.NewProvider[JarJarProviderData]() + +var overridableJarJarPrefix = "com.android.internal.hidden_from_bootclasspath" + +func init() { + android.SetJarJarPrefixHandler(mergeJarJarPrefixes) +} + +// BaseJarJarProviderData contains information that will propagate across dependencies regardless of +// whether they are java modules or not. +type BaseJarJarProviderData struct { + JarJarProviderData JarJarProviderData +} + +func (this BaseJarJarProviderData) GetDebugString() string { + return this.JarJarProviderData.GetDebugString() +} + +var BaseJarJarProvider = blueprint.NewProvider[BaseJarJarProviderData]() + +// mergeJarJarPrefixes is called immediately before module.GenerateAndroidBuildActions is called. +// Since there won't be a JarJarProvider, we create the BaseJarJarProvider if any of our deps have +// either JarJarProvider or BaseJarJarProvider. +func mergeJarJarPrefixes(ctx android.ModuleContext) { + mod := ctx.Module() + // Explicitly avoid propagating into some module types. + switch reflect.TypeOf(mod).String() { + case "*java.Droidstubs": + return + } + jarJarData := collectDirectDepsProviders(ctx) + if jarJarData != nil { + providerData := BaseJarJarProviderData{ + JarJarProviderData: *jarJarData, + } + android.SetProvider(ctx, BaseJarJarProvider, providerData) + } + +} + +// Add a jarjar renaming rule to this module, to be inherited to all dependent modules. +func (module *Module) addJarJarRenameRule(original string, renamed string) { + if module.jarjarRenameRules == nil { + module.jarjarRenameRules = make(map[string]string) + } + module.jarjarRenameRules[original] = renamed +} + +func collectDirectDepsProviders(ctx android.ModuleContext) (result *JarJarProviderData) { + // Gather repackage information from deps + // If the dep jas a JarJarProvider, it is used. Otherwise, any BaseJarJarProvider is used. + + module := ctx.Module() + moduleName := module.Name() + + ctx.VisitDirectDepsIgnoreBlueprint(func(m android.Module) { + tag := ctx.OtherModuleDependencyTag(m) + // This logic mirrors that in (*Module).collectDeps above. There are several places + // where we explicitly return RenameUseExclude, even though it is the default, to + // indicate that it has been verified to be the case. + // + // Note well: there are probably cases that are getting to the unconditional return + // and are therefore wrong. + shouldIncludeRenames := func() (DependencyUse, string) { + if moduleName == m.Name() { + return RenameUseInclude, "name" // If we have the same module name, include the renames. + } + if sc, ok := module.(android.SdkContext); ok { + if ctx.Device() { + sdkDep := decodeSdkDep(ctx, sc) + if !sdkDep.invalidVersion && sdkDep.useFiles { + return RenameUseExclude, "useFiles" + } + } + } + if IsJniDepTag(tag) || tag == certificateTag || tag == proguardRaiseTag { + return RenameUseExclude, "tags" + } + if _, ok := m.(SdkLibraryDependency); ok { + switch tag { + case sdkLibTag, libTag: + return RenameUseExclude, "sdklibdep" // matches collectDeps() + } + return RenameUseInvalid, "sdklibdep" // dep is not used in collectDeps() + } else if ji, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok { + switch ji.StubsLinkType { + case Stubs: + return RenameUseExclude, "info" + case Implementation: + return RenameUseInclude, "info" + default: + //fmt.Printf("collectDirectDepsProviders: %v -> %v StubsLinkType unknown\n", module, m) + // Fall through to the heuristic logic. + } + switch reflect.TypeOf(m).String() { + case "*java.GeneratedJavaLibraryModule": + // Probably a java_aconfig_library module. + // TODO: make this check better. + return RenameUseInclude, "reflect" + } + switch tag { + case bootClasspathTag: + return RenameUseExclude, "tagswitch" + case sdkLibTag, libTag, instrumentationForTag: + return RenameUseInclude, "tagswitch" + case java9LibTag: + return RenameUseExclude, "tagswitch" + case staticLibTag: + return RenameUseInclude, "tagswitch" + case pluginTag: + return RenameUseInclude, "tagswitch" + case errorpronePluginTag: + return RenameUseInclude, "tagswitch" + case exportedPluginTag: + return RenameUseInclude, "tagswitch" + case kotlinStdlibTag, kotlinAnnotationsTag: + return RenameUseExclude, "tagswitch" + case kotlinPluginTag: + return RenameUseInclude, "tagswitch" + default: + return RenameUseExclude, "tagswitch" + } + } else if _, ok := m.(android.SourceFileProducer); ok { + switch tag { + case sdkLibTag, libTag, staticLibTag: + return RenameUseInclude, "srcfile" + default: + return RenameUseExclude, "srcfile" + } + } else if _, ok := android.OtherModuleProvider(ctx, m, android.CodegenInfoProvider); ok { + return RenameUseInclude, "aconfig_declarations_group" + } else { + switch tag { + case bootClasspathTag: + return RenameUseExclude, "else" + case systemModulesTag: + return RenameUseInclude, "else" + } + } + // If we got here, choose the safer option, which may lead to a build failure, rather + // than runtime failures on the device. + return RenameUseExclude, "end" + } + + if result == nil { + result = &JarJarProviderData{ + Rename: make(map[string]string), + RenameUse: make([]RenameUseElement, 0), + } + } + how, why := shouldIncludeRenames() + result.RenameUse = append(result.RenameUse, RenameUseElement{DepName: m.Name(), RenameUse: how, Why: why}) + if how != RenameUseInclude { + // Nothing to merge. + return + } + + merge := func(theirs *JarJarProviderData) { + for orig, renamed := range theirs.Rename { + if preexisting, exists := (*result).Rename[orig]; !exists || preexisting == "" { + result.Rename[orig] = renamed + } else if preexisting != "" && renamed != "" && preexisting != renamed { + if strings.HasPrefix(preexisting, overridableJarJarPrefix) { + result.Rename[orig] = renamed + } else if !strings.HasPrefix(renamed, overridableJarJarPrefix) { + ctx.ModuleErrorf("1. Conflicting jarjar rules inherited for class: %s (%s and %s)", orig, renamed, preexisting, ctx.ModuleName(), m.Name()) + continue + } + } + } + } + if theirs, ok := android.OtherModuleProvider(ctx, m, JarJarProvider); ok { + merge(&theirs) + } else if theirs, ok := android.OtherModuleProvider(ctx, m, BaseJarJarProvider); ok { + // TODO: if every java.Module should have a JarJarProvider, and we find only the + // BaseJarJarProvider, then there is a bug. Consider seeing if m can be cast + // to java.Module. + merge(&theirs.JarJarProviderData) + } + }) + return +} + +func (this Module) GetDebugString() string { + return "sdk_version=" + proptools.String(this.deviceProperties.Sdk_version) +} + +// Merge the jarjar rules we inherit from our dependencies, any that have been added directly to +// us, and if it's been set, apply the jarjar_prefix property to rename them. +func (module *Module) collectJarJarRules(ctx android.ModuleContext) *JarJarProviderData { + // Gather repackage information from deps + result := collectDirectDepsProviders(ctx) + + // Update that with entries we've stored for ourself + for orig, renamed := range module.jarjarRenameRules { + if result == nil { + result = &JarJarProviderData{ + Rename: make(map[string]string), + } + } + if renamed != "" { + if preexisting, exists := (*result).Rename[orig]; exists && preexisting != renamed { + ctx.ModuleErrorf("Conflicting jarjar rules inherited for class: %s (%s and %s)", orig, renamed, preexisting) + continue + } + } + (*result).Rename[orig] = renamed + } + + // If there are no renamings, then jarjar_prefix does nothing, so skip the extra work. + if result == nil { + return nil + } + + // If they've given us a jarjar_prefix property, then we will use that to rename any classes + // that have not yet been renamed. + prefix := proptools.String(module.properties.Jarjar_prefix) + if prefix != "" { + if prefix[0] == '.' { + ctx.PropertyErrorf("jarjar_prefix", "jarjar_prefix can not start with '.'") + return nil + } + if prefix[len(prefix)-1] == '.' { + ctx.PropertyErrorf("jarjar_prefix", "jarjar_prefix can not end with '.'") + return nil + } + + var updated map[string]string + for orig, renamed := range (*result).Rename { + if renamed == "" { + if updated == nil { + updated = make(map[string]string) + } + updated[orig] = prefix + "." + orig + } + } + for orig, renamed := range updated { + (*result).Rename[orig] = renamed + } + } + + return result +} + +// Get the jarjar rule text for a given provider for the fully resolved rules. Classes that map +// to "" won't be in this list because they shouldn't be renamed yet. +func getJarJarRuleText(provider *JarJarProviderData) string { + result := "" + for _, orig := range android.SortedKeys(provider.Rename) { + renamed := provider.Rename[orig] + if renamed != "" { + result += "rule " + orig + " " + renamed + "\n" + } + } + return result +} + +// Repackage the flags if the jarjar rule txt for the flags is generated +func (j *Module) repackageFlagsIfNecessary(ctx android.ModuleContext, infile android.WritablePath, jarName, info string) android.WritablePath { + if j.repackageJarjarRules == nil { + return infile + } + repackagedJarjarFile := android.PathForModuleOut(ctx, "repackaged-jarjar", info+jarName) + TransformJarJar(ctx, repackagedJarjarFile, infile, j.repackageJarjarRules) + return repackagedJarjarFile +} + func addPlugins(deps *deps, pluginJars android.Paths, pluginClasses ...string) { deps.processorPath = append(deps.processorPath, pluginJars...) deps.processorClasses = append(deps.processorClasses, pluginClasses...) @@ -2175,15 +2739,12 @@ 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) - } - } +type ModuleWithUsesLibrary interface { + UsesLibrary() *usesLibrary } + +func (j *Module) UsesLibrary() *usesLibrary { + return &j.usesLibrary +} + +var _ ModuleWithUsesLibrary = (*Module)(nil) diff --git a/java/boot_jars.go b/java/boot_jars.go index 5d40ec389..6223dede8 100644 --- a/java/boot_jars.go +++ b/java/boot_jars.go @@ -21,8 +21,8 @@ import ( // isActiveModule returns true if the given module should be considered for boot // jars, i.e. if it's enabled and the preferred one in case of source and // prebuilt alternatives. -func isActiveModule(module android.Module) bool { - if !module.Enabled() { +func isActiveModule(ctx android.ConfigAndErrorContext, module android.Module) bool { + if !module.Enabled(ctx) { return false } return android.IsModulePreferred(module) diff --git a/java/bootclasspath.go b/java/bootclasspath.go index f4cef7fa6..77ddf5c05 100644 --- a/java/bootclasspath.go +++ b/java/bootclasspath.go @@ -77,7 +77,7 @@ func addDependencyOntoApexVariants(ctx android.BottomUpMutatorContext, propertyN // Use gatherApexModulePairDepsWithTag to retrieve the dependencies. func addDependencyOntoApexModulePair(ctx android.BottomUpMutatorContext, apex string, name string, tag blueprint.DependencyTag) { var variations []blueprint.Variation - if apex != "platform" && apex != "system_ext" { + if !android.IsConfiguredJarForPlatform(apex) { // Pick the correct apex variant. variations = []blueprint.Variation{ {Mutator: "apex", Variation: apex}, @@ -127,7 +127,10 @@ func reportMissingVariationDependency(ctx android.BottomUpMutatorContext, variat // added by addDependencyOntoApexModulePair. func gatherApexModulePairDepsWithTag(ctx android.BaseModuleContext, tag blueprint.DependencyTag) []android.Module { var modules []android.Module - ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) { + isActiveModulePred := func(module android.Module) bool { + return isActiveModule(ctx, module) + } + ctx.VisitDirectDepsIf(isActiveModulePred, func(module android.Module) { t := ctx.OtherModuleDependencyTag(module) if t == tag { modules = append(modules, module) @@ -185,6 +188,9 @@ var _ android.ExcludeFromVisibilityEnforcementTag = bootclasspathDependencyTag{} // The tag used for dependencies onto bootclasspath_fragments. var bootclasspathFragmentDepTag = bootclasspathDependencyTag{name: "fragment"} +// The tag used for dependencies onto platform_bootclasspath. +var platformBootclasspathDepTag = bootclasspathDependencyTag{name: "platform"} + // BootclasspathNestedAPIProperties defines properties related to the API provided by parts of the // bootclasspath that are nested within the main BootclasspathAPIProperties. type BootclasspathNestedAPIProperties struct { diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index 108fdd483..16209b72e 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -23,6 +23,7 @@ import ( "android/soong/android" "android/soong/dexpreopt" + "android/soong/testing" "github.com/google/blueprint/proptools" @@ -76,7 +77,7 @@ func (b bootclasspathFragmentContentDependencyTag) SdkMemberType(child android.M return javaSdkLibrarySdkMemberType } - return javaBootLibsSdkMemberType + return JavaBootLibsSdkMemberType } func (b bootclasspathFragmentContentDependencyTag) ExportMember() bool { @@ -228,6 +229,7 @@ type SourceOnlyBootclasspathProperties struct { type BootclasspathFragmentModule struct { android.ModuleBase + android.DefaultableModuleBase android.ApexModuleBase ClasspathFragmentBase @@ -238,11 +240,9 @@ type BootclasspathFragmentModule struct { sourceOnlyProperties SourceOnlyBootclasspathProperties - // Collect the module directory for IDE info in java/jdeps.go. - modulePaths []string - // Path to the boot image profile. - profilePath android.Path + profilePath android.WritablePath + profilePathErr error } // commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt @@ -256,16 +256,6 @@ type commonBootclasspathFragment interface { // versioned sdk. produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, fragments []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput - // produceBootImageFiles will attempt to produce rules to create the boot image files at the paths - // predefined in the bootImageConfig. - // - // If it could not create the files then it will return nil. Otherwise, it will return a map from - // android.ArchType to the predefined paths of the boot image files. - produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageOutputs - - // getImageName returns the `image_name` property of this fragment. - getImageName() *string - // getProfilePath returns the path to the boot image profile. getProfilePath() android.Path } @@ -278,6 +268,7 @@ func bootclasspathFragmentFactory() android.Module { android.InitApexModule(m) initClasspathFragment(m, BOOTCLASSPATH) android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) + android.InitDefaultableModule(m) android.AddLoadHook(m, func(ctx android.LoadHookContext) { // If code coverage has been enabled for the framework then append the properties with @@ -295,9 +286,6 @@ func bootclasspathFragmentFactory() android.Module { return } } - - // Initialize the contents property from the image_name. - bootclasspathFragmentInitContentsFromImage(ctx, m) }) return m } @@ -308,9 +296,7 @@ func testBootclasspathFragmentFactory() android.Module { return m } -// bootclasspathFragmentInitContentsFromImage will initialize the contents property from the image_name if -// necessary. -func bootclasspathFragmentInitContentsFromImage(ctx android.EarlyModuleContext, m *BootclasspathFragmentModule) { +func (m *BootclasspathFragmentModule) bootclasspathFragmentPropertyCheck(ctx android.EarlyModuleContext) { contents := m.properties.Contents if len(contents) == 0 { ctx.PropertyErrorf("contents", "required property is missing") @@ -332,6 +318,18 @@ func bootclasspathFragmentInitContentsFromImage(ctx android.EarlyModuleContext, // too early in the Soong processing for that to work. global := dexpreopt.GetGlobalConfig(ctx) modules := global.ArtApexJars + configuredJars := modules.CopyOfJars() + + // Skip the check if the configured jars list is empty as that is a common configuration when + // building targets that do not result in a system image. + if len(configuredJars) == 0 { + return + } + + if !reflect.DeepEqual(configuredJars, contents) { + ctx.ModuleErrorf("inconsistency in specification of contents. ArtApexJars configuration specifies %#v, contents property specifies %#v", + configuredJars, contents) + } // Make sure that the apex specified in the configuration is consistent and is one for which // this boot image is available. @@ -357,42 +355,11 @@ func bootclasspathFragmentInitContentsFromImage(ctx android.EarlyModuleContext, } } -// bootclasspathImageNameContentsConsistencyCheck checks that the configuration that applies to this -// module (if any) matches the contents. -// -// This should be a noop as if image_name="art" then the contents will be set from the ArtApexJars -// config by bootclasspathFragmentInitContentsFromImage so it will be guaranteed to match. However, -// in future this will not be the case. -func (b *BootclasspathFragmentModule) bootclasspathImageNameContentsConsistencyCheck(ctx android.BaseModuleContext) { - imageName := proptools.String(b.properties.Image_name) - if imageName == "art" { - // Get the configuration for the art apex jars. - modules := b.getImageConfig(ctx).modules - configuredJars := modules.CopyOfJars() - - // Skip the check if the configured jars list is empty as that is a common configuration when - // building targets that do not result in a system image. - if len(configuredJars) == 0 { - return - } - - contents := b.properties.Contents - if !reflect.DeepEqual(configuredJars, contents) { - ctx.ModuleErrorf("inconsistency in specification of contents. ArtApexJars configuration specifies %#v, contents property specifies %#v", - configuredJars, contents) - } - } -} - -var BootclasspathFragmentApexContentInfoProvider = blueprint.NewProvider(BootclasspathFragmentApexContentInfo{}) +var BootclasspathFragmentApexContentInfoProvider = blueprint.NewProvider[BootclasspathFragmentApexContentInfo]() // BootclasspathFragmentApexContentInfo contains the bootclasspath_fragments contributions to the // apex contents. type BootclasspathFragmentApexContentInfo struct { - // The configured modules, will be empty if this is from a bootclasspath_fragment that does not - // set image_name: "art". - modules android.ConfiguredJarList - // 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 @@ -405,10 +372,6 @@ type BootclasspathFragmentApexContentInfo struct { profileInstallPathInApex string } -func (i BootclasspathFragmentApexContentInfo) Modules() android.ConfiguredJarList { - return i.modules -} - // 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. @@ -424,6 +387,10 @@ func (i BootclasspathFragmentApexContentInfo) DexBootJarPathForContentModule(mod } } +func (i BootclasspathFragmentApexContentInfo) DexBootJarPathMap() bootDexJarByModule { + return i.contentModuleDexJarPaths +} + func (i BootclasspathFragmentApexContentInfo) ProfilePathOnHost() android.Path { return i.profilePathOnHost } @@ -434,6 +401,11 @@ func (i BootclasspathFragmentApexContentInfo) ProfileInstallPathInApex() string func (b *BootclasspathFragmentModule) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool { tag := ctx.OtherModuleDependencyTag(dep) + + // If the module is a default module, do not check the tag + if _, ok := dep.(*Defaults); ok { + return true + } if IsBootclasspathFragmentContentDepTag(tag) { // Boot image contents are automatically added to apex. return true @@ -479,8 +451,6 @@ func (b *BootclasspathFragmentModule) DepsMutator(ctx android.BottomUpMutatorCon for _, apiScope := range hiddenAPISdkLibrarySupportedScopes { // Add a dependency onto a possibly scope specific stub library. scopeSpecificDependency := apiScope.scopeSpecificStubModule(ctx, additionalStubModule) - // Use JavaApiLibraryName function to be redirected to stubs generated from .txt if applicable - scopeSpecificDependency = android.JavaApiLibraryName(ctx.Config(), scopeSpecificDependency) tag := hiddenAPIStubsDependencyTag{apiScope: apiScope, fromAdditionalDependency: true} ctx.AddVariationDependencies(nil, tag, scopeSpecificDependency) } @@ -504,16 +474,13 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo // Only perform a consistency check if this module is the active module. That will prevent an // unused prebuilt that was created without instrumentation from breaking an instrumentation // build. - if isActiveModule(ctx.Module()) { - b.bootclasspathImageNameContentsConsistencyCheck(ctx) + if isActiveModule(ctx, ctx.Module()) { + b.bootclasspathFragmentPropertyCheck(ctx) } // 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) { @@ -525,34 +492,15 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo fragments := gatherApexModulePairDepsWithTag(ctx, bootclasspathFragmentDepTag) - // Verify that the image_name specified on a bootclasspath_fragment is valid even if this is a - // prebuilt which will not use the image config. - imageConfig := b.getImageConfig(ctx) - // Perform hidden API processing. hiddenAPIOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments) - var bootImageFiles bootImageOutputs - if imageConfig != nil { - // Delegate the production of the boot image files to a module type specific method. - common := ctx.Module().(commonBootclasspathFragment) - bootImageFiles = common.produceBootImageFiles(ctx, imageConfig) - b.profilePath = bootImageFiles.profile - - if shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) { - // Zip the boot image files up, if available. This will generate the zip file in a - // predefined location. - buildBootImageZipInPredefinedLocation(ctx, imageConfig, bootImageFiles.byArch) - - // Copy the dex jars of this fragment's content modules to their predefined locations. - copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule) - } - } - - // A prebuilt fragment cannot contribute to an apex. - if !android.IsModulePrebuilt(ctx.Module()) { - // Provide the apex content info. - b.provideApexContentInfo(ctx, imageConfig, hiddenAPIOutput, bootImageFiles) + if android.IsModulePrebuilt(ctx.Module()) { + b.profilePath = ctx.Module().(*PrebuiltBootclasspathFragmentModule).produceBootImageProfile(ctx) + } else { + b.profilePath = b.produceBootImageProfileFromSource(ctx, contents, hiddenAPIOutput.EncodedBootDexFilesByModule) + // Provide the apex content info. A prebuilt fragment cannot contribute to an apex. + b.provideApexContentInfo(ctx, hiddenAPIOutput, b.profilePath) } // In order for information about bootclasspath_fragment modules to be added to module-info.json @@ -564,51 +512,50 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo if ctx.Module() != ctx.FinalModule() { b.HideFromMake() } + android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{}) } -// shouldCopyBootFilesToPredefinedLocations determines whether the current module should copy boot -// files, e.g. boot dex jars or boot image files, to the predefined location expected by the rest -// of the build. -// -// This ensures that only a single module will copy its files to the image configuration. -func shouldCopyBootFilesToPredefinedLocations(ctx android.ModuleContext, imageConfig *bootImageConfig) bool { +// getProfileProviderApex returns the name of the apex that provides a boot image profile, or an +// empty string if this module should not provide a boot image profile. +func (b *BootclasspathFragmentModule) getProfileProviderApex(ctx android.BaseModuleContext) string { + // Only use the profile from the module that is preferred. + if !isActiveModule(ctx, ctx.Module()) { + return "" + } + // Bootclasspath fragment modules that are for the platform do not produce boot related files. - apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) - if apexInfo.IsForPlatform() { - return false + apexInfos, _ := android.ModuleProvider(ctx, android.AllApexInfoProvider) + if apexInfos == nil { + return "" } - // If the image configuration has no modules specified then it means that the build has been - // configured to build something other than a boot image, e.g. an sdk, so do not try and copy the - // files. - if imageConfig.modules.Len() == 0 { - return false + for _, apexInfo := range apexInfos.ApexInfos { + for _, apex := range apexInfo.InApexVariants { + if isProfileProviderApex(ctx, apex) { + return apex + } + } } - // Only copy files from the module that is preferred. - return isActiveModule(ctx.Module()) + return "" } // provideApexContentInfo creates, initializes and stores the apex content info for use by other // modules. -func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, hiddenAPIOutput *HiddenAPIOutput, bootImageFiles bootImageOutputs) { +func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, hiddenAPIOutput *HiddenAPIOutput, profile android.WritablePath) { // Construct the apex content info from the config. info := BootclasspathFragmentApexContentInfo{ // Populate the apex content info with paths to the dex jars. contentModuleDexJarPaths: hiddenAPIOutput.EncodedBootDexFilesByModule, } - if imageConfig != nil { - info.modules = imageConfig.modules - global := dexpreopt.GetGlobalConfig(ctx) - if !global.DisableGenerateProfile { - info.profilePathOnHost = bootImageFiles.profile - info.profileInstallPathInApex = imageConfig.profileInstallPathInApex - } + if profile != nil { + info.profilePathOnHost = profile + info.profileInstallPathInApex = ProfileInstallPathInApex } // Make the apex content info available for other modules. - ctx.SetProvider(BootclasspathFragmentApexContentInfoProvider, info) + android.SetProvider(ctx, BootclasspathFragmentApexContentInfoProvider, info) } // generateClasspathProtoBuildActions generates all required build actions for classpath.proto config @@ -625,12 +572,12 @@ func (b *BootclasspathFragmentModule) generateClasspathProtoBuildActions(ctx and } func (b *BootclasspathFragmentModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList { + global := dexpreopt.GetGlobalConfig(ctx) + if "art" == proptools.String(b.properties.Image_name) { - return b.getImageConfig(ctx).modules + return global.ArtApexJars } - global := dexpreopt.GetGlobalConfig(ctx) - possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, b.properties.Contents, bootclasspathFragmentContentDepTag) jars, unknown := global.ApexBootJars.Filter(possibleUpdatableModules) @@ -649,30 +596,34 @@ func (b *BootclasspathFragmentModule) configuredJars(ctx android.ModuleContext) // 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) + if isApexVariant(ctx) && len(unknown) > 0 { + if android.IsModulePrebuilt(ctx.Module()) { + // prebuilt bcpf. the validation of this will be done at the top-level apex + providerClasspathFragmentValidationInfoProvider(ctx, unknown) + } else if !disableSourceApexVariant(ctx) { + // source bcpf, and prebuilt apex are not selected. + ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown) + } } } return jars } -func (b *BootclasspathFragmentModule) getImageConfig(ctx android.EarlyModuleContext) *bootImageConfig { - // Get a map of the image configs that are supported. - imageConfigs := genBootImageConfigs(ctx) +var ClasspathFragmentValidationInfoProvider = blueprint.NewProvider[ClasspathFragmentValidationInfo]() - // Retrieve the config for this image. - imageNamePtr := b.properties.Image_name - if imageNamePtr == nil { - return nil - } +type ClasspathFragmentValidationInfo struct { + ClasspathFragmentModuleName string + UnknownJars []string +} - imageName := *imageNamePtr - imageConfig := imageConfigs[imageName] - if imageConfig == nil { - ctx.PropertyErrorf("image_name", "Unknown image name %q, expected one of %s", imageName, strings.Join(android.SortedKeys(imageConfigs), ", ")) - return nil +// Set a provider with the list of jars that have not been added to PRODUCT_APEX_BOOT_JARS +// The validation will be done in the ctx of the top-level _selected_ apex +func providerClasspathFragmentValidationInfoProvider(ctx android.ModuleContext, unknown []string) { + info := ClasspathFragmentValidationInfo{ + ClasspathFragmentModuleName: ctx.ModuleName(), + UnknownJars: unknown, } - return imageConfig + android.SetProvider(ctx, ClasspathFragmentValidationInfoProvider, info) } // generateHiddenAPIBuildActions generates all the hidden API related build rules. @@ -713,26 +664,11 @@ func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android. hiddenAPIInfo.HiddenAPIFlagOutput = output.HiddenAPIFlagOutput // Provide it for use by other modules. - ctx.SetProvider(HiddenAPIInfoProvider, hiddenAPIInfo) + android.SetProvider(ctx, HiddenAPIInfoProvider, hiddenAPIInfo) return output } -// retrieveLegacyEncodedBootDexFiles attempts to retrieve the legacy encoded boot dex jar files. -func retrieveLegacyEncodedBootDexFiles(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule { - // If the current bootclasspath_fragment is the active module or a source module then retrieve the - // encoded dex files, otherwise return an empty map. - // - // An inactive (i.e. not preferred) bootclasspath_fragment needs to retrieve the encoded dex jars - // as they are still needed by an apex. An inactive prebuilt_bootclasspath_fragment does not need - // to do so and may not yet have access to dex boot jars from a prebuilt_apex/apex_set. - if isActiveModule(ctx.Module()) || !android.IsModulePrebuilt(ctx.Module()) { - return extractEncodedDexJarsFromModules(ctx, contents) - } else { - return nil - } -} - // createHiddenAPIFlagInput creates a HiddenAPIFlagInput struct and initializes it with information derived // from the properties on this module and its dependencies. func (b *BootclasspathFragmentModule) createHiddenAPIFlagInput(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) HiddenAPIFlagInput { @@ -849,7 +785,7 @@ func (b *BootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleC } // Make the information available for the sdk snapshot. - ctx.SetProvider(HiddenAPIInfoForSdkProvider, HiddenAPIInfoForSdk{ + android.SetProvider(ctx, HiddenAPIInfoForSdkProvider, HiddenAPIInfoForSdk{ FlagFilesByCategory: flagFilesByCategory, HiddenAPIFlagOutput: flagOutput, }) @@ -857,48 +793,22 @@ func (b *BootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleC return output } -// produceBootImageFiles builds the boot image files from the source if it is required. -func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageOutputs { - // Only generate the boot image if the configuration does not skip it. - return b.generateBootImageBuildActions(ctx, imageConfig) -} - -// generateBootImageBuildActions generates ninja rules to create the boot image if required for this -// module. -// -// If it could not create the files then it will return nil. Otherwise, it will return a map from -// android.ArchType to the predefined paths of the boot image files. -func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageOutputs { - global := dexpreopt.GetGlobalConfig(ctx) - if !shouldBuildBootImages(ctx.Config(), global) { - return bootImageOutputs{} - } - - // Bootclasspath fragment modules that are for the platform do not produce a boot image. - apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) - if apexInfo.IsForPlatform() { - return bootImageOutputs{} +// produceBootImageProfileFromSource builds the boot image profile from the source if it is required. +func (b *BootclasspathFragmentModule) produceBootImageProfileFromSource(ctx android.ModuleContext, contents []android.Module, modules bootDexJarByModule) android.WritablePath { + apex := b.getProfileProviderApex(ctx) + if apex == "" { + return nil } - // Build a profile for the image config and then use that to build the boot image. - profile := bootImageProfileRule(ctx, imageConfig) - - // If dexpreopt of boot image jars should be skipped, generate only a profile. - if SkipDexpreoptBootJars(ctx) { - return bootImageOutputs{ - profile: profile, - } + dexPaths := make(android.Paths, 0, len(contents)) + dexLocations := make([]string, 0, len(contents)) + for _, module := range contents { + dexPaths = append(dexPaths, modules[module.Name()]) + dexLocations = append(dexLocations, filepath.Join("/", "apex", apex, "javalib", module.Name()+".jar")) } - // Build boot image files for the host variants. - buildBootImageVariantsForBuildOs(ctx, imageConfig, profile) - - // Build boot image files for the android variants. - bootImageFiles := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile) - - // Return the boot image files for the android variants for inclusion in an APEX and to be zipped - // up for the dist. - return bootImageFiles + // Build a profile for the modules in this fragment. + return bootImageProfileRuleCommon(ctx, b.Name(), dexPaths, dexLocations) } func (b *BootclasspathFragmentModule) AndroidMkEntries() []android.AndroidMkEntries { @@ -921,10 +831,6 @@ func (b *BootclasspathFragmentModule) AndroidMkEntries() []android.AndroidMkEntr return entriesList } -func (b *BootclasspathFragmentModule) getImageName() *string { - return b.properties.Image_name -} - func (b *BootclasspathFragmentModule) getProfilePath() android.Path { return b.profilePath } @@ -932,7 +838,6 @@ func (b *BootclasspathFragmentModule) getProfilePath() android.Path { // 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 { @@ -1012,7 +917,7 @@ func (b *bootclasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx andro // Get the hidden API information from the module. mctx := ctx.SdkModuleContext() - hiddenAPIInfo := mctx.OtherModuleProvider(module, HiddenAPIInfoForSdkProvider).(HiddenAPIInfoForSdk) + hiddenAPIInfo, _ := android.OtherModuleProvider(mctx, module, HiddenAPIInfoForSdkProvider) b.Flag_files_by_category = hiddenAPIInfo.FlagFilesByCategory // Copy all the generated file paths. @@ -1073,7 +978,7 @@ func (b *bootclasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android. builder.CopyToSnapshot(p, dest) dests = append(dests, dest) } - hiddenAPISet.AddProperty(category.PropertyName, dests) + hiddenAPISet.AddProperty(category.PropertyName(), dests) } } } @@ -1169,10 +1074,6 @@ func (module *PrebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx an return android.PathForModuleSrc(ctx, *src) } - // Retrieve the dex files directly from the content modules. They in turn should retrieve the - // encoded dex jars from the prebuilt .apex files. - encodedBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, contents) - output := HiddenAPIOutput{ HiddenAPIFlagOutput: HiddenAPIFlagOutput{ AnnotationFlagsPath: pathForSrc("hidden_api.annotation_flags", module.prebuiltProperties.Hidden_api.Annotation_flags), @@ -1183,8 +1084,6 @@ func (module *PrebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx an 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. @@ -1194,42 +1093,28 @@ func (module *PrebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx an return &output } -// produceBootImageFiles extracts the boot image files from the APEX if available. -func (module *PrebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageOutputs { - if !shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) { - return bootImageOutputs{} - } - - di := android.FindDeapexerProviderForModule(ctx) - if di == nil { - return bootImageOutputs{} // An error has been reported by FindDeapexerProviderForModule. - } - - profile := (android.WritablePath)(nil) - if imageConfig.profileInstallPathInApex != "" { - profile = di.PrebuiltExportPath(imageConfig.profileInstallPathInApex) +// produceBootImageProfile extracts the boot image profile from the APEX if available. +func (module *PrebuiltBootclasspathFragmentModule) produceBootImageProfile(ctx android.ModuleContext) android.WritablePath { + // This module does not provide a boot image profile. + if module.getProfileProviderApex(ctx) == "" { + return 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) - - if profile == nil && imageConfig.isProfileGuided() { - ctx.ModuleErrorf("Unable to produce boot image files: profiles not found in the prebuilt apex") - return bootImageOutputs{} + di, err := android.FindDeapexerProviderForModule(ctx) + if err != nil { + // An error was found, possibly due to multiple apexes in the tree that export this library + // Defer the error till a client tries to call getProfilePath + module.profilePathErr = err + return nil // An error has been reported by FindDeapexerProviderForModule. } - // Build boot image files for the android variants from the dex files provided by the contents - // of this module. - return buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile) -} -func (b *PrebuiltBootclasspathFragmentModule) getImageName() *string { - return b.properties.Image_name + return di.PrebuiltExportPath(ProfileInstallPathInApex) } func (b *PrebuiltBootclasspathFragmentModule) getProfilePath() android.Path { + if b.profilePathErr != nil { + panic(b.profilePathErr.Error()) + } return b.profilePath } @@ -1241,18 +1126,18 @@ var _ commonBootclasspathFragment = (*PrebuiltBootclasspathFragmentModule)(nil) // 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 { - imageConfig := module.getImageConfig(ctx) - if imageConfig != nil { - files := []string{} - if imageConfig.profileInstallPathInApex != "" { - // Add the boot image profile. - files = append(files, imageConfig.profileInstallPathInApex) + for _, apex := range module.ApexProperties.Apex_available { + if isProfileProviderApex(ctx, apex) { + return []string{ProfileInstallPathInApex} } - return files } return nil } +func (module *PrebuiltBootclasspathFragmentModule) UseProfileGuidedDexpreopt() bool { + return false +} + var _ android.RequiredFilesFromPrebuiltApex = (*PrebuiltBootclasspathFragmentModule)(nil) func prebuiltBootclasspathFragmentFactory() android.Module { @@ -1264,9 +1149,5 @@ func prebuiltBootclasspathFragmentFactory() android.Module { android.InitApexModule(m) android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon) - // Initialize the contents property from the image_name. - android.AddLoadHook(m, func(ctx android.LoadHookContext) { - bootclasspathFragmentInitContentsFromImage(ctx, &m.BootclasspathFragmentModule) - }) return m } diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go index 2541f14ff..8bc0a7ef6 100644 --- a/java/bootclasspath_fragment_test.go +++ b/java/bootclasspath_fragment_test.go @@ -40,6 +40,12 @@ func TestBootclasspathFragment_UnknownImageName(t *testing.T) { image_name: "unknown", contents: ["foo"], } + + java_library { + name: "foo", + srcs: ["foo.java"], + installable: true, + } `) } @@ -53,6 +59,11 @@ func TestPrebuiltBootclasspathFragment_UnknownImageName(t *testing.T) { image_name: "unknown", contents: ["foo"], } + + java_import { + name: "foo", + jars: ["foo.jar"], + } `) } @@ -72,6 +83,18 @@ func TestBootclasspathFragmentInconsistentArtConfiguration_Platform(t *testing.T "apex", ], } + + java_library { + name: "foo", + srcs: ["foo.java"], + installable: true, + } + + java_library { + name: "bar", + srcs: ["bar.java"], + installable: true, + } `) } @@ -92,6 +115,18 @@ func TestBootclasspathFragmentInconsistentArtConfiguration_ApexMixture(t *testin "apex2", ], } + + java_library { + name: "foo", + srcs: ["foo.java"], + installable: true, + } + + java_library { + name: "bar", + srcs: ["bar.java"], + installable: true, + } `) } @@ -187,6 +222,11 @@ func TestBootclasspathFragment_StubLibs(t *testing.T) { PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary", "mycoreplatform"), FixtureConfigureApexBootJars("someapex:mysdklibrary"), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildFlags = map[string]string{ + "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", + } + }), ).RunTestWithBp(t, ` bootclasspath_fragment { name: "myfragment", @@ -237,16 +277,16 @@ func TestBootclasspathFragment_StubLibs(t *testing.T) { `) fragment := result.Module("myfragment", "android_common") - info := result.ModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo) + info, _ := android.SingletonModuleProvider(result, fragment, HiddenAPIInfoProvider) stubsJar := "out/soong/.intermediates/mystublib/android_common/dex/mystublib.jar" // Stubs jars for mysdklibrary - publicStubsJar := "out/soong/.intermediates/mysdklibrary.stubs/android_common/dex/mysdklibrary.stubs.jar" - systemStubsJar := "out/soong/.intermediates/mysdklibrary.stubs.system/android_common/dex/mysdklibrary.stubs.system.jar" + publicStubsJar := "out/soong/.intermediates/mysdklibrary.stubs.exportable/android_common/dex/mysdklibrary.stubs.exportable.jar" + systemStubsJar := "out/soong/.intermediates/mysdklibrary.stubs.exportable.system/android_common/dex/mysdklibrary.stubs.exportable.system.jar" // Stubs jars for myothersdklibrary - otherPublicStubsJar := "out/soong/.intermediates/myothersdklibrary.stubs/android_common/dex/myothersdklibrary.stubs.jar" + otherPublicStubsJar := "out/soong/.intermediates/myothersdklibrary.stubs.exportable/android_common/dex/myothersdklibrary.stubs.exportable.jar" // Check that SdkPublic uses public stubs for all sdk libraries. android.AssertPathsRelativeToTopEquals(t, "public dex stubs jar", []string{otherPublicStubsJar, publicStubsJar, stubsJar}, info.TransitiveStubDexJarsByScope.StubDexJarsForScope(PublicHiddenAPIScope)) @@ -281,6 +321,60 @@ func TestBootclasspathFragment_StubLibs(t *testing.T) { android.AssertPathsRelativeToTopEquals(t, "widest dex stubs jar", expectedWidestPaths, info.TransitiveStubDexJarsByScope.StubDexJarsForWidestAPIScope()) } +func TestFromTextWidestApiScope(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForTestWithBootclasspathFragment, + PrepareForTestWithJavaSdkLibraryFiles, + android.FixtureModifyConfig(func(config android.Config) { + config.SetBuildFromTextStub(true) + }), + FixtureWithLastReleaseApis("mysdklibrary", "android-non-updatable"), + FixtureConfigureApexBootJars("someapex:mysdklibrary"), + ).RunTestWithBp(t, ` + bootclasspath_fragment { + name: "myfragment", + contents: ["mysdklibrary"], + additional_stubs: [ + "android-non-updatable", + ], + hidden_api: { + split_packages: ["*"], + }, + } + java_sdk_library { + name: "mysdklibrary", + srcs: ["a.java"], + shared_library: false, + public: {enabled: true}, + system: {enabled: true}, + } + java_sdk_library { + name: "android-non-updatable", + srcs: ["b.java"], + compile_dex: true, + public: { + enabled: true, + }, + system: { + enabled: true, + }, + test: { + enabled: true, + }, + module_lib: { + enabled: true, + }, + } + `) + + fragment := result.ModuleForTests("myfragment", "android_common") + dependencyStubDexFlag := "--dependency-stub-dex=out/soong/.intermediates/default/java/android-non-updatable.stubs.test_module_lib/android_common/dex/android-non-updatable.stubs.test_module_lib.jar" + stubFlagsCommand := fragment.Output("modular-hiddenapi/stub-flags.csv").RuleParams.Command + android.AssertStringDoesContain(t, + "Stub flags generating command does not include the expected dependency stub dex file", + stubFlagsCommand, dependencyStubDexFlag) +} + func TestSnapshotWithBootclasspathFragment_HiddenAPI(t *testing.T) { result := android.GroupFixturePreparers( prepareForTestWithBootclasspathFragment, @@ -367,16 +461,16 @@ func TestSnapshotWithBootclasspathFragment_HiddenAPI(t *testing.T) { // Make sure that the library exports hidden API properties for use by the bootclasspath_fragment. library := result.Module("mynewlibrary", "android_common") - info := result.ModuleProvider(library, hiddenAPIPropertyInfoProvider).(HiddenAPIPropertyInfo) + info, _ := android.SingletonModuleProvider(result, library, hiddenAPIPropertyInfoProvider) android.AssertArrayString(t, "split packages", []string{"sdklibrary", "newlibrary"}, info.SplitPackages) android.AssertArrayString(t, "package prefixes", []string{"newlibrary.all.mine"}, info.PackagePrefixes) android.AssertArrayString(t, "single packages", []string{"newlibrary.mine"}, info.SinglePackages) for _, c := range HiddenAPIFlagFileCategories { expectedMaxTargetQPaths := []string(nil) - if c.PropertyName == "max_target_q" { + if c.PropertyName() == "max_target_q" { expectedMaxTargetQPaths = []string{"my-new-max-target-q.txt"} } - android.AssertPathsRelativeToTopEquals(t, c.PropertyName, expectedMaxTargetQPaths, info.FlagFilesByCategory[c]) + android.AssertPathsRelativeToTopEquals(t, c.PropertyName(), expectedMaxTargetQPaths, info.FlagFilesByCategory[c]) } // Make sure that the signature-patterns.csv is passed all the appropriate package properties diff --git a/java/builder.go b/java/builder.go index 462626712..5d84d0b43 100644 --- a/java/builder.go +++ b/java/builder.go @@ -42,7 +42,8 @@ var ( // TODO(b/143658984): goma can't handle the --system argument to javac. javac, javacRE = pctx.MultiCommandRemoteStaticRules("javac", blueprint.RuleParams{ - Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" "$out" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` + + Command: `rm -rf "$outDir" "$annoDir" "$annoSrcJar.tmp" "$srcJarDir" "$out.tmp" && ` + + `mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` + `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` + `(if [ -s $srcJarDir/list ] || [ -s $out.rsp ] ; then ` + `${config.SoongJavacWrapper} $javaTemplate${config.JavacCmd} ` + @@ -50,14 +51,18 @@ var ( `$processorpath $processor $javacFlags $bootClasspath $classpath ` + `-source $javaVersion -target $javaVersion ` + `-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list ; fi ) && ` + - `$zipTemplate${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir && ` + - `rm -rf "$srcJarDir"`, + `$annoSrcJarTemplate${config.SoongZipCmd} -jar -o $annoSrcJar.tmp -C $annoDir -D $annoDir && ` + + `$zipTemplate${config.SoongZipCmd} -jar -o $out.tmp -C $outDir -D $outDir && ` + + `if ! cmp -s "$out.tmp" "$out"; then mv "$out.tmp" "$out"; fi && ` + + `if ! cmp -s "$annoSrcJar.tmp" "$annoSrcJar"; then mv "$annoSrcJar.tmp" "$annoSrcJar"; fi && ` + + `rm -rf "$srcJarDir" "$outDir"`, CommandDeps: []string{ "${config.JavacCmd}", "${config.SoongZipCmd}", "${config.ZipSyncCmd}", }, CommandOrderOnly: []string{"${config.SoongJavacWrapper}"}, + Restat: true, Rspfile: "$out.rsp", RspfileContent: "$in", }, map[string]*remoteexec.REParams{ @@ -69,12 +74,19 @@ var ( "$zipTemplate": &remoteexec.REParams{ Labels: map[string]string{"type": "tool", "name": "soong_zip"}, Inputs: []string{"${config.SoongZipCmd}", "$outDir"}, - OutputFiles: []string{"$out"}, + OutputFiles: []string{"$out.tmp"}, + ExecStrategy: "${config.REJavacExecStrategy}", + Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"}, + }, + "$annoSrcJarTemplate": &remoteexec.REParams{ + Labels: map[string]string{"type": "tool", "name": "soong_zip"}, + Inputs: []string{"${config.SoongZipCmd}", "$annoDir"}, + OutputFiles: []string{"$annoSrcJar.tmp"}, ExecStrategy: "${config.REJavacExecStrategy}", Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"}, }, }, []string{"javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir", - "outDir", "annoDir", "javaVersion"}, nil) + "outDir", "annoDir", "annoSrcJar", "javaVersion"}, nil) _ = pctx.VariableFunc("kytheCorpus", func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() }) @@ -108,6 +120,8 @@ var ( `--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED ` + `--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED ` + `--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED ` + + `--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED ` + + `--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED ` + `-jar ${config.JavaKytheExtractorJar} ` + `${config.JavacHeapFlags} ${config.CommonJdkFlags} ` + `$processorpath $processor $javacFlags $bootClasspath $classpath ` + @@ -200,6 +214,14 @@ var ( CommandDeps: []string{"${config.MergeZipsCmd}"}, }, "jarArgs") + combineJarRsp = pctx.AndroidStaticRule("combineJarRsp", + blueprint.RuleParams{ + Command: `${config.MergeZipsCmd} --ignore-duplicates -j $jarArgs $out @$out.rsp`, + CommandDeps: []string{"${config.MergeZipsCmd}"}, + Rspfile: "$out.rsp", + RspfileContent: "$in", + }, + "jarArgs") jarjar = pctx.AndroidStaticRule("jarjar", blueprint.RuleParams{ @@ -246,11 +268,46 @@ var ( CommandDeps: []string{"${config.ZipAlign}"}, }, ) + + convertImplementationJarToHeaderJarRule = pctx.AndroidStaticRule("convertImplementationJarToHeaderJar", + blueprint.RuleParams{ + Command: `${config.Zip2ZipCmd} -i ${in} -o ${out} -x 'META-INF/services/**/*'`, + CommandDeps: []string{"${config.Zip2ZipCmd}"}, + }) + + writeCombinedProguardFlagsFileRule = pctx.AndroidStaticRule("writeCombinedProguardFlagsFileRule", + blueprint.RuleParams{ + Command: `rm -f $out && ` + + `for f in $in; do ` + + ` echo && ` + + ` echo "# including $$f" && ` + + ` cat $$f; ` + + `done > $out`, + }) + + gatherReleasedFlaggedApisRule = pctx.AndroidStaticRule("gatherReleasedFlaggedApisRule", + blueprint.RuleParams{ + Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}={state:bool}' ` + + `--out ${out} ` + + `${flags_path} ` + + `${filter_args} `, + CommandDeps: []string{"${aconfig}"}, + Description: "aconfig_bool", + }, "flags_path", "filter_args") + + generateMetalavaRevertAnnotationsRule = pctx.AndroidStaticRule("generateMetalavaRevertAnnotationsRule", + blueprint.RuleParams{ + Command: `${keep-flagged-apis} ${in} > ${out}`, + CommandDeps: []string{"${keep-flagged-apis}"}, + }) ) func init() { pctx.Import("android/soong/android") pctx.Import("android/soong/java/config") + + pctx.HostBinToolVariable("aconfig", "aconfig") + pctx.HostBinToolVariable("keep-flagged-apis", "keep-flagged-apis") } type javaBuilderFlags struct { @@ -292,8 +349,14 @@ type javaBuilderFlags struct { proto android.ProtoFlags } +func DefaultJavaBuilderFlags() javaBuilderFlags { + return javaBuilderFlags{ + javaVersion: JAVA_VERSION_8, + } +} + func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath, shardIdx int, - srcFiles, srcJars android.Paths, flags javaBuilderFlags, deps android.Paths) { + srcFiles, srcJars android.Paths, annoSrcJar android.WritablePath, flags javaBuilderFlags, deps android.Paths) { // Compile java sources into .class files desc := "javac" @@ -301,7 +364,7 @@ func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.Writab desc += strconv.Itoa(shardIdx) } - transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, flags, deps, "javac", desc) + transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, annoSrcJar, flags, deps, "javac", desc) } // Emits the rule to generate Xref input file (.kzip file) for the given set of source files and source jars @@ -365,7 +428,7 @@ func emitXrefRule(ctx android.ModuleContext, xrefFile android.WritablePath, idx }) } -func turbineFlags(ctx android.ModuleContext, flags javaBuilderFlags) (string, android.Paths) { +func turbineFlags(ctx android.ModuleContext, flags javaBuilderFlags, dir string) (string, android.Paths) { var deps android.Paths classpath := flags.classpath @@ -390,13 +453,21 @@ func turbineFlags(ctx android.ModuleContext, flags javaBuilderFlags) (string, an deps = append(deps, classpath...) turbineFlags := bootClasspath + " " + classpath.FormTurbineClassPath("--classpath ") + const flagsLimit = 32 * 1024 + if len(turbineFlags) > flagsLimit { + flagsRspFile := android.PathForModuleOut(ctx, dir, "turbine-flags.rsp") + android.WriteFileRule(ctx, flagsRspFile, turbineFlags) + turbineFlags = "@" + flagsRspFile.String() + deps = append(deps, flagsRspFile) + } + return turbineFlags, deps } func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath, srcFiles, srcJars android.Paths, flags javaBuilderFlags) { - turbineFlags, deps := turbineFlags(ctx, flags) + turbineFlags, deps := turbineFlags(ctx, flags, "turbine") deps = append(deps, srcJars...) @@ -428,7 +499,7 @@ func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android. func TurbineApt(ctx android.ModuleContext, outputSrcJar, outputResJar android.WritablePath, srcFiles, srcJars android.Paths, flags javaBuilderFlags) { - turbineFlags, deps := turbineFlags(ctx, flags) + turbineFlags, deps := turbineFlags(ctx, flags, "kapt") deps = append(deps, srcJars...) @@ -475,20 +546,20 @@ func TurbineApt(ctx android.ModuleContext, outputSrcJar, outputResJar android.Wr // suffix will be appended to various intermediate files and directories to avoid collisions when // this function is called twice in the same module directory. func transformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath, - shardIdx int, srcFiles, srcJars android.Paths, + shardIdx int, srcFiles, srcJars android.Paths, annoSrcJar android.WritablePath, flags javaBuilderFlags, deps android.Paths, intermediatesDir, desc string) { deps = append(deps, srcJars...) - classpath := flags.classpath + javacClasspath := flags.classpath var bootClasspath string if flags.javaVersion.usesJavaModules() { var systemModuleDeps android.Paths bootClasspath, systemModuleDeps = flags.systemModules.FormJavaSystemModulesPath(ctx.Device()) deps = append(deps, systemModuleDeps...) - classpath = append(flags.java9Classpath, classpath...) + javacClasspath = append(flags.java9Classpath, javacClasspath...) } else { deps = append(deps, flags.bootClasspath...) if len(flags.bootClasspath) == 0 && ctx.Device() { @@ -500,7 +571,19 @@ func transformJavaToClasses(ctx android.ModuleContext, outputFile android.Writab } } - deps = append(deps, classpath...) + classpathArg := javacClasspath.FormJavaClassPath("-classpath") + + // Keep the command line under the MAX_ARG_STRLEN limit by putting the classpath argument into an rsp file + // if it is too long. + const classpathLimit = 64 * 1024 + if len(classpathArg) > classpathLimit { + classpathRspFile := outputFile.ReplaceExtension(ctx, "classpath") + android.WriteFileRule(ctx, classpathRspFile, classpathArg) + deps = append(deps, classpathRspFile) + classpathArg = "@" + classpathRspFile.String() + } + + deps = append(deps, javacClasspath...) deps = append(deps, flags.processorPath...) processor := "-proc:none" @@ -522,21 +605,23 @@ func transformJavaToClasses(ctx android.ModuleContext, outputFile android.Writab rule = javacRE } ctx.Build(pctx, android.BuildParams{ - Rule: rule, - Description: desc, - Output: outputFile, - Inputs: srcFiles, - Implicits: deps, + Rule: rule, + Description: desc, + Output: outputFile, + ImplicitOutput: annoSrcJar, + Inputs: srcFiles, + Implicits: deps, Args: map[string]string{ "javacFlags": flags.javacFlags, "bootClasspath": bootClasspath, - "classpath": classpath.FormJavaClassPath("-classpath"), + "classpath": classpathArg, "processorpath": flags.processorPath.FormJavaClassPath("-processorpath"), "processor": processor, "srcJars": strings.Join(srcJars.Strings(), " "), "srcJarDir": android.PathForModuleOut(ctx, intermediatesDir, srcJarDir).String(), "outDir": android.PathForModuleOut(ctx, intermediatesDir, outDir).String(), "annoDir": android.PathForModuleOut(ctx, intermediatesDir, annoDir).String(), + "annoSrcJar": annoSrcJar.String(), "javaVersion": flags.javaVersion.String(), }, }) @@ -588,8 +673,23 @@ func TransformJarsToJar(ctx android.ModuleContext, outputFile android.WritablePa jarArgs = append(jarArgs, "-D") } + rule := combineJar + // Keep the command line under the MAX_ARG_STRLEN limit by putting the list of jars into an rsp file + // if it is too long. + const jarsLengthLimit = 64 * 1024 + jarsLength := 0 + for i, jar := range jars { + if i != 0 { + jarsLength += 1 + } + jarsLength += len(jar.String()) + } + if jarsLength > jarsLengthLimit { + rule = combineJarRsp + } + ctx.Build(pctx, android.BuildParams{ - Rule: combineJar, + Rule: rule, Description: desc, Output: outputFile, Inputs: jars, @@ -600,6 +700,15 @@ func TransformJarsToJar(ctx android.ModuleContext, outputFile android.WritablePa }) } +func convertImplementationJarToHeaderJar(ctx android.ModuleContext, implementationJarFile android.Path, + headerJarFile android.WritablePath) { + ctx.Build(pctx, android.BuildParams{ + Rule: convertImplementationJarToHeaderJarRule, + Input: implementationJarFile, + Output: headerJarFile, + }) +} + func TransformJarJar(ctx android.ModuleContext, outputFile android.WritablePath, classesJar android.Path, rulesFile android.Path) { ctx.Build(pctx, android.BuildParams{ @@ -641,12 +750,22 @@ func GenerateMainClassManifest(ctx android.ModuleContext, outputFile android.Wri android.WriteFileRule(ctx, outputFile, "Main-Class: "+mainClass+"\n") } -func TransformZipAlign(ctx android.ModuleContext, outputFile android.WritablePath, inputFile android.Path) { +func TransformZipAlign(ctx android.ModuleContext, outputFile android.WritablePath, inputFile android.Path, validations android.Paths) { ctx.Build(pctx, android.BuildParams{ Rule: zipalign, Description: "align", Input: inputFile, Output: outputFile, + Validations: validations, + }) +} + +func writeCombinedProguardFlagsFile(ctx android.ModuleContext, outputFile android.WritablePath, files android.Paths) { + ctx.Build(pctx, android.BuildParams{ + Rule: writeCombinedProguardFlagsFileRule, + Description: "write combined proguard flags file", + Inputs: files, + Output: outputFile, }) } diff --git a/java/classpath_element.go b/java/classpath_element.go index 496291678..abbcae7a3 100644 --- a/java/classpath_element.go +++ b/java/classpath_element.go @@ -21,7 +21,6 @@ import ( "strings" "android/soong/android" - "github.com/google/blueprint" ) // Supports constructing a list of ClasspathElement from a set of fragments and modules. @@ -72,8 +71,7 @@ var _ ClasspathElement = (*ClasspathLibraryElement)(nil) // ClasspathElementContext defines the context methods needed by CreateClasspathElements type ClasspathElementContext interface { - OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool - OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{} + android.OtherModuleProviderContext ModuleErrorf(fmt string, args ...interface{}) } @@ -123,12 +121,12 @@ func CreateClasspathElements(ctx ClasspathElementContext, libraries []android.Mo // associated with a particular apex. apexToFragment := map[string]android.Module{} for _, fragment := range fragments { - if !ctx.OtherModuleHasProvider(fragment, android.ApexInfoProvider) { + apexInfo, ok := android.OtherModuleProvider(ctx, fragment, android.ApexInfoProvider) + if !ok { ctx.ModuleErrorf("fragment %s is not part of an apex", fragment) continue } - apexInfo := ctx.OtherModuleProvider(fragment, android.ApexInfoProvider).(android.ApexInfo) for _, apex := range apexInfo.InApexVariants { if existing, ok := apexToFragment[apex]; ok { ctx.ModuleErrorf("apex %s has multiple fragments, %s and %s", apex, fragment, existing) @@ -146,8 +144,7 @@ skipLibrary: // Iterate over the libraries to construct the ClasspathElements list. for _, library := range libraries { var element ClasspathElement - if ctx.OtherModuleHasProvider(library, android.ApexInfoProvider) { - apexInfo := ctx.OtherModuleProvider(library, android.ApexInfoProvider).(android.ApexInfo) + if apexInfo, ok := android.OtherModuleProvider(ctx, library, android.ApexInfoProvider); ok { var fragment android.Module diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go index bc9de5043..18a5dae6c 100644 --- a/java/classpath_fragment.go +++ b/java/classpath_fragment.go @@ -103,8 +103,8 @@ type classpathJar struct { func gatherPossibleApexModuleNamesAndStems(ctx android.ModuleContext, contents []string, tag blueprint.DependencyTag) []string { set := map[string]struct{}{} for _, name := range contents { - dep := ctx.GetDirectDepWithTag(name, tag) - set[name] = struct{}{} + dep, _ := ctx.GetDirectDepWithTag(name, tag).(android.Module) + set[ModuleStemForDeapexing(dep)] = struct{}{} if m, ok := dep.(ModuleWithStem); ok { set[m.Stem()] = struct{}{} } else { @@ -128,19 +128,21 @@ func configuredJarListToClasspathJars(ctx android.ModuleContext, configuredJars return m.Name() == configuredJars.Jar(i) }, func(m android.Module) { if s, ok := m.(*SdkLibrary); ok { + minSdkVersion := s.MinSdkVersion(ctx) + maxSdkVersion := s.MaxSdkVersion(ctx) // TODO(208456999): instead of mapping "current" to latest, min_sdk_version should never be set to "current" - if s.minSdkVersion.Specified() { - if s.minSdkVersion.IsCurrent() { + if minSdkVersion.Specified() { + if minSdkVersion.IsCurrent() { jar.minSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String() } else { - jar.minSdkVersion = s.minSdkVersion.String() + jar.minSdkVersion = minSdkVersion.String() } } - if s.maxSdkVersion.Specified() { - if s.maxSdkVersion.IsCurrent() { + if maxSdkVersion.Specified() { + if maxSdkVersion.IsCurrent() { jar.maxSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String() } else { - jar.maxSdkVersion = s.maxSdkVersion.String() + jar.maxSdkVersion = maxSdkVersion.String() } } } @@ -151,10 +153,14 @@ func configuredJarListToClasspathJars(ctx android.ModuleContext, configuredJars return jars } +func (c *ClasspathFragmentBase) outputFilename() string { + return strings.ToLower(c.classpathType.String()) + ".pb" +} + 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" + outputFilename := c.outputFilename() c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths") @@ -178,7 +184,11 @@ func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.M ClasspathFragmentProtoInstallDir: c.installDirPath, ClasspathFragmentProtoOutput: c.outputFilepath, } - ctx.SetProvider(ClasspathFragmentProtoContentInfoProvider, classpathProtoInfo) + android.SetProvider(ctx, ClasspathFragmentProtoContentInfoProvider, classpathProtoInfo) +} + +func (c *ClasspathFragmentBase) installClasspathProto(ctx android.ModuleContext) android.InstallPath { + return ctx.InstallFile(c.installDirPath, c.outputFilename(), c.outputFilepath) } func writeClasspathsTextproto(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) { @@ -211,7 +221,7 @@ func (c *ClasspathFragmentBase) androidMkEntries() []android.AndroidMkEntries { }} } -var ClasspathFragmentProtoContentInfoProvider = blueprint.NewProvider(ClasspathFragmentProtoContentInfo{}) +var ClasspathFragmentProtoContentInfoProvider = blueprint.NewProvider[ClasspathFragmentProtoContentInfo]() type ClasspathFragmentProtoContentInfo struct { // Whether the classpaths.proto config is generated for the fragment. diff --git a/java/code_metadata_test.go b/java/code_metadata_test.go new file mode 100644 index 000000000..99b1f5226 --- /dev/null +++ b/java/code_metadata_test.go @@ -0,0 +1,115 @@ +package java + +import ( + "strings" + "testing" + + "android/soong/android" + soongTesting "android/soong/testing" + "android/soong/testing/code_metadata_internal_proto" + + "google.golang.org/protobuf/proto" +) + +func TestCodeMetadata(t *testing.T) { + bp := `code_metadata { + name: "module-name", + teamId: "12345", + code: [ + "foo", + ] + } + + java_sdk_library { + name: "foo", + srcs: ["a.java"], + }` + result := runCodeMetadataTest(t, android.FixtureExpectsNoErrors, bp) + + module := result.ModuleForTests("module-name", "") + + // Check that the provider has the right contents + data, _ := android.SingletonModuleProvider(result, module.Module(), soongTesting.CodeMetadataProviderKey) + if !strings.HasSuffix( + data.IntermediatePath.String(), "/intermediateCodeMetadata.pb", + ) { + t.Errorf( + "Missing intermediates path in provider: %s", + data.IntermediatePath.String(), + ) + } + + metadata := android.ContentFromFileRuleForTests(t, result.TestContext, + module.Output(data.IntermediatePath.String())) + + metadataList := make([]*code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership, 0, 2) + teamId := "12345" + bpFilePath := "Android.bp" + targetName := "foo" + srcFile := []string{"a.java"} + expectedMetadataProto := code_metadata_internal_proto.CodeMetadataInternal_TargetOwnership{ + TrendyTeamId: &teamId, + TargetName: &targetName, + Path: &bpFilePath, + SourceFiles: srcFile, + } + metadataList = append(metadataList, &expectedMetadataProto) + + CodeMetadataMetadata := code_metadata_internal_proto.CodeMetadataInternal{TargetOwnershipList: metadataList} + protoData, _ := proto.Marshal(&CodeMetadataMetadata) + expectedMetadata := string(protoData) + + if metadata != expectedMetadata { + t.Errorf( + "Retrieved metadata: %s is not equal to expectedMetadata: %s", metadata, + expectedMetadata, + ) + } + + // Tests for all_test_spec singleton. + singleton := result.SingletonForTests("all_code_metadata") + rule := singleton.Rule("all_code_metadata_rule") + prebuiltOs := result.Config.PrebuiltOS() + expectedCmd := "out/soong/host/" + prebuiltOs + "/bin/metadata -rule code_metadata -inputFile out/soong/all_code_metadata_paths.rsp -outputFile out/soong/ownership/all_code_metadata.pb" + expectedOutputFile := "out/soong/ownership/all_code_metadata.pb" + expectedInputFile := "out/soong/.intermediates/module-name/intermediateCodeMetadata.pb" + if !strings.Contains( + strings.TrimSpace(rule.Output.String()), + expectedOutputFile, + ) { + t.Errorf( + "Retrieved singletonOutputFile: %s is not equal to expectedSingletonOutputFile: %s", + rule.Output.String(), expectedOutputFile, + ) + } + + if !strings.Contains( + strings.TrimSpace(rule.Inputs[0].String()), + expectedInputFile, + ) { + t.Errorf( + "Retrieved singletonInputFile: %s is not equal to expectedSingletonInputFile: %s", + rule.Inputs[0].String(), expectedInputFile, + ) + } + + if !strings.Contains( + strings.TrimSpace(rule.RuleParams.Command), + expectedCmd, + ) { + t.Errorf( + "Retrieved cmd: %s doesn't contain expectedCmd: %s", + rule.RuleParams.Command, expectedCmd, + ) + } +} +func runCodeMetadataTest( + t *testing.T, errorHandler android.FixtureErrorHandler, bp string, +) *android.TestResult { + return android.GroupFixturePreparers( + soongTesting.PrepareForTestWithTestingBuildComponents, prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("foo"), + ). + ExtendWithErrorHandler(errorHandler). + RunTestWithBp(t, bp) +} diff --git a/java/config/Android.bp b/java/config/Android.bp index 194e2c6ed..bfe83ab8c 100644 --- a/java/config/Android.bp +++ b/java/config/Android.bp @@ -12,6 +12,7 @@ bootstrap_go_package { ], srcs: [ "config.go", + "droidstubs.go", "error_prone.go", "kotlin.go", "makevars.go", diff --git a/java/config/config.go b/java/config/config.go index b82a137e7..0d30fbdf4 100644 --- a/java/config/config.go +++ b/java/config/config.go @@ -26,8 +26,7 @@ import ( ) var ( - pctx = android.NewPackageContext("android/soong/java/config") - exportedVars = android.NewExportedVariables(pctx) + pctx = android.NewPackageContext("android/soong/java/config") LegacyCorePlatformBootclasspathLibraries = []string{"legacy.core.platform.api.stubs", "core-lambda-stubs"} LegacyCorePlatformSystemModules = "legacy-core-platform-api-stubs-system-modules" @@ -52,6 +51,7 @@ var ( "core-icu4j", "core-oj", "core-libart", + "wear-sdk.impl", } ) @@ -79,32 +79,30 @@ var ( func init() { pctx.Import("github.com/google/blueprint/bootstrap") - exportedVars.ExportStringStaticVariable("JavacHeapSize", "4096M") - exportedVars.ExportStringStaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}") + pctx.StaticVariable("JavacHeapSize", "4096M") + pctx.StaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}") // ErrorProne can use significantly more memory than javac alone, give it a higher heap // size (b/221480398). - exportedVars.ExportStringStaticVariable("ErrorProneHeapSize", "8192M") - exportedVars.ExportStringStaticVariable("ErrorProneHeapFlags", "-J-Xmx${ErrorProneHeapSize}") + pctx.StaticVariable("ErrorProneHeapSize", "8192M") + pctx.StaticVariable("ErrorProneHeapFlags", "-J-Xmx${ErrorProneHeapSize}") // D8 invocations are shorter lived, so we restrict their JIT tiering relative to R8. // Note that the `-JXX` prefix syntax is specific to the R8/D8 invocation wrappers. - exportedVars.ExportStringListStaticVariable("D8Flags", append([]string{ + pctx.StaticVariable("D8Flags", strings.Join(append([]string{ "-JXmx4096M", "-JXX:+TieredCompilation", "-JXX:TieredStopAtLevel=1", "-JDcom.android.tools.r8.emitRecordAnnotationsInDex", "-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex", - "-JDcom.android.tools.r8.emitRecordAnnotationsExInDex", - }, dexerJavaVmFlagsList...)) - exportedVars.ExportStringListStaticVariable("R8Flags", append([]string{ - "-JXmx2048M", + }, dexerJavaVmFlagsList...), " ")) + pctx.StaticVariable("R8Flags", strings.Join(append([]string{ + "-JXmx4096M", "-JDcom.android.tools.r8.emitRecordAnnotationsInDex", "-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex", - "-JDcom.android.tools.r8.emitRecordAnnotationsExInDex", - }, dexerJavaVmFlagsList...)) + }, dexerJavaVmFlagsList...), " ")) - exportedVars.ExportStringListStaticVariable("CommonJdkFlags", []string{ + pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{ `-Xmaxerrs 9999999`, `-encoding UTF-8`, `-sourcepath ""`, @@ -118,10 +116,10 @@ func init() { // b/65004097: prevent using java.lang.invoke.StringConcatFactory when using -target 1.9 `-XDstringConcat=inline`, - }) + }, " ")) - exportedVars.ExportStringListStaticVariable("JavaVmFlags", javaVmFlagsList) - exportedVars.ExportStringListStaticVariable("JavacVmFlags", javacVmFlagsList) + pctx.StaticVariable("JavaVmFlags", strings.Join(javaVmFlagsList, " ")) + pctx.StaticVariable("JavacVmFlags", strings.Join(javacVmFlagsList, " ")) pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS) @@ -133,7 +131,7 @@ func init() { if override := ctx.Config().Getenv("OVERRIDE_JLINK_VERSION_NUMBER"); override != "" { return override } - return "17" + return "21" }) pctx.SourcePathVariable("JavaToolchain", "${JavaHome}/bin") @@ -148,6 +146,8 @@ func init() { pctx.SourcePathVariable("JavaKytheExtractorJar", "prebuilts/build-tools/common/framework/javac_extractor.jar") pctx.SourcePathVariable("Ziptime", "prebuilts/build-tools/${hostPrebuiltTag}/bin/ziptime") + pctx.SourcePathVariable("ResourceProcessorBusyBox", "prebuilts/bazel/common/android_tools/android_tools/all_android_tools_deploy.jar") + pctx.HostBinToolVariable("GenKotlinBuildFileCmd", "gen-kotlin-build-file") pctx.SourcePathVariable("JarArgsCmd", "build/soong/scripts/jar-args.sh") @@ -214,10 +214,6 @@ 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/droidstubs.go b/java/config/droidstubs.go new file mode 100644 index 000000000..04a3f96b9 --- /dev/null +++ b/java/config/droidstubs.go @@ -0,0 +1,64 @@ +// Copyright 2023 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 config + +import "strings" + +var ( + metalavaFlags = []string{ + "--color", + "--quiet", + "--format=v2", + "--repeat-errors-max 10", + "--hide UnresolvedImport", + + // Force metalava to ignore classes on the classpath when an API file contains missing classes. + // See b/285140653 for more information. + "--api-class-resolution api", + + // Force metalava to sort overloaded methods by their order in the source code. + // See b/285312164 for more information. + // And add concrete overrides of abstract methods, see b/299366704 for more + // information. + "--format-defaults overloaded-method-order=source,add-additional-overrides=yes", + } + + MetalavaFlags = strings.Join(metalavaFlags, " ") + + metalavaAnnotationsFlags = []string{ + "--include-annotations", + "--exclude-annotation androidx.annotation.RequiresApi", + } + + MetalavaAnnotationsFlags = strings.Join(metalavaAnnotationsFlags, " ") + + metalavaAnnotationsWarningsFlags = []string{ + // TODO(tnorbye): find owners to fix these warnings when annotation was enabled. + "--hide HiddenTypedefConstant", + "--hide SuperfluousPrefix", + } + + MetalavaAnnotationsWarningsFlags = strings.Join(metalavaAnnotationsWarningsFlags, " ") +) + +const ( + MetalavaAddOpens = "-J--add-opens=java.base/java.util=ALL-UNNAMED" +) + +func init() { + pctx.StaticVariable("MetalavaAnnotationsFlags", strings.Join(metalavaAnnotationsFlags, " ")) + + pctx.StaticVariable("MetalavaAnnotationWarningsFlags", strings.Join(metalavaAnnotationsWarningsFlags, " ")) +} diff --git a/java/config/error_prone.go b/java/config/error_prone.go index 5f853c812..767164f70 100644 --- a/java/config/error_prone.go +++ b/java/config/error_prone.go @@ -15,6 +15,7 @@ package config import ( + "android/soong/android" "strings" ) @@ -29,23 +30,23 @@ var ( ) // Wrapper that grabs value of val late so it can be initialized by a later module's init function -func errorProneVar(val *[]string, sep string) func() string { - return func() string { +func errorProneVar(val *[]string, sep string) func(android.PackageVarContext) string { + return func(android.PackageVarContext) string { return strings.Join(*val, sep) } } func init() { - 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{ + pctx.VariableFunc("ErrorProneClasspath", errorProneVar(&ErrorProneClasspath, ":")) + pctx.VariableFunc("ErrorProneChecksError", errorProneVar(&ErrorProneChecksError, " ")) + pctx.VariableFunc("ErrorProneChecksWarning", errorProneVar(&ErrorProneChecksWarning, " ")) + pctx.VariableFunc("ErrorProneChecksDefaultDisabled", errorProneVar(&ErrorProneChecksDefaultDisabled, " ")) + pctx.VariableFunc("ErrorProneChecksOff", errorProneVar(&ErrorProneChecksOff, " ")) + pctx.VariableFunc("ErrorProneFlags", errorProneVar(&ErrorProneFlags, " ")) + pctx.StaticVariable("ErrorProneChecks", strings.Join([]string{ "${ErrorProneChecksOff}", "${ErrorProneChecksError}", "${ErrorProneChecksWarning}", "${ErrorProneChecksDefaultDisabled}", - }) + }, " ")) } diff --git a/java/config/kotlin.go b/java/config/kotlin.go index fc63f4dfb..e5e187cad 100644 --- a/java/config/kotlin.go +++ b/java/config/kotlin.go @@ -49,8 +49,5 @@ func init() { "-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", - }, " ")) + pctx.StaticVariable("KotlincGlobalFlags", strings.Join([]string{}, " ")) } diff --git a/java/config/makevars.go b/java/config/makevars.go index 273aca0b4..649b6c58f 100644 --- a/java/config/makevars.go +++ b/java/config/makevars.go @@ -28,13 +28,12 @@ func makeVarsProvider(ctx android.MakeVarsContext) { ctx.Strict("FRAMEWORK_LIBRARIES", strings.Join(FrameworkLibraries, " ")) // These are used by make when LOCAL_PRIVATE_PLATFORM_APIS is set (equivalent to platform_apis in blueprint): - ctx.Strict("LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES", strings.Join(LegacyCorePlatformBootclasspathLibraries, " ")) + ctx.Strict("LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES", + strings.Join(LegacyCorePlatformBootclasspathLibraries, " ")) ctx.Strict("LEGACY_CORE_PLATFORM_SYSTEM_MODULES", LegacyCorePlatformSystemModules) ctx.Strict("ANDROID_JAVA_HOME", "${JavaHome}") ctx.Strict("ANDROID_JAVA8_HOME", "prebuilts/jdk/jdk8/${hostPrebuiltTag}") - ctx.Strict("ANDROID_JAVA9_HOME", "prebuilts/jdk/jdk9/${hostPrebuiltTag}") - ctx.Strict("ANDROID_JAVA11_HOME", "prebuilts/jdk/jdk11/${hostPrebuiltTag}") ctx.Strict("ANDROID_JAVA_TOOLCHAIN", "${JavaToolchain}") ctx.Strict("JAVA", "${JavaCmd} ${JavaVmFlags}") ctx.Strict("JAVAC", "${JavacCmd} ${JavacVmFlags}") diff --git a/java/container_test.go b/java/container_test.go new file mode 100644 index 000000000..344185553 --- /dev/null +++ b/java/container_test.go @@ -0,0 +1,129 @@ +// Copyright 2024 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package java + +import ( + "android/soong/android" + "fmt" + "testing" +) + +var checkContainerMatch = func(t *testing.T, name string, container string, expected bool, actual bool) { + errorMessage := fmt.Sprintf("module %s container %s value differ", name, container) + android.AssertBoolEquals(t, errorMessage, expected, actual) +} + +func TestJavaContainersModuleProperties(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + ).RunTestWithBp(t, ` + java_library { + name: "foo", + srcs: ["A.java"], + } + java_library { + name: "foo_vendor", + srcs: ["A.java"], + vendor: true, + sdk_version: "current", + } + java_library { + name: "foo_soc_specific", + srcs: ["A.java"], + soc_specific: true, + sdk_version: "current", + } + java_library { + name: "foo_product_specific", + srcs: ["A.java"], + product_specific: true, + sdk_version: "current", + } + java_test { + name: "foo_cts_test", + srcs: ["A.java"], + test_suites: [ + "cts", + ], + } + java_test { + name: "foo_non_cts_test", + srcs: ["A.java"], + test_suites: [ + "general-tests", + ], + } + `) + + testcases := []struct { + moduleName string + isSystemContainer bool + isVendorContainer bool + isProductContainer bool + isCts bool + }{ + { + moduleName: "foo", + isSystemContainer: true, + isVendorContainer: false, + isProductContainer: false, + isCts: false, + }, + { + moduleName: "foo_vendor", + isSystemContainer: false, + isVendorContainer: true, + isProductContainer: false, + isCts: false, + }, + { + moduleName: "foo_soc_specific", + isSystemContainer: false, + isVendorContainer: true, + isProductContainer: false, + isCts: false, + }, + { + moduleName: "foo_product_specific", + isSystemContainer: false, + isVendorContainer: false, + isProductContainer: true, + isCts: false, + }, + { + moduleName: "foo_cts_test", + isSystemContainer: false, + isVendorContainer: false, + isProductContainer: false, + isCts: true, + }, + { + moduleName: "foo_non_cts_test", + isSystemContainer: false, + isVendorContainer: false, + isProductContainer: false, + isCts: false, + }, + } + + for _, c := range testcases { + m := result.ModuleForTests(c.moduleName, "android_common") + containers, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), m.Module(), android.ContainersInfoProvider) + belongingContainers := containers.BelongingContainers() + checkContainerMatch(t, c.moduleName, "system", c.isSystemContainer, android.InList(android.SystemContainer, belongingContainers)) + checkContainerMatch(t, c.moduleName, "vendor", c.isVendorContainer, android.InList(android.VendorContainer, belongingContainers)) + checkContainerMatch(t, c.moduleName, "product", c.isProductContainer, android.InList(android.ProductContainer, belongingContainers)) + } +} diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp index 958f4cead..ab72e8b6d 100644 --- a/java/core-libraries/Android.bp +++ b/java/core-libraries/Android.bp @@ -33,22 +33,86 @@ dist_targets = [ "win_sdk", ] -java_library { - name: "core.current.stubs", +java_defaults { + name: "core.current.stubs.defaults", visibility: ["//visibility:public"], + sdk_version: "none", + system_modules: "none", +} + +java_library { + name: "core.current.stubs.from-source", + defaults: [ + "core.current.stubs.defaults", + ], static_libs: [ "art.module.public.api.stubs", "conscrypt.module.public.api.stubs", "i18n.module.public.api.stubs", ], - sdk_version: "none", - system_modules: "none", +} +java_api_library { + name: "core.current.stubs.from-text", + api_surface: "core", + api_contributions: [ + "art.module.public.api.stubs.source.api.contribution", + "conscrypt.module.public.api.stubs.source.api.contribution", + "i18n.module.public.api.stubs.source.api.contribution", + ], + libs: [ + "stub-annotations", + ], + enable_validation: false, + stubs_type: "everything", +} + +java_library { + name: "core.current.stubs", + defaults: [ + "core.current.stubs.defaults", + ], + static_libs: [ + "core.current.stubs.from-source", + ], + product_variables: { + build_from_text_stub: { + static_libs: [ + "core.current.stubs.from-text", + ], + exclude_static_libs: [ + "core.current.stubs.from-source", + ], + }, + }, +} + +java_library { + name: "core.current.stubs.exportable.from-source", + defaults: [ + "core.current.stubs.defaults", + ], + static_libs: [ + "art.module.public.api.stubs.exportable", + "conscrypt.module.public.api.stubs.exportable", + "i18n.module.public.api.stubs.exportable", + ], dist: { targets: dist_targets, + dest: "core.current.stubs.jar", }, } +java_library { + name: "core.current.stubs.exportable", + defaults: [ + "core.current.stubs.defaults", + ], + static_libs: [ + "core.current.stubs.exportable.from-source", + ], +} + // Distributed with the SDK for turning into system modules to compile apps // against. // @@ -120,12 +184,42 @@ java_system_modules { ], } +java_defaults { + name: "core.module_lib.stubs.defaults", + visibility: ["//visibility:private"], + sdk_version: "none", + system_modules: "none", +} + // 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", + defaults: [ + "core.module_lib.stubs.defaults", + ], + static_libs: [ + "core.module_lib.stubs.from-source", + ], + product_variables: { + build_from_text_stub: { + static_libs: [ + "core.module_lib.stubs.from-text", + ], + exclude_static_libs: [ + "core.module_lib.stubs.from-source", + ], + }, + }, +} + +java_library { + name: "core.module_lib.stubs.from-source", + defaults: [ + "core.module_lib.stubs.defaults", + ], static_libs: [ "art.module.public.api.stubs.module_lib", @@ -135,9 +229,27 @@ java_library { "conscrypt.module.public.api.stubs", "i18n.module.public.api.stubs", ], - sdk_version: "none", - system_modules: "none", +} + +java_api_library { + name: "core.module_lib.stubs.from-text", + api_surface: "module-lib", + api_contributions: [ + "art.module.public.api.stubs.source.api.contribution", + "art.module.public.api.stubs.source.system.api.contribution", + "art.module.public.api.stubs.source.module_lib.api.contribution", + + // Add 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.source.api.contribution", + "i18n.module.public.api.stubs.source.api.contribution", + ], + libs: [ + "stub-annotations", + ], visibility: ["//visibility:private"], + stubs_type: "everything", } // Produces a dist file that is used by the @@ -199,18 +311,97 @@ core_platform_visibility = ["//visibility:public"] // API annotations are available to the dex tools that enable enforcement of runtime // accessibility. b/119068555 java_library { + name: "legacy.core.platform.api.stubs.from-source", + visibility: core_platform_visibility, + defaults: [ + "core.platform.api.stubs.defaults", + ], + static_libs: [ + "art.module.public.api.stubs.module_lib", + "conscrypt.module.platform.api.stubs", + "legacy.i18n.module.platform.api.stubs", + ], +} + +java_library { + name: "legacy.core.platform.api.stubs.exportable.from-source", + visibility: core_platform_visibility, + defaults: [ + "core.platform.api.stubs.defaults", + ], + static_libs: [ + "art.module.public.api.stubs.exportable.module_lib", + "conscrypt.module.platform.api.stubs.exportable", + "legacy.i18n.module.platform.api.stubs.exportable", + ], +} + +java_defaults { + name: "android_core_platform_stubs_current_contributions", + api_surface: "core_platform", + api_contributions: [ + "art.module.public.api.stubs.source.api.contribution", + "art.module.public.api.stubs.source.system.api.contribution", + "art.module.public.api.stubs.source.module_lib.api.contribution", + "conscrypt.module.platform.api.stubs.source.api.contribution", + "i18n.module.public.api.stubs.source.api.contribution", + ], +} + +java_api_library { + name: "legacy.core.platform.api.stubs.from-text", + api_surface: "core_platform", + defaults: [ + "android_core_platform_stubs_current_contributions", + ], + api_contributions: [ + "legacy.i18n.module.platform.api.stubs.source.api.contribution", + ], + libs: [ + "stub-annotations", + ], + stubs_type: "everything", +} + +java_library { name: "legacy.core.platform.api.stubs", visibility: core_platform_visibility, + defaults: [ + "core.platform.api.stubs.defaults", + ], + static_libs: [ + "legacy.core.platform.api.stubs.from-source", + ], + product_variables: { + build_from_text_stub: { + static_libs: [ + "legacy.core.platform.api.stubs.from-text", + ], + exclude_static_libs: [ + "legacy.core.platform.api.stubs.from-source", + ], + }, + }, +} + +java_library { + name: "legacy.core.platform.api.stubs.exportable", + visibility: core_platform_visibility, + defaults: [ + "core.platform.api.stubs.defaults", + ], + static_libs: [ + "legacy.core.platform.api.stubs.exportable.from-source", + ], +} + +java_defaults { + name: "core.platform.api.stubs.defaults", 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", } @@ -233,20 +424,53 @@ java_library { } java_library { - name: "stable.core.platform.api.stubs", + name: "stable.core.platform.api.stubs.from-source", visibility: core_platform_visibility, - hostdex: true, - compile_dex: true, - - sdk_version: "none", - system_modules: "none", + defaults: [ + "core.platform.api.stubs.defaults", + ], 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", +} + +java_api_library { + name: "stable.core.platform.api.stubs.from-text", + api_surface: "core_platform", + defaults: [ + "android_core_platform_stubs_current_contributions", + ], + api_contributions: [ + "stable.i18n.module.platform.api.stubs.source.api.contribution", + ], + libs: [ + "stub-annotations", + ], + stubs_type: "everything", +} + +java_library { + name: "stable.core.platform.api.stubs", + visibility: core_platform_visibility, + defaults: [ + "core.platform.api.stubs.defaults", + ], + static_libs: [ + "stable.core.platform.api.stubs.from-source", + ], + product_variables: { + build_from_text_stub: { + static_libs: [ + "stable.core.platform.api.stubs.from-text", + ], + exclude_static_libs: [ + "stable.core.platform.api.stubs.from-source", + ], + }, + }, } // Same as stable.core.platform.api.stubs, but android annotations are @@ -351,7 +575,3 @@ java_system_modules { "art-module-intra-core-api-stubs-system-modules-lib", ], } - -build = [ - "TxtStubLibraries.bp", -] diff --git a/java/core-libraries/TxtStubLibraries.bp b/java/core-libraries/TxtStubLibraries.bp deleted file mode 100644 index 813187e54..000000000 --- a/java/core-libraries/TxtStubLibraries.bp +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (C) 2023 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. - -// This file contains java_system_modules provided by the SDK. -// These system modules transitively depend on core stub libraries generated from .txt files. - -// Same as core-public-stubs-system-modules, but the stubs are generated from .txt files -java_system_modules { - name: "core-public-stubs-system-modules.from-text", - visibility: ["//visibility:public"], - libs: [ - "core-current-stubs-for-system-modules-no-annotations.from-text", - ], - // TODO: Enable after stub generation from .txt file is available - enabled: false, -} - -java_library { - name: "core-current-stubs-for-system-modules-no-annotations.from-text", - visibility: ["//visibility:private"], - defaults: [ - "system-modules-no-annotations", - ], - static_libs: [ - "core.current.stubs.from-text", - "core-lambda-stubs.from-text", - ], - // TODO: Enable after stub generation from .txt file is available - enabled: false, -} - -// Same as core-module-lib-stubs-system-modules, but the stubs are generated from .txt files -java_system_modules { - name: "core-module-lib-stubs-system-modules.from-text", - visibility: ["//visibility:public"], - libs: [ - "core-module-lib-stubs-for-system-modules-no-annotations.from-text", - ], - // TODO: Enable after stub generation from .txt file is available - enabled: false, -} - -java_library { - name: "core-module-lib-stubs-for-system-modules-no-annotations.from-text", - visibility: ["//visibility:private"], - defaults: [ - "system-modules-no-annotations", - ], - static_libs: [ - "core.module_lib.stubs.from-text", - "core-lambda-stubs.from-text", - ], - // TODO: Enable after stub generation from .txt file is available - enabled: false, -} - -java_library { - name: "core.module_lib.stubs.from-text", - static_libs: [ - "art.module.public.api.stubs.module_lib.from-text", - - // 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.from-text", - "i18n.module.public.api.stubs.from-text", - ], - sdk_version: "none", - system_modules: "none", - visibility: ["//visibility:private"], - // TODO: Enable after stub generation from .txt file is available - enabled: false, -} - -// Same as legacy-core-platform-api-stubs-system-modules, but the stubs are generated from .txt files -java_system_modules { - name: "legacy-core-platform-api-stubs-system-modules.from-text", - visibility: core_platform_visibility, - libs: [ - "legacy.core.platform.api.no.annotations.stubs.from-text", - "core-lambda-stubs.from-text", - ], - // TODO: Enable after stub generation from .txt file is available - enabled: false, -} - -java_library { - name: "legacy.core.platform.api.no.annotations.stubs.from-text", - visibility: core_platform_visibility, - defaults: [ - "system-modules-no-annotations", - ], - hostdex: true, - compile_dex: true, - - static_libs: [ - "legacy.core.platform.api.stubs.from-text", - ], - patch_module: "java.base", - // TODO: Enable after stub generation from .txt file is available - enabled: false, -} - -// Same as stable-core-platform-api-stubs-system-modules, but the stubs are generated from .txt files -java_system_modules { - name: "stable-core-platform-api-stubs-system-modules.from-text", - visibility: core_platform_visibility, - libs: [ - "stable.core.platform.api.no.annotations.stubs.from-text", - "core-lambda-stubs.from-text", - ], - // TODO: Enable after stub generation from .txt file is available - enabled: false, -} - -java_library { - name: "stable.core.platform.api.no.annotations.stubs.from-text", - visibility: core_platform_visibility, - defaults: [ - "system-modules-no-annotations", - ], - hostdex: true, - compile_dex: true, - - static_libs: [ - "stable.core.platform.api.stubs.from-text", - ], - patch_module: "java.base", - // TODO: Enable after stub generation from .txt file is available - enabled: false, -} - -java_api_library { - name: "core-lambda-stubs.from-text", - api_surface: "toolchain", - api_contributions: [ - "art.module.toolchain.api.api.contribution", - ], - libs: [ - // LambdaMetaFactory depends on CallSite etc. which is part of the Core API surface - "core.current.stubs.from-text", - ], - // TODO: Enable after stub generation from .txt file is available - enabled: false, -} diff --git a/java/device_host_converter.go b/java/device_host_converter.go index 3581040f8..3f8735c0c 100644 --- a/java/device_host_converter.go +++ b/java/device_host_converter.go @@ -19,16 +19,12 @@ import ( "io" "android/soong/android" - "android/soong/bazel" "android/soong/dexpreopt" - - "github.com/google/blueprint/proptools" ) type DeviceHostConverter struct { android.ModuleBase android.DefaultableModuleBase - android.BazelModuleBase properties DeviceHostConverterProperties @@ -80,7 +76,6 @@ func HostForDeviceFactory() android.Module { module.AddProperties(&module.properties) InitJavaModule(module, android.DeviceSupported) - android.InitBazelModule(module) return module } @@ -102,8 +97,7 @@ func (d *DeviceHostConverter) GenerateAndroidBuildActions(ctx android.ModuleCont } ctx.VisitDirectDepsWithTag(deviceHostConverterDepTag, func(m android.Module) { - if ctx.OtherModuleHasProvider(m, JavaInfoProvider) { - dep := ctx.OtherModuleProvider(m, JavaInfoProvider).(JavaInfo) + if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok { d.headerJars = append(d.headerJars, dep.HeaderJars...) d.implementationJars = append(d.implementationJars, dep.ImplementationJars...) d.implementationAndResourceJars = append(d.implementationAndResourceJars, dep.ImplementationAndResourcesJars...) @@ -136,13 +130,16 @@ func (d *DeviceHostConverter) GenerateAndroidBuildActions(ctx android.ModuleCont d.combinedHeaderJar = d.headerJars[0] } - ctx.SetProvider(JavaInfoProvider, JavaInfo{ + android.SetProvider(ctx, JavaInfoProvider, JavaInfo{ HeaderJars: d.headerJars, ImplementationAndResourcesJars: d.implementationAndResourceJars, ImplementationJars: d.implementationJars, ResourceJars: d.resourceJars, SrcJarArgs: d.srcJarArgs, SrcJarDeps: d.srcJarDeps, + StubsLinkType: Implementation, + // TODO: Not sure if aconfig flags that have been moved between device and host variants + // make sense. }) } @@ -155,7 +152,7 @@ func (d *DeviceHostConverter) ImplementationAndResourcesJars() android.Paths { return d.implementationAndResourceJars } -func (d *DeviceHostConverter) DexJarBuildPath() android.Path { +func (d *DeviceHostConverter) DexJarBuildPath(ctx android.ModuleErrorfContext) android.Path { return nil } @@ -191,32 +188,3 @@ func (d *DeviceHostConverter) AndroidMk() android.AndroidMkData { }, } } - -type bazelDeviceHostConverterAttributes struct { - Exports bazel.LabelListAttribute -} - -func (d *DeviceHostConverter) ConvertWithBp2build(ctx android.TopDownMutatorContext) { - ctx.CreateBazelTargetModule( - bazel.BazelTargetModuleProperties{ - Rule_class: "java_host_for_device", - Bzl_load_location: "//build/bazel/rules/java:host_for_device.bzl", - }, - android.CommonAttributes{Name: d.Name()}, - &bazelDeviceHostConverterAttributes{ - Exports: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, d.properties.Libs)), - }, - ) - neverLinkAttrs := &javaLibraryAttributes{ - Exports: bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + d.Name()}), - Neverlink: bazel.BoolAttribute{Value: proptools.BoolPtr(true)}, - javaCommonAttributes: &javaCommonAttributes{ - Sdk_version: bazel.StringAttribute{Value: proptools.StringPtr("none")}, - }, - } - ctx.CreateBazelTargetModule( - javaLibraryBazelTargetModuleProperties(), - android.CommonAttributes{Name: d.Name() + "-neverlink"}, - neverLinkAttrs) - -} diff --git a/java/device_host_converter_test.go b/java/device_host_converter_test.go index 3c9a0f3f1..6ccc5c1b1 100644 --- a/java/device_host_converter_test.go +++ b/java/device_host_converter_test.go @@ -16,7 +16,7 @@ package java import ( "android/soong/android" - "reflect" + "slices" "strings" "testing" ) @@ -84,7 +84,7 @@ func TestDeviceForHost(t *testing.T) { deviceImportCombined.Output, } - if !reflect.DeepEqual(combined.Inputs, expectedInputs) { + if !slices.Equal(combined.Inputs.Strings(), expectedInputs.Strings()) { t.Errorf("expected host_module combined inputs:\n%q\ngot:\n%q", expectedInputs, combined.Inputs) } @@ -95,7 +95,7 @@ func TestDeviceForHost(t *testing.T) { deviceRes.Output, } - if !reflect.DeepEqual(resCombined.Inputs, expectedInputs) { + if !slices.Equal(resCombined.Inputs.Strings(), expectedInputs.Strings()) { t.Errorf("expected host_module res combined inputs:\n%q\ngot:\n%q", expectedInputs, resCombined.Inputs) } @@ -135,6 +135,7 @@ func TestHostForDevice(t *testing.T) { hostModule := ctx.ModuleForTests("host_module", config.BuildOSCommonTarget.String()) hostJavac := hostModule.Output("javac/host_module.jar") + hostJavacHeader := hostModule.Output("javac-header/host_module.jar") hostRes := hostModule.Output("res/host_module.jar") hostImportModule := ctx.ModuleForTests("host_import_module", config.BuildOSCommonTarget.String()) @@ -148,7 +149,7 @@ func TestHostForDevice(t *testing.T) { // check classpath of device module with dependency on host_for_device_module expectedClasspath := "-classpath " + strings.Join(android.Paths{ - hostJavac.Output, + hostJavacHeader.Output, hostImportCombined.Output, }.Strings(), ":") @@ -164,7 +165,7 @@ func TestHostForDevice(t *testing.T) { hostImportCombined.Output, } - if !reflect.DeepEqual(combined.Inputs, expectedInputs) { + if !slices.Equal(combined.Inputs.Strings(), expectedInputs.Strings()) { t.Errorf("expected device_module combined inputs:\n%q\ngot:\n%q", expectedInputs, combined.Inputs) } @@ -175,7 +176,7 @@ func TestHostForDevice(t *testing.T) { hostRes.Output, } - if !reflect.DeepEqual(resCombined.Inputs, expectedInputs) { + if !slices.Equal(resCombined.Inputs.Strings(), expectedInputs.Strings()) { t.Errorf("expected device_module res combined inputs:\n%q\ngot:\n%q", expectedInputs, resCombined.Inputs) } diff --git a/java/dex.go b/java/dex.go index 4d6aa3456..7bb69257c 100644 --- a/java/dex.go +++ b/java/dex.go @@ -45,8 +45,8 @@ type DexProperties struct { // Whether to continue building even if warnings are emitted. Defaults to true. Ignore_warnings *bool - // If true, runs R8 in Proguard compatibility mode (default). - // Otherwise, runs R8 in full mode. + // If true, runs R8 in Proguard compatibility mode, otherwise runs R8 in full mode. + // Defaults to false for apps, true for libraries and tests. Proguard_compatibility *bool // If true, optimize for size by removing unused code. Defaults to true for apps, @@ -66,11 +66,21 @@ type DexProperties struct { // If true, optimize for size by removing unused resources. Defaults to false. Shrink_resources *bool + // If true, use optimized resource shrinking in R8, overriding the + // Shrink_resources setting. Defaults to false. + // Optimized shrinking means that R8 will trace and treeshake resources together with code + // and apply additional optimizations. This implies non final fields in the R classes. + Optimized_shrink_resources *bool + // Flags to pass to proguard. Proguard_flags []string // Specifies the locations of files containing proguard flags. Proguard_flags_files []string `android:"path"` + + // If true, transitive reverse dependencies of this module will have this + // module's proguard spec appended to their optimization action + Export_proguard_flags_files *bool } // Keep the data uncompressed. We always need uncompressed dex for execution, @@ -81,16 +91,22 @@ type DexProperties struct { // Exclude kotlinc generate files: *.kotlin_module, *.kotlin_builtins. Defaults to false. Exclude_kotlinc_generated_files *bool + + // Disable dex container (also known as "multi-dex"). + // This may be necessary as a temporary workaround to mask toolchain bugs (see b/341652226). + No_dex_container *bool } type dexer struct { dexProperties DexProperties // list of extra proguard flag files - extraProguardFlagFiles android.Paths - proguardDictionary android.OptionalPath - proguardConfiguration android.OptionalPath - proguardUsageZip android.OptionalPath + extraProguardFlagsFiles android.Paths + proguardDictionary android.OptionalPath + proguardConfiguration android.OptionalPath + proguardUsageZip android.OptionalPath + resourcesInput android.OptionalPath + resourcesOutput android.OptionalPath providesTransitiveHeaderJars } @@ -99,17 +115,27 @@ func (d *dexer) effectiveOptimizeEnabled() bool { return BoolDefault(d.dexProperties.Optimize.Enabled, d.dexProperties.Optimize.EnabledByDefault) } +func (d *DexProperties) resourceShrinkingEnabled(ctx android.ModuleContext) bool { + return !ctx.Config().Eng() && BoolDefault(d.Optimize.Optimized_shrink_resources, Bool(d.Optimize.Shrink_resources)) +} + +func (d *DexProperties) optimizedResourceShrinkingEnabled(ctx android.ModuleContext) bool { + return d.resourceShrinkingEnabled(ctx) && Bool(d.Optimize.Optimized_shrink_resources) +} + +func (d *dexer) optimizeOrObfuscateEnabled() bool { + return d.effectiveOptimizeEnabled() && (proptools.Bool(d.dexProperties.Optimize.Optimize) || proptools.Bool(d.dexProperties.Optimize.Obfuscate)) +} + var d8, d8RE = pctx.MultiCommandRemoteStaticRules("d8", blueprint.RuleParams{ Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` + - `mkdir -p $$(dirname $tmpJar) && ` + - `${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` + - `$d8Template${config.D8Cmd} ${config.D8Flags} --output $outDir $d8Flags $tmpJar && ` + + `$d8Template${config.D8Cmd} ${config.D8Flags} $d8Flags --output $outDir --no-dex-input-jar $in && ` + `$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` + - `${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in`, + `${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in && ` + + `rm -f "$outDir/classes*.dex" "$outDir/classes.dex.jar"`, CommandDeps: []string{ "${config.D8Cmd}", - "${config.Zip2ZipCmd}", "${config.SoongZipCmd}", "${config.MergeZipsCmd}", }, @@ -128,32 +154,29 @@ var d8, d8RE = pctx.MultiCommandRemoteStaticRules("d8", ExecStrategy: "${config.RED8ExecStrategy}", Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"}, }, - }, []string{"outDir", "d8Flags", "zipFlags", "tmpJar", "mergeZipsFlags"}, nil) + }, []string{"outDir", "d8Flags", "zipFlags", "mergeZipsFlags"}, nil) var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8", blueprint.RuleParams{ Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` + `rm -f "$outDict" && rm -f "$outConfig" && rm -rf "${outUsageDir}" && ` + `mkdir -p $$(dirname ${outUsage}) && ` + - `mkdir -p $$(dirname $tmpJar) && ` + - `${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` + - `$r8Template${config.R8Cmd} ${config.R8Flags} -injars $tmpJar --output $outDir ` + + `$r8Template${config.R8Cmd} ${config.R8Flags} $r8Flags -injars $in --output $outDir ` + `--no-data-resources ` + `-printmapping ${outDict} ` + - `--pg-conf-output ${outConfig} ` + + `-printconfiguration ${outConfig} ` + `-printusage ${outUsage} ` + - `--deps-file ${out}.d ` + - `$r8Flags && ` + + `--deps-file ${out}.d && ` + `touch "${outDict}" "${outConfig}" "${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" $mergeZipsFlags $out $outDir/classes.dex.jar $in`, + `${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in && ` + + `rm -f "$outDir/classes*.dex" "$outDir/classes.dex.jar"`, Depfile: "${out}.d", Deps: blueprint.DepsGCC, CommandDeps: []string{ "${config.R8Cmd}", - "${config.Zip2ZipCmd}", "${config.SoongZipCmd}", "${config.MergeZipsCmd}", }, @@ -161,7 +184,7 @@ var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8", "$r8Template": &remoteexec.REParams{ Labels: map[string]string{"type": "compile", "compiler": "r8"}, Inputs: []string{"$implicits", "${config.R8Jar}"}, - OutputFiles: []string{"${outUsage}"}, + OutputFiles: []string{"${outUsage}", "${outConfig}", "${outDict}", "${resourcesOutput}", "${outR8ArtProfile}"}, ExecStrategy: "${config.RER8ExecStrategy}", ToolchainInputs: []string{"${config.JavaCmd}"}, Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"}, @@ -181,7 +204,7 @@ var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8", Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"}, }, }, []string{"outDir", "outDict", "outConfig", "outUsage", "outUsageZip", "outUsageDir", - "r8Flags", "zipFlags", "tmpJar", "mergeZipsFlags"}, []string{"implicits"}) + "r8Flags", "zipFlags", "mergeZipsFlags", "resourcesOutput", "outR8ArtProfile"}, []string{"implicits"}) func (d *dexer) dexCommonFlags(ctx android.ModuleContext, dexParams *compileDexParams) (flags []string, deps android.Paths) { @@ -213,8 +236,9 @@ func (d *dexer) dexCommonFlags(ctx android.ModuleContext, // Note: Targets with a min SDK kind of core_platform (e.g., framework.jar) or unspecified (e.g., // services.jar), are not classified as stable, which is WAI. // TODO(b/232073181): Expand to additional min SDK cases after validation. + var addAndroidPlatformBuildFlag = false if !dexParams.sdkVersion.Stable() { - flags = append(flags, "--android-platform-build") + addAndroidPlatformBuildFlag = true } effectiveVersion, err := dexParams.minSdkVersion.EffectiveVersion(ctx) @@ -222,21 +246,40 @@ func (d *dexer) dexCommonFlags(ctx android.ModuleContext, ctx.PropertyErrorf("min_sdk_version", "%s", err) } - flags = append(flags, "--min-api "+strconv.Itoa(effectiveVersion.FinalOrFutureInt())) + // If the specified SDK level is 10000, then configure the compiler to use the + // current platform SDK level and to compile the build as a platform build. + var minApiFlagValue = effectiveVersion.FinalOrFutureInt() + if minApiFlagValue == 10000 { + minApiFlagValue = ctx.Config().PlatformSdkVersion().FinalInt() + addAndroidPlatformBuildFlag = true + } + flags = append(flags, "--min-api "+strconv.Itoa(minApiFlagValue)) + + if addAndroidPlatformBuildFlag { + flags = append(flags, "--android-platform-build") + } return flags, deps } -func d8Flags(flags javaBuilderFlags) (d8Flags []string, d8Deps android.Paths) { +func (d *dexer) d8Flags(ctx android.ModuleContext, dexParams *compileDexParams) (d8Flags []string, d8Deps android.Paths, artProfileOutput *android.OutputPath) { + flags := dexParams.flags d8Flags = append(d8Flags, flags.bootClasspath.FormRepeatedClassPath("--lib ")...) d8Flags = append(d8Flags, flags.dexClasspath.FormRepeatedClassPath("--lib ")...) d8Deps = append(d8Deps, flags.bootClasspath...) d8Deps = append(d8Deps, flags.dexClasspath...) - return d8Flags, d8Deps + if flags, deps, profileOutput := d.addArtProfile(ctx, dexParams); profileOutput != nil { + d8Flags = append(d8Flags, flags...) + d8Deps = append(d8Deps, deps...) + artProfileOutput = profileOutput + } + + return d8Flags, d8Deps, artProfileOutput } -func (d *dexer) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Flags []string, r8Deps android.Paths) { +func (d *dexer) r8Flags(ctx android.ModuleContext, dexParams *compileDexParams) (r8Flags []string, r8Deps android.Paths, artProfileOutput *android.OutputPath) { + flags := dexParams.flags opt := d.dexProperties.Optimize // When an app contains references to APIs that are not in the SDK specified by @@ -248,8 +291,8 @@ func (d *dexer) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Fl // See b/20667396 var proguardRaiseDeps classpath ctx.VisitDirectDepsWithTag(proguardRaiseTag, func(m android.Module) { - dep := ctx.OtherModuleProvider(m, JavaInfoProvider).(JavaInfo) - proguardRaiseDeps = append(proguardRaiseDeps, dep.HeaderJars...) + dep, _ := android.OtherModuleProvider(ctx, m, JavaInfoProvider) + proguardRaiseDeps = append(proguardRaiseDeps, dep.RepackagedHeaderJars...) }) r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars")) @@ -283,11 +326,13 @@ func (d *dexer) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Fl android.PathForSource(ctx, "build/make/core/proguard.flags"), } - flagFiles = append(flagFiles, d.extraProguardFlagFiles...) + flagFiles = append(flagFiles, d.extraProguardFlagsFiles...) // TODO(ccross): static android library proguard files flagFiles = append(flagFiles, android.PathsForModuleSrc(ctx, opt.Proguard_flags_files)...) + flagFiles = android.FirstUniquePaths(flagFiles) + r8Flags = append(r8Flags, android.JoinWithPrefix(flagFiles.Strings(), "-include ")) r8Deps = append(r8Deps, flagFiles...) @@ -299,15 +344,14 @@ func (d *dexer) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Fl if BoolDefault(opt.Proguard_compatibility, true) { r8Flags = append(r8Flags, "--force-proguard-compatibility") - } else { + } + + if Bool(opt.Optimize) || Bool(opt.Obfuscate) { // 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) - } + 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. @@ -337,23 +381,58 @@ func (d *dexer) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Fl r8Flags = append(r8Flags, "-ignorewarnings") } - return r8Flags, r8Deps + // resourcesInput is empty when we don't use resource shrinking, if on, pass these to R8 + if d.resourcesInput.Valid() { + r8Flags = append(r8Flags, "--resource-input", d.resourcesInput.Path().String()) + r8Deps = append(r8Deps, d.resourcesInput.Path()) + r8Flags = append(r8Flags, "--resource-output", d.resourcesOutput.Path().String()) + if Bool(opt.Optimized_shrink_resources) { + r8Flags = append(r8Flags, "--optimized-resource-shrinking") + } + } + + if flags, deps, profileOutput := d.addArtProfile(ctx, dexParams); profileOutput != nil { + r8Flags = append(r8Flags, flags...) + r8Deps = append(r8Deps, deps...) + artProfileOutput = profileOutput + } + + return r8Flags, r8Deps, artProfileOutput } type compileDexParams struct { - flags javaBuilderFlags - sdkVersion android.SdkSpec - minSdkVersion android.ApiLevel - classesJar android.Path - jarName string + flags javaBuilderFlags + sdkVersion android.SdkSpec + minSdkVersion android.ApiLevel + classesJar android.Path + jarName string + artProfileInput *string } -func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParams) android.OutputPath { +// Adds --art-profile to r8/d8 command. +// r8/d8 will output a generated profile file to match the optimized dex code. +func (d *dexer) addArtProfile(ctx android.ModuleContext, dexParams *compileDexParams) (flags []string, deps android.Paths, artProfileOutputPath *android.OutputPath) { + if dexParams.artProfileInput != nil { + artProfileInputPath := android.PathForModuleSrc(ctx, *dexParams.artProfileInput) + artProfileOutputPathValue := android.PathForModuleOut(ctx, "profile.prof.txt").OutputPath + artProfileOutputPath = &artProfileOutputPathValue + flags = []string{ + "--art-profile", + artProfileInputPath.String(), + artProfileOutputPath.String(), + } + deps = append(deps, artProfileInputPath) + } + return flags, deps, artProfileOutputPath + +} + +// Return the compiled dex jar and (optional) profile _after_ r8 optimization +func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParams) (android.OutputPath, *android.OutputPath) { // Compile classes.jar into classes.dex and then javalib.jar javalibJar := android.PathForModuleOut(ctx, "dex", dexParams.jarName).OutputPath outDir := android.PathForModuleOut(ctx, "dex") - tmpJar := android.PathForModuleOut(ctx, "withres-withoutdex", dexParams.jarName) zipFlags := "--ignore_missing_files" if proptools.Bool(d.dexProperties.Uncompress_dex) { @@ -369,6 +448,7 @@ func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParam } useR8 := d.effectiveOptimizeEnabled() + var artProfileOutputPath *android.OutputPath if useR8 { proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary") d.proguardDictionary = android.OptionalPathForPath(proguardDictionary) @@ -379,8 +459,14 @@ func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParam android.ModuleNameWithPossibleOverride(ctx), "unused.txt") proguardUsageZip := android.PathForModuleOut(ctx, "proguard_usage.zip") d.proguardUsageZip = android.OptionalPathForPath(proguardUsageZip) - r8Flags, r8Deps := d.r8Flags(ctx, dexParams.flags) - r8Deps = append(r8Deps, commonDeps...) + resourcesOutput := android.PathForModuleOut(ctx, "package-res-shrunken.apk") + d.resourcesOutput = android.OptionalPathForPath(resourcesOutput) + implicitOutputs := android.WritablePaths{ + proguardDictionary, + proguardUsageZip, + proguardConfiguration, + } + r8Flags, r8Deps, r8ArtProfileOutputPath := d.r8Flags(ctx, dexParams) rule := r8 args := map[string]string{ "r8Flags": strings.Join(append(commonFlags, r8Flags...), " "), @@ -391,49 +477,71 @@ func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParam "outUsage": proguardUsage.String(), "outUsageZip": proguardUsageZip.String(), "outDir": outDir.String(), - "tmpJar": tmpJar.String(), "mergeZipsFlags": mergeZipsFlags, } + if r8ArtProfileOutputPath != nil { + artProfileOutputPath = r8ArtProfileOutputPath + implicitOutputs = append( + implicitOutputs, + artProfileOutputPath, + ) + // Add the implicit r8 Art profile output to args so that r8RE knows + // about this implicit output + args["outR8ArtProfile"] = artProfileOutputPath.String() + } + if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_R8") { rule = r8RE args["implicits"] = strings.Join(r8Deps.Strings(), ",") } + if d.resourcesInput.Valid() { + implicitOutputs = append(implicitOutputs, resourcesOutput) + args["resourcesOutput"] = resourcesOutput.String() + } ctx.Build(pctx, android.BuildParams{ Rule: rule, Description: "r8", Output: javalibJar, - ImplicitOutputs: android.WritablePaths{proguardDictionary, proguardUsageZip}, + ImplicitOutputs: implicitOutputs, Input: dexParams.classesJar, Implicits: r8Deps, Args: args, }) } else { - d8Flags, d8Deps := d8Flags(dexParams.flags) + implicitOutputs := android.WritablePaths{} + d8Flags, d8Deps, d8ArtProfileOutputPath := d.d8Flags(ctx, dexParams) + if d8ArtProfileOutputPath != nil { + artProfileOutputPath = d8ArtProfileOutputPath + implicitOutputs = append( + implicitOutputs, + artProfileOutputPath, + ) + } d8Deps = append(d8Deps, commonDeps...) rule := d8 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8") { rule = d8RE } ctx.Build(pctx, android.BuildParams{ - Rule: rule, - Description: "d8", - Output: javalibJar, - Input: dexParams.classesJar, - Implicits: d8Deps, + Rule: rule, + Description: "d8", + Output: javalibJar, + Input: dexParams.classesJar, + ImplicitOutputs: implicitOutputs, + Implicits: d8Deps, Args: map[string]string{ "d8Flags": strings.Join(append(commonFlags, d8Flags...), " "), "zipFlags": zipFlags, "outDir": outDir.String(), - "tmpJar": tmpJar.String(), "mergeZipsFlags": mergeZipsFlags, }, }) } if proptools.Bool(d.dexProperties.Uncompress_dex) { alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", dexParams.jarName).OutputPath - TransformZipAlign(ctx, alignedJavalibJar, javalibJar) + TransformZipAlign(ctx, alignedJavalibJar, javalibJar, nil) javalibJar = alignedJavalibJar } - return javalibJar + return javalibJar, artProfileOutputPath } diff --git a/java/dex_test.go b/java/dex_test.go index 2ba3831f4..4862d06c9 100644 --- a/java/dex_test.go +++ b/java/dex_test.go @@ -15,6 +15,7 @@ package java import ( + "fmt" "testing" "android/soong/android" @@ -327,7 +328,7 @@ func TestD8(t *testing.T) { fooD8.Args["d8Flags"], staticLibHeader.String()) } -func TestProguardFlagsInheritance(t *testing.T) { +func TestProguardFlagsInheritanceStatic(t *testing.T) { result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, ` android_app { name: "app", @@ -380,3 +381,335 @@ func TestProguardFlagsInheritance(t *testing.T) { android.AssertStringDoesContain(t, "expected tertiary_lib's proguard flags from inherited dep", appR8.Args["r8Flags"], "tertiary.flags") } + +func TestProguardFlagsInheritance(t *testing.T) { + directDepFlagsFileName := "direct_dep.flags" + transitiveDepFlagsFileName := "transitive_dep.flags" + + topLevelModules := []struct { + name string + definition string + }{ + { + name: "android_app", + definition: ` + android_app { + name: "app", + static_libs: ["androidlib"], // this must be static_libs to initate dexing + platform_apis: true, + } + `, + }, + { + name: "android_library", + definition: ` + android_library { + name: "app", + static_libs: ["androidlib"], // this must be static_libs to initate dexing + installable: true, + optimize: { + enabled: true, + shrink: true, + }, + } + `, + }, + { + name: "java_library", + definition: ` + java_library { + name: "app", + static_libs: ["androidlib"], // this must be static_libs to initate dexing + srcs: ["Foo.java"], + installable: true, + optimize: { + enabled: true, + shrink: true, + }, + } + `, + }, + } + + bp := ` + android_library { + name: "androidlib", + static_libs: ["app_dep"], + } + + java_library { + name: "app_dep", + %s: ["dep"], + } + + java_library { + name: "dep", + %s: ["transitive_dep"], + optimize: { + proguard_flags_files: ["direct_dep.flags"], + export_proguard_flags_files: %v, + }, + } + + java_library { + name: "transitive_dep", + optimize: { + proguard_flags_files: ["transitive_dep.flags"], + export_proguard_flags_files: %v, + }, + } + ` + + testcases := []struct { + name string + depType string + depExportsFlagsFiles bool + transitiveDepType string + transitiveDepExportsFlagsFiles bool + expectedFlagsFiles []string + }{ + { + name: "libs_export_libs_export", + depType: "libs", + depExportsFlagsFiles: true, + transitiveDepType: "libs", + transitiveDepExportsFlagsFiles: true, + expectedFlagsFiles: []string{directDepFlagsFileName, transitiveDepFlagsFileName}, + }, + { + name: "static_export_libs_export", + depType: "static_libs", + depExportsFlagsFiles: true, + transitiveDepType: "libs", + transitiveDepExportsFlagsFiles: true, + expectedFlagsFiles: []string{directDepFlagsFileName, transitiveDepFlagsFileName}, + }, + { + name: "libs_no-export_static_export", + depType: "libs", + depExportsFlagsFiles: false, + transitiveDepType: "static_libs", + transitiveDepExportsFlagsFiles: true, + expectedFlagsFiles: []string{transitiveDepFlagsFileName}, + }, + { + name: "static_no-export_static_export", + depType: "static_libs", + depExportsFlagsFiles: false, + transitiveDepType: "static_libs", + transitiveDepExportsFlagsFiles: true, + expectedFlagsFiles: []string{directDepFlagsFileName, transitiveDepFlagsFileName}, + }, + { + name: "libs_export_libs_no-export", + depType: "libs", + depExportsFlagsFiles: true, + transitiveDepType: "libs", + transitiveDepExportsFlagsFiles: false, + expectedFlagsFiles: []string{directDepFlagsFileName}, + }, + { + name: "static_export_libs_no-export", + depType: "static_libs", + depExportsFlagsFiles: true, + transitiveDepType: "libs", + transitiveDepExportsFlagsFiles: false, + expectedFlagsFiles: []string{directDepFlagsFileName}, + }, + { + name: "libs_no-export_static_no-export", + depType: "libs", + depExportsFlagsFiles: false, + transitiveDepType: "static_libs", + transitiveDepExportsFlagsFiles: false, + expectedFlagsFiles: []string{}, + }, + { + name: "static_no-export_static_no-export", + depType: "static_libs", + depExportsFlagsFiles: false, + transitiveDepType: "static_libs", + transitiveDepExportsFlagsFiles: false, + expectedFlagsFiles: []string{directDepFlagsFileName, transitiveDepFlagsFileName}, + }, + { + name: "libs_no-export_libs_export", + depType: "libs", + depExportsFlagsFiles: false, + transitiveDepType: "libs", + transitiveDepExportsFlagsFiles: true, + expectedFlagsFiles: []string{transitiveDepFlagsFileName}, + }, + { + name: "static_no-export_libs_export", + depType: "static_libs", + depExportsFlagsFiles: false, + transitiveDepType: "libs", + transitiveDepExportsFlagsFiles: true, + expectedFlagsFiles: []string{directDepFlagsFileName, transitiveDepFlagsFileName}, + }, + { + name: "libs_export_static_export", + depType: "libs", + depExportsFlagsFiles: true, + transitiveDepType: "static_libs", + transitiveDepExportsFlagsFiles: true, + expectedFlagsFiles: []string{directDepFlagsFileName, transitiveDepFlagsFileName}, + }, + { + name: "static_export_static_export", + depType: "static_libs", + depExportsFlagsFiles: true, + transitiveDepType: "static_libs", + transitiveDepExportsFlagsFiles: true, + expectedFlagsFiles: []string{directDepFlagsFileName, transitiveDepFlagsFileName}, + }, + { + name: "libs_no-export_libs_no-export", + depType: "libs", + depExportsFlagsFiles: false, + transitiveDepType: "libs", + transitiveDepExportsFlagsFiles: false, + expectedFlagsFiles: []string{}, + }, + { + name: "static_no-export_libs_no-export", + depType: "static_libs", + depExportsFlagsFiles: false, + transitiveDepType: "libs", + transitiveDepExportsFlagsFiles: false, + expectedFlagsFiles: []string{directDepFlagsFileName}, + }, + { + name: "libs_export_static_no-export", + depType: "libs", + depExportsFlagsFiles: true, + transitiveDepType: "static_libs", + transitiveDepExportsFlagsFiles: false, + expectedFlagsFiles: []string{directDepFlagsFileName, transitiveDepFlagsFileName}, + }, + { + name: "static_export_static_no-export", + depType: "static_libs", + depExportsFlagsFiles: true, + transitiveDepType: "static_libs", + transitiveDepExportsFlagsFiles: false, + expectedFlagsFiles: []string{directDepFlagsFileName, transitiveDepFlagsFileName}, + }, + } + + for _, topLevelModuleDef := range topLevelModules { + for _, tc := range testcases { + t.Run(topLevelModuleDef.name+"-"+tc.name, func(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + android.FixtureMergeMockFs(android.MockFS{ + directDepFlagsFileName: nil, + transitiveDepFlagsFileName: nil, + }), + ).RunTestWithBp(t, + topLevelModuleDef.definition+ + fmt.Sprintf( + bp, + tc.depType, + tc.transitiveDepType, + tc.depExportsFlagsFiles, + tc.transitiveDepExportsFlagsFiles, + ), + ) + appR8 := result.ModuleForTests("app", "android_common").Rule("r8") + + shouldHaveDepFlags := android.InList(directDepFlagsFileName, tc.expectedFlagsFiles) + if shouldHaveDepFlags { + android.AssertStringDoesContain(t, "expected deps's proguard flags", + appR8.Args["r8Flags"], directDepFlagsFileName) + } else { + android.AssertStringDoesNotContain(t, "app did not expect deps's proguard flags", + appR8.Args["r8Flags"], directDepFlagsFileName) + } + + shouldHaveTransitiveDepFlags := android.InList(transitiveDepFlagsFileName, tc.expectedFlagsFiles) + if shouldHaveTransitiveDepFlags { + android.AssertStringDoesContain(t, "expected transitive deps's proguard flags", + appR8.Args["r8Flags"], transitiveDepFlagsFileName) + } else { + android.AssertStringDoesNotContain(t, "app did not expect transitive deps's proguard flags", + appR8.Args["r8Flags"], transitiveDepFlagsFileName) + } + }) + } + } +} + +func TestProguardFlagsInheritanceAppImport(t *testing.T) { + bp := ` + android_app { + name: "app", + static_libs: ["aarimport"], // this must be static_libs to initate dexing + platform_apis: true, + } + + android_library_import { + name: "aarimport", + aars: ["import.aar"], + } + ` + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + ).RunTestWithBp(t, bp) + + appR8 := result.ModuleForTests("app", "android_common").Rule("r8") + android.AssertStringDoesContain(t, "expected aarimports's proguard flags", + appR8.Args["r8Flags"], "proguard.txt") +} + +func TestR8FlagsArtProfile(t *testing.T) { + result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, ` + android_app { + name: "app", + srcs: ["foo.java"], + platform_apis: true, + dex_preopt: { + profile_guided: true, + profile: "profile.txt.prof", + enable_profile_rewriting: true, + }, + } + `) + + app := result.ModuleForTests("app", "android_common") + appR8 := app.Rule("r8") + android.AssertStringDoesContain(t, "expected --art-profile in app r8 flags", + appR8.Args["r8Flags"], "--art-profile") + + appDexpreopt := app.Rule("dexpreopt") + android.AssertStringDoesContain(t, + "expected --art-profile output to be used to create .prof binary", + appDexpreopt.RuleParams.Command, + "--create-profile-from=out/soong/.intermediates/app/android_common/profile.prof.txt --output-profile-type=app", + ) +} + +// This test checks that users explicitly set `enable_profile_rewriting` to true when the following are true +// 1. optimize or obfuscate is enabled AND +// 2. dex_preopt.profile_guided is enabled +// +// The rewritten profile should be used since the dex signatures in the checked-in profile will not match the optimized binary. +func TestEnableProfileRewritingIsRequiredForOptimizedApps(t *testing.T) { + testJavaError(t, + "Enable_profile_rewriting must be true when profile_guided dexpreopt and R8 optimization/obfuscation is turned on", + ` +android_app { + name: "app", + srcs: ["foo.java"], + platform_apis: true, + dex_preopt: { + profile_guided: true, + profile: "profile.txt.prof", + // enable_profile_rewriting is not set, this is an error + }, + optimize: { + optimize: true, + } +}`) +} diff --git a/java/dexpreopt.go b/java/dexpreopt.go index a96b31281..794924401 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -16,8 +16,11 @@ package java import ( "path/filepath" + "sort" "strings" + "github.com/google/blueprint/proptools" + "android/soong/android" "android/soong/dexpreopt" ) @@ -28,7 +31,7 @@ type DexpreopterInterface interface { IsInstallable() bool // True if dexpreopt is disabled for the java module. - dexpreoptDisabled(ctx android.BaseModuleContext) bool + dexpreoptDisabled(ctx android.BaseModuleContext, libraryName string) bool // If the java module is to be installed into an APEX, this list contains information about the // dexpreopt outputs to be installed on devices. Note that these dexpreopt outputs are installed @@ -78,22 +81,38 @@ func (install *dexpreopterInstall) SubModuleName() string { 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", install.FullModuleName()) entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String()) entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice) entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false") + // Unset LOCAL_SOONG_INSTALLED_MODULE so that this does not default to the primary .apex file + // Without this, installation of the dexpreopt artifacts get skipped + entries.SetString("LOCAL_SOONG_INSTALLED_MODULE", "") }, }, } } +func (install dexpreopterInstall) PackageFile(ctx android.ModuleContext) android.PackagingSpec { + return ctx.PackageFile(install.installDirOnDevice, install.installFileOnDevice, install.outputPathOnHost) +} + +type Dexpreopter struct { + dexpreopter +} + type dexpreopter struct { dexpreoptProperties DexpreoptProperties importDexpreoptProperties ImportDexpreoptProperties + // If true, the dexpreopt rules will not be generated + // Unlike Dex_preopt.Enabled which is user-facing, + // shouldDisableDexpreopt is a mutated propery. + shouldDisableDexpreopt bool + installPath android.InstallPath uncompressedDex bool isSDKLibrary bool @@ -126,6 +145,10 @@ type dexpreopter struct { // The path to the profile that dexpreopter accepts. It must be in the binary format. If this is // set, it overrides the profile settings in `dexpreoptProperties`. inputProfilePathOnHost android.Path + + // The path to the profile that matches the dex optimized by r8/d8. It is in text format. If this is + // set, it will be converted to a binary profile which will be subsequently used for dexpreopt. + rewrittenProfile android.Path } type DexpreoptProperties struct { @@ -145,6 +168,11 @@ type DexpreoptProperties struct { // defaults to searching for a file that matches the name of this module in the default // profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found. Profile *string `android:"path"` + + // If set to true, r8/d8 will use `profile` as input to generate a new profile that matches + // the optimized dex. + // The new profile will be subsequently used as the profile to dexpreopt the dex file. + Enable_profile_rewriting *bool } Dex_preopt_result struct { @@ -165,24 +193,47 @@ func init() { } func isApexVariant(ctx android.BaseModuleContext) bool { - apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) return !apexInfo.IsForPlatform() } func forPrebuiltApex(ctx android.BaseModuleContext) bool { - apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) 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()) +// For apex variant of modules, this returns true on the source variant if the prebuilt apex +// has been selected using apex_contributions. +// The prebuilt apex will be responsible for generating the dexpreopt rules of the deapexed java lib. +func disableSourceApexVariant(ctx android.BaseModuleContext) bool { + if !isApexVariant(ctx) { + return false // platform variant + } + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) + psi := android.PrebuiltSelectionInfoMap{} + ctx.VisitDirectDeps(func(am android.Module) { + if prebuiltSelectionInfo, ok := android.OtherModuleProvider(ctx, am, android.PrebuiltSelectionInfoProvider); ok { + psi = prebuiltSelectionInfo + } + }) + // Find the apex variant for this module + _, apexVariantsWithoutTestApexes, _ := android.ListSetDifference(apexInfo.InApexVariants, apexInfo.TestApexes) + disableSource := false + // find the selected apexes + for _, apexVariant := range apexVariantsWithoutTestApexes { + for _, selected := range psi.GetSelectedModulesForApiDomain(apexVariant) { + // If the apex_contribution for this api domain contains a prebuilt apex, disable the source variant + if strings.HasPrefix(selected, "prebuilt_com.google.android") { + disableSource = true + } + } + } + return disableSource } // Returns whether dexpreopt is applicable to the module. // When it returns true, neither profile nor dexpreopt artifacts will be generated. -func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool { +func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext, libName string) bool { if !ctx.Device() { return true } @@ -195,6 +246,10 @@ func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool { return true } + if d.shouldDisableDexpreopt { + return true + } + // If the module is from a prebuilt APEX, it shouldn't be installable, but it can still be // dexpreopted. if !ctx.Module().(DexpreopterInterface).IsInstallable() && !forPrebuiltApex(ctx) { @@ -205,14 +260,37 @@ func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool { return true } + if _, isApex := android.ModuleProvider(ctx, android.ApexBundleInfoProvider); isApex { + // dexpreopt rules for system server jars can be generated in the ModuleCtx of prebuilt apexes + return false + } + global := dexpreopt.GetGlobalConfig(ctx) - 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. + // Use the libName argument to determine if the library being dexpreopt'd is a system server jar + // ctx.ModuleName() is not safe. In case of prebuilt apexes, the dexpreopt rules of system server jars + // are created in the ctx object of the top-level prebuilt apex. + isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(libName) + + if _, isApex := android.ModuleProvider(ctx, android.ApexBundleInfoProvider); isApex || isApexVariant(ctx) { + // dexpreopt rules for system server jars can be generated in the ModuleCtx of prebuilt apexes if !isApexSystemServerJar { return true } + ai, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) + allApexInfos := []android.ApexInfo{} + if allApexInfosProvider, ok := android.ModuleProvider(ctx, android.AllApexInfoProvider); ok { + allApexInfos = allApexInfosProvider.ApexInfos + } + if len(allApexInfos) > 0 && !ai.MinSdkVersion.EqualTo(allApexInfos[0].MinSdkVersion) { + // Apex system server jars are dexpreopted and installed on to the system image. + // Since we can have BigAndroid and Go variants of system server jar providing apexes, + // and these two variants can have different min_sdk_versions, hide one of the apex variants + // from make to prevent collisions. + // + // Unlike cc, min_sdk_version does not have an effect on the build actions of java libraries. + ctx.Module().MakeUninstallable() + } } else { // Don't preopt the platform variant of an APEX system server jar to avoid conflicts. if isApexSystemServerJar { @@ -226,16 +304,18 @@ func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool { } func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) { - if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) || !dexpreopt.IsDex2oatNeeded(ctx) { + if _, isApex := android.ModuleProvider(ctx, android.ApexBundleInfoProvider); isApex && dexpreopt.IsDex2oatNeeded(ctx) { + // prebuilt apexes can genererate rules to dexpreopt deapexed jars + // Add a dex2oat dep aggressively on _every_ apex module + dexpreopt.RegisterToolDeps(ctx) + return + } + if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName())) || !dexpreopt.IsDex2oatNeeded(ctx) { return } dexpreopt.RegisterToolDeps(ctx) } -func (d *dexpreopter) odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool { - return dexpreopt.OdexOnSystemOtherByName(moduleName(ctx), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx)) -} - // Returns the install path of the dex jar of a module. // // Do not rely on `ApexInfo.ApexVariationName` because it can be something like "apex1000", rather @@ -244,20 +324,42 @@ func (d *dexpreopter) odexOnSystemOther(ctx android.ModuleContext, installPath a // 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 { + ctx android.ModuleContext, libName string, defaultInstallPath android.InstallPath) android.InstallPath { global := dexpreopt.GetGlobalConfig(ctx) - if global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) { - dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, moduleName(ctx)) + if global.AllApexSystemServerJars(ctx).ContainsJar(libName) { + dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, libName) return android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexLocation, "/")) } - if !d.dexpreoptDisabled(ctx) && isApexVariant(ctx) && + if !d.dexpreoptDisabled(ctx, libName) && 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) { +// DexpreoptPrebuiltApexSystemServerJars generates the dexpreopt artifacts from a jar file that has been deapexed from a prebuilt apex +func (d *Dexpreopter) DexpreoptPrebuiltApexSystemServerJars(ctx android.ModuleContext, libraryName string, di *android.DeapexerInfo) { + // A single prebuilt apex can have multiple apex system jars + // initialize the output path for this dex jar + dc := dexpreopt.GetGlobalConfig(ctx) + d.installPath = android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexpreopt.GetSystemServerDexLocation(ctx, dc, libraryName), "/")) + // generate the rules for creating the .odex and .vdex files for this system server jar + dexJarFile := di.PrebuiltExportPath(ApexRootRelativePathToJavaLib(libraryName)) + + d.inputProfilePathOnHost = nil // reset: TODO(spandandas): Make dexpreopter stateless + if android.InList(libraryName, di.GetDexpreoptProfileGuidedExportedModuleNames()) { + // Set the profile path to guide optimization + prof := di.PrebuiltExportPath(ApexRootRelativePathToJavaLib(libraryName) + ".prof") + if prof == nil { + ctx.ModuleErrorf("Could not find a .prof file in this prebuilt apex") + } + d.inputProfilePathOnHost = prof + } + + d.dexpreopt(ctx, libraryName, dexJarFile) +} + +func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, libName string, dexJarFile android.WritablePath) { global := dexpreopt.GetGlobalConfig(ctx) // TODO(b/148690468): The check on d.installPath is to bail out in cases where @@ -270,7 +372,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath) - providesUsesLib := moduleName(ctx) + providesUsesLib := libName if ulib, ok := ctx.Module().(ProvidesUsesLib); ok { name := ulib.ProvidesUsesLib() if name != nil { @@ -280,11 +382,11 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr // If it is test, make config files regardless of its dexpreopt setting. // The config files are required for apps defined in make which depend on the lib. - if d.isTest && d.dexpreoptDisabled(ctx) { + if d.isTest && d.dexpreoptDisabled(ctx, libName) { return } - isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(moduleName(ctx)) + isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(libName) bootImage := defaultBootImageConfig(ctx) // When `global.PreoptWithUpdatableBcp` is true, `bcpForDexpreopt` below includes the mainline @@ -303,7 +405,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr targets = append(targets, target) } } - if isSystemServerJar && moduleName(ctx) != "com.android.location.provider" { + if isSystemServerJar && libName != "com.android.location.provider" { // If the module is a system server jar, only preopt for the primary arch because the jar can // only be loaded by system server. "com.android.location.provider" is a special case because // it's also used by apps as a shared library. @@ -326,30 +428,39 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr var profileClassListing android.OptionalPath var profileBootListing android.OptionalPath profileIsTextListing := false + if d.inputProfilePathOnHost != nil { profileClassListing = android.OptionalPathForPath(d.inputProfilePathOnHost) } else if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) && !forPrebuiltApex(ctx) { - // If dex_preopt.profile_guided is not set, default it based on the existence of the - // dexprepot.profile option or the profile class listing. - if String(d.dexpreoptProperties.Dex_preopt.Profile) != "" { + // If enable_profile_rewriting is set, use the rewritten profile instead of the checked-in profile + if d.EnableProfileRewriting() { + profileClassListing = android.OptionalPathForPath(d.GetRewrittenProfile()) + profileIsTextListing = true + } else if profile := d.GetProfile(); profile != "" { + // If dex_preopt.profile_guided is not set, default it based on the existence of the + // dexprepot.profile option or the profile class listing. profileClassListing = android.OptionalPathForPath( - android.PathForModuleSrc(ctx, String(d.dexpreoptProperties.Dex_preopt.Profile))) + android.PathForModuleSrc(ctx, profile)) profileBootListing = android.ExistentPathForSource(ctx, - ctx.ModuleDir(), String(d.dexpreoptProperties.Dex_preopt.Profile)+"-boot") + ctx.ModuleDir(), profile+"-boot") profileIsTextListing = true } else if global.ProfileDir != "" { profileClassListing = android.ExistentPathForSource(ctx, - global.ProfileDir, moduleName(ctx)+".prof") + global.ProfileDir, libName+".prof") } } d.dexpreoptProperties.Dex_preopt_result.Profile_guided = profileClassListing.Valid() + // A single apex can have multiple system server jars + // Use the dexJar to create a unique scope for each + dexJarStem := strings.TrimSuffix(dexJarFile.Base(), dexJarFile.Ext()) + // Full dexpreopt config, used to create dexpreopt build rules. dexpreoptConfig := &dexpreopt.ModuleConfig{ - Name: moduleName(ctx), + Name: libName, DexLocation: dexLocation, - BuildPath: android.PathForModuleOut(ctx, "dexpreopt", moduleName(ctx)+".jar").OutputPath, + BuildPath: android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, libName+".jar").OutputPath, DexPath: dexJarFile, ManifestPath: android.OptionalPathForPath(d.manifestFile), UncompressedDex: d.uncompressedDex, @@ -373,39 +484,90 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr PreoptBootClassPathDexFiles: dexFiles.Paths(), PreoptBootClassPathDexLocations: dexLocations, - PreoptExtractedApk: false, - NoCreateAppImage: !BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, true), ForceCreateAppImage: BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, false), PresignedPrebuilt: d.isPresignedPrebuilt, } - d.configPath = android.PathForModuleOut(ctx, "dexpreopt", "dexpreopt.config") + d.configPath = android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, "dexpreopt.config") dexpreopt.WriteModuleConfig(ctx, dexpreoptConfig, d.configPath) - if d.dexpreoptDisabled(ctx) { + if d.dexpreoptDisabled(ctx, libName) { return } globalSoong := dexpreopt.GetGlobalSoongConfig(ctx) - dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, dexpreoptConfig) + // The root "product_packages.txt" is generated by `build/make/core/Makefile`. It contains a list + // of all packages that are installed on the device. We use `grep` to filter the list by the app's + // dependencies to create a per-app list, and use `rsync --checksum` to prevent the file's mtime + // from being changed if the contents don't change. This avoids unnecessary dexpreopt reruns. + productPackages := android.PathForModuleInPartitionInstall(ctx, "", "product_packages.txt") + appProductPackages := android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, "product_packages.txt") + appProductPackagesStaging := appProductPackages.ReplaceExtension(ctx, "txt.tmp") + clcNames, _ := dexpreopt.ComputeClassLoaderContextDependencies(dexpreoptConfig.ClassLoaderContexts) + sort.Strings(clcNames) // The order needs to be deterministic. + productPackagesRule := android.NewRuleBuilder(pctx, ctx) + if len(clcNames) > 0 { + productPackagesRule.Command(). + Text("grep -F -x"). + FlagForEachArg("-e ", clcNames). + Input(productPackages). + FlagWithOutput("> ", appProductPackagesStaging). + Text("|| true") + } else { + productPackagesRule.Command(). + Text("rm -f").Output(appProductPackagesStaging). + Text("&&"). + Text("touch").Output(appProductPackagesStaging) + } + productPackagesRule.Command(). + Text("rsync --checksum"). + Input(appProductPackagesStaging). + Output(appProductPackages) + productPackagesRule.Restat().Build("product_packages."+dexJarStem, "dexpreopt product_packages") + + // Prebuilts are active, do not copy the dexpreopt'd source javalib to out/soong/system_server_dexjars + // The javalib from the deapexed prebuilt will be copied to this location. + // TODO (b/331665856): Implement a principled solution for this. + copyApexSystemServerJarDex := !disableSourceApexVariant(ctx) && !ctx.Module().IsHideFromMake() + dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule( + ctx, globalSoong, global, dexpreoptConfig, appProductPackages, copyApexSystemServerJarDex) if err != nil { ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error()) return } - dexpreoptRule.Build("dexpreopt", "dexpreopt") + dexpreoptRule.Build("dexpreopt"+"."+dexJarStem, "dexpreopt") - isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) + // The current ctx might be of a deapexer module created by a prebuilt apex + // Use the path of the dex file to determine the library name + isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(dexJarStem) + dexpreoptPartition := d.installPath.Partition() + // dexpreoptPartition is set to empty for dexpreopts of system APEX and system_other. + // In case of system APEX, however, we can set it to "system" manually. + // TODO(b/346662300): Let dexpreopter generate the installPath for dexpreopt files instead of + // using the dex location to generate the installPath. + if isApexSystemServerJar { + dexpreoptPartition = "system" + } 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), "/") + partition := dexpreoptPartition + if strings.HasPrefix(installDir, partition+"/") { + installDir = strings.TrimPrefix(installDir, partition+"/") + } else { + // If the partition for the installDir is different from the install partition, set the + // partition empty to install the dexpreopt files to the desired partition. + // TODO(b/346439786): Define and use the dexpreopt module type to avoid this mismatch. + partition = "" + } installBase := filepath.Base(install.To) arch := filepath.Base(installDir) - installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir) + installPath := android.PathForModuleInPartitionInstall(ctx, partition, installDir) isProfile := strings.HasSuffix(installBase, ".prof") if isProfile { @@ -423,7 +585,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr // 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), + moduleName: libName, outputPathOnHost: install.From, installDirOnDevice: installPath, installFileOnDevice: installBase, @@ -439,6 +601,37 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr } } +func getModuleInstallPathInfo(ctx android.ModuleContext, fullInstallPath string) (android.InstallPath, string, string) { + installPath := android.PathForModuleInstall(ctx) + installDir, installBase := filepath.Split(strings.TrimPrefix(fullInstallPath, "/")) + + if !strings.HasPrefix(installDir, installPath.Partition()+"/") { + // Return empty filename if the install partition is not for the target image. + return installPath, "", "" + } + relDir, err := filepath.Rel(installPath.Partition(), installDir) + if err != nil { + panic(err) + } + return installPath, relDir, installBase +} + +// RuleBuilder.Install() adds output-to-install copy pairs to a list for Make. To share this +// information with PackagingSpec in soong, call PackageFile for them. +// The install path and the target install partition of the module must be the same. +func packageFile(ctx android.ModuleContext, install android.RuleBuilderInstall) { + installPath, relDir, name := getModuleInstallPathInfo(ctx, install.To) + // Empty name means the install partition is not for the target image. + // For the system image, files for "apex" and "system_other" are skipped here. + // The skipped "apex" files are for testing only, for example, + // "/apex/art_boot_images/javalib/x86/boot.vdex". + // TODO(b/320196894): Files for "system_other" are skipped because soong creates the system + // image only for now. + if name != "" { + ctx.PackageFile(installPath.Join(ctx, relDir), name, install.From) + } +} + func (d *dexpreopter) DexpreoptBuiltInstalledForApex() []dexpreopterInstall { return d.builtInstalledForApex } @@ -454,3 +647,27 @@ func (d *dexpreopter) AndroidMkEntriesForApex() []android.AndroidMkEntries { func (d *dexpreopter) OutputProfilePathOnHost() android.Path { return d.outputProfilePathOnHost } + +func (d *dexpreopter) disableDexpreopt() { + d.shouldDisableDexpreopt = true +} + +func (d *dexpreopter) EnableProfileRewriting() bool { + return proptools.Bool(d.dexpreoptProperties.Dex_preopt.Enable_profile_rewriting) +} + +func (d *dexpreopter) GetProfile() string { + return proptools.String(d.dexpreoptProperties.Dex_preopt.Profile) +} + +func (d *dexpreopter) GetProfileGuided() bool { + return proptools.Bool(d.dexpreoptProperties.Dex_preopt.Profile_guided) +} + +func (d *dexpreopter) GetRewrittenProfile() android.Path { + return d.rewrittenProfile +} + +func (d *dexpreopter) SetRewrittenProfile(p android.Path) { + d.rewrittenProfile = p +} diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index f477f404e..defa82c0c 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -16,12 +16,12 @@ package java import ( "path/filepath" - "sort" "strings" "android/soong/android" "android/soong/dexpreopt" + "github.com/google/blueprint" "github.com/google/blueprint/proptools" ) @@ -224,6 +224,12 @@ var artApexNames = []string{ "com.google.android.art.testing", } +var ( + dexpreoptBootJarDepTag = bootclasspathDependencyTag{name: "dexpreopt-boot-jar"} + dexBootJarsFragmentsKey = android.NewOnceKey("dexBootJarsFragments") + apexContributionsMetadataDepTag = dependencyTag{name: "all_apex_contributions"} +) + func init() { RegisterDexpreoptBootJarsComponents(android.InitRegistrationContext) } @@ -232,8 +238,7 @@ func init() { // // WARNING: All fields in this struct should be initialized in the genBootImageConfigs function. // Failure to do so can lead to data races if there is no synchronization enforced ordering between -// the writer and the reader. Fields which break this rule are marked as deprecated and should be -// removed and replaced with something else, e.g. providers. +// the writer and the reader. type bootImageConfig struct { // If this image is an extension, the image that it extends. extends *bootImageConfig @@ -241,6 +246,9 @@ type bootImageConfig struct { // Image name (used in directory names and ninja rule names). name string + // If the module with the given name exists, this config is enabled. + enabledIfExists string + // Basename of the image: the resulting filenames are <stem>[-<jar>].{art,oat,vdex}. stem string @@ -257,10 +265,6 @@ type bootImageConfig struct { // the location is relative to "/". installDir 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 @@ -274,16 +278,6 @@ type bootImageConfig struct { // File path to a zip archive with all image files (or nil, if not needed). zip android.WritablePath - // Rules which should be used in make to install the outputs. - // - // Deprecated: Not initialized correctly, see struct comment. - profileInstalls android.RuleBuilderInstalls - - // Path to the license metadata file for the module that built the profile. - // - // Deprecated: Not initialized correctly, see struct comment. - profileLicenseMetadataFile android.OptionalPath - // Target-dependent fields. variants []*bootImageVariant @@ -296,10 +290,9 @@ type bootImageConfig struct { // The "--single-image" argument. singleImage bool - // Profiles imported from other boot image configs. Each element must represent a - // `bootclasspath_fragment` of an APEX (i.e., the `name` field of each element must refer to the - // `image_name` property of a `bootclasspath_fragment`). - profileImports []*bootImageConfig + // Profiles imported from APEXes, in addition to the profile at the default path. Each entry must + // be the name of an APEX module. + profileImports []string } // Target-dependent description of a boot image. @@ -384,7 +377,7 @@ func (image bootImageConfig) moduleName(ctx android.PathContext, idx int) string m := image.modules.Jar(idx) name := image.stem if idx != 0 || image.extends != nil { - name += "-" + android.ModuleStem(m) + name += "-" + android.ModuleStem(ctx.Config(), image.modules.Apex(idx), m) } return name } @@ -458,18 +451,26 @@ func (image *bootImageConfig) isProfileGuided() bool { return image.compilerFilter == "speed-profile" } +func (image *bootImageConfig) isEnabled(ctx android.BaseModuleContext) bool { + return ctx.OtherModuleExists(image.enabledIfExists) +} + func dexpreoptBootJarsFactory() android.SingletonModule { m := &dexpreoptBootJars{} - android.InitAndroidModule(m) + android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) return m } func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) { - ctx.RegisterSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory) + ctx.RegisterParallelSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory) + ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) { + ctx.BottomUp("dex_bootjars_deps", DexpreoptBootJarsMutator).Parallel() + }) } func SkipDexpreoptBootJars(ctx android.PathContext) bool { - return dexpreopt.GetGlobalConfig(ctx).DisablePreoptBootImages + global := dexpreopt.GetGlobalConfig(ctx) + return global.DisablePreoptBootImages || !shouldBuildBootImages(ctx.Config(), global) } // Singleton module for generating boot image build rules. @@ -492,37 +493,136 @@ type dexpreoptBootJars struct { dexpreoptConfigForMake android.WritablePath } -// Provide paths to boot images for use by modules that depend upon them. -// -// The build rules are created in GenerateSingletonBuildActions(). -func (d *dexpreoptBootJars) GenerateAndroidBuildActions(ctx android.ModuleContext) { - // Placeholder for now. +func (dbj *dexpreoptBootJars) DepsMutator(ctx android.BottomUpMutatorContext) { + // Create a dependency on all_apex_contributions to determine the selected mainline module + ctx.AddDependency(ctx.Module(), apexContributionsMetadataDepTag, "all_apex_contributions") } -// Generate build rules for boot images. -func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonContext) { - if dexpreopt.GetCachedGlobalSoongConfig(ctx) == nil { - // No module has enabled dexpreopting, so we assume there will be no boot image to make. +func DexpreoptBootJarsMutator(ctx android.BottomUpMutatorContext) { + if _, ok := ctx.Module().(*dexpreoptBootJars); !ok { return } - archType := ctx.Config().Targets[android.Android][0].Arch.ArchType - d.dexpreoptConfigForMake = android.PathForOutput(ctx, toDexpreoptDirName(archType), "dexpreopt.config") - writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake) - global := dexpreopt.GetGlobalConfig(ctx) - if !shouldBuildBootImages(ctx.Config(), global) { - return + if dexpreopt.IsDex2oatNeeded(ctx) { + // Add a dependency onto the dex2oat tool which is needed for creating the boot image. The + // path is retrieved from the dependency by GetGlobalSoongConfig(ctx). + dexpreopt.RegisterToolDeps(ctx) } - defaultImageConfig := defaultBootImageConfig(ctx) - d.defaultBootImage = defaultImageConfig imageConfigs := genBootImageConfigs(ctx) - d.otherImages = make([]*bootImageConfig, 0, len(imageConfigs)-1) for _, config := range imageConfigs { - if config != defaultImageConfig { + if !config.isEnabled(ctx) { + continue + } + // For accessing the boot jars. + addDependenciesOntoBootImageModules(ctx, config.modules, dexpreoptBootJarDepTag) + // Create a dependency on the apex selected using RELEASE_APEX_CONTRIBUTIONS_* + // TODO: b/308174306 - Remove the direct depedendency edge to the java_library (source/prebuilt) once all mainline modules + // have been flagged using RELEASE_APEX_CONTRIBUTIONS_* + apexes := []string{} + for i := 0; i < config.modules.Len(); i++ { + apexes = append(apexes, config.modules.Apex(i)) + } + addDependenciesOntoSelectedBootImageApexes(ctx, android.FirstUniqueStrings(apexes)...) + } + + if ctx.OtherModuleExists("platform-bootclasspath") { + // For accessing all bootclasspath fragments. + addDependencyOntoApexModulePair(ctx, "platform", "platform-bootclasspath", platformBootclasspathDepTag) + } else if ctx.OtherModuleExists("art-bootclasspath-fragment") { + // For accessing the ART bootclasspath fragment on a thin manifest (e.g., master-art) where + // platform-bootclasspath doesn't exist. + addDependencyOntoApexModulePair(ctx, "com.android.art", "art-bootclasspath-fragment", bootclasspathFragmentDepTag) + } +} + +// Create a dependency from dex_bootjars to the specific apexes selected using all_apex_contributions +// This dependency will be used to get the path to the deapexed dex boot jars and profile (via a provider) +func addDependenciesOntoSelectedBootImageApexes(ctx android.BottomUpMutatorContext, apexes ...string) { + psi := android.PrebuiltSelectionInfoMap{} + ctx.VisitDirectDepsWithTag(apexContributionsMetadataDepTag, func(am android.Module) { + if info, exists := android.OtherModuleProvider(ctx, am, android.PrebuiltSelectionInfoProvider); exists { + psi = info + } + }) + for _, apex := range apexes { + for _, selected := range psi.GetSelectedModulesForApiDomain(apex) { + // We need to add a dep on only the apex listed in `contents` of the selected apex_contributions module + // This is not available in a structured format in `apex_contributions`, so this hack adds a dep on all `contents` + // (some modules like art.module.public.api do not have an apex variation since it is a pure stub module that does not get installed) + apexVariationOfSelected := append(ctx.Target().Variations(), blueprint.Variation{Mutator: "apex", Variation: apex}) + if ctx.OtherModuleDependencyVariantExists(apexVariationOfSelected, selected) { + ctx.AddFarVariationDependencies(apexVariationOfSelected, dexpreoptBootJarDepTag, selected) + } + } + } +} + +func gatherBootclasspathFragments(ctx android.ModuleContext) map[string]android.Module { + return ctx.Config().Once(dexBootJarsFragmentsKey, func() interface{} { + fragments := make(map[string]android.Module) + ctx.WalkDeps(func(child, parent android.Module) bool { + if !isActiveModule(ctx, child) { + return false + } + tag := ctx.OtherModuleDependencyTag(child) + if tag == platformBootclasspathDepTag { + return true + } + if tag == bootclasspathFragmentDepTag { + apexInfo, _ := android.OtherModuleProvider(ctx, child, android.ApexInfoProvider) + for _, apex := range apexInfo.InApexVariants { + fragments[apex] = child + } + return false + } + return false + }) + return fragments + }).(map[string]android.Module) +} + +func getBootclasspathFragmentByApex(ctx android.ModuleContext, apexName string) android.Module { + return gatherBootclasspathFragments(ctx)[apexName] +} + +// GenerateAndroidBuildActions generates the build rules for boot images. +func (d *dexpreoptBootJars) GenerateAndroidBuildActions(ctx android.ModuleContext) { + imageConfigs := genBootImageConfigs(ctx) + d.defaultBootImage = defaultBootImageConfig(ctx) + d.otherImages = make([]*bootImageConfig, 0, len(imageConfigs)-1) + var profileInstalls android.RuleBuilderInstalls + for _, name := range getImageNames() { + config := imageConfigs[name] + if config != d.defaultBootImage { d.otherImages = append(d.otherImages, config) } + if !config.isEnabled(ctx) { + continue + } + installs := generateBootImage(ctx, config) + profileInstalls = append(profileInstalls, installs...) + if config == d.defaultBootImage { + _, installs := bootFrameworkProfileRule(ctx, config) + profileInstalls = append(profileInstalls, installs...) + } } + if len(profileInstalls) > 0 { + android.SetProvider(ctx, profileInstallInfoProvider, profileInstallInfo{ + profileInstalls: profileInstalls, + profileLicenseMetadataFile: android.OptionalPathForPath(ctx.LicenseMetadataFile()), + }) + for _, install := range profileInstalls { + packageFile(ctx, install) + } + } +} + +// GenerateSingletonBuildActions generates build rules for the dexpreopt config for Make. +func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonContext) { + d.dexpreoptConfigForMake = + android.PathForOutput(ctx, dexpreopt.GetDexpreoptDirName(ctx), "dexpreopt.config") + writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake) } // shouldBuildBootImages determines whether boot images should be built. @@ -537,6 +637,141 @@ func shouldBuildBootImages(config android.Config, global *dexpreopt.GlobalConfig return true } +func generateBootImage(ctx android.ModuleContext, imageConfig *bootImageConfig) android.RuleBuilderInstalls { + apexJarModulePairs := getModulesForImage(ctx, imageConfig) + + // Copy module dex jars to their predefined locations. + bootDexJarsByModule := extractEncodedDexJarsFromModulesOrBootclasspathFragments(ctx, apexJarModulePairs) + copyBootJarsToPredefinedLocations(ctx, bootDexJarsByModule, imageConfig.dexPathsByModule) + + // Build a profile for the image config from the profile at the default path. The profile will + // then be used along with profiles imported from APEXes to build the boot image. + profile, profileInstalls := bootImageProfileRule(ctx, imageConfig) + + // If dexpreopt of boot image jars should be skipped, stop after generating a profile. + global := dexpreopt.GetGlobalConfig(ctx) + if SkipDexpreoptBootJars(ctx) || (global.OnlyPreoptArtBootImage && imageConfig.name != "art") { + return profileInstalls + } + + // Build boot image files for the android variants. + androidBootImageFiles := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile) + + // Zip the android variant boot image files up. + buildBootImageZipInPredefinedLocation(ctx, imageConfig, androidBootImageFiles.byArch) + + // Build boot image files for the host variants. There are use directly by ART host side tests. + buildBootImageVariantsForBuildOs(ctx, imageConfig, profile) + + // Create a `dump-oat-<image-name>` rule that runs `oatdump` for debugging purposes. + dumpOatRules(ctx, imageConfig) + + return profileInstalls +} + +type apexJarModulePair struct { + apex string + jarModule android.Module +} + +func getModulesForImage(ctx android.ModuleContext, imageConfig *bootImageConfig) []apexJarModulePair { + modules := make([]apexJarModulePair, 0, imageConfig.modules.Len()) + for i := 0; i < imageConfig.modules.Len(); i++ { + found := false + for _, module := range gatherApexModulePairDepsWithTag(ctx, dexpreoptBootJarDepTag) { + name := android.RemoveOptionalPrebuiltPrefix(module.Name()) + if name == imageConfig.modules.Jar(i) { + modules = append(modules, apexJarModulePair{ + apex: imageConfig.modules.Apex(i), + jarModule: module, + }) + found = true + break + } + } + if !found && !ctx.Config().AllowMissingDependencies() { + ctx.ModuleErrorf( + "Boot image '%s' module '%s' not added as a dependency of dex_bootjars", + imageConfig.name, + imageConfig.modules.Jar(i)) + return []apexJarModulePair{} + } + } + return modules +} + +// extractEncodedDexJarsFromModulesOrBootclasspathFragments gets the hidden API encoded dex jars for +// the given modules. +func extractEncodedDexJarsFromModulesOrBootclasspathFragments(ctx android.ModuleContext, apexJarModulePairs []apexJarModulePair) bootDexJarByModule { + apexNameToApexExportInfoMap := getApexNameToApexExportsInfoMap(ctx) + encodedDexJarsByModuleName := bootDexJarByModule{} + for _, pair := range apexJarModulePairs { + dexJarPath := getDexJarForApex(ctx, pair, apexNameToApexExportInfoMap) + encodedDexJarsByModuleName.addPath(pair.jarModule, dexJarPath) + } + return encodedDexJarsByModuleName +} + +type apexNameToApexExportsInfoMap map[string]android.ApexExportsInfo + +// javaLibraryPathOnHost returns the path to the java library which is exported by the apex for hiddenapi and dexpreopt and a boolean indicating whether the java library exists +// For prebuilt apexes, this is created by deapexing the prebuilt apex +func (m *apexNameToApexExportsInfoMap) javaLibraryDexPathOnHost(ctx android.ModuleContext, apex string, javalib string) (android.Path, bool) { + if info, exists := (*m)[apex]; exists { + if dex, exists := info.LibraryNameToDexJarPathOnHost[javalib]; exists { + return dex, true + } else { + ctx.ModuleErrorf("Apex %s does not provide a dex boot jar for library %s\n", apex, javalib) + } + } + // An apex entry could not be found. Return false. + // TODO: b/308174306 - When all the mainline modules have been flagged, make this a hard error + return nil, false +} + +// Returns the stem of an artifact inside a prebuilt apex +func ModuleStemForDeapexing(m android.Module) string { + bmn, _ := m.(interface{ BaseModuleName() string }) + return bmn.BaseModuleName() +} + +// Returns the java libraries exported by the apex for hiddenapi and dexpreopt +// This information can come from two mechanisms +// 1. New: Direct deps to _selected_ apexes. The apexes return a ApexExportsInfo +// 2. Legacy: An edge to java_library or java_import (java_sdk_library) module. For prebuilt apexes, this serves as a hook and is populated by deapexers of prebuilt apxes +// TODO: b/308174306 - Once all mainline modules have been flagged, drop (2) +func getDexJarForApex(ctx android.ModuleContext, pair apexJarModulePair, apexNameToApexExportsInfoMap apexNameToApexExportsInfoMap) android.Path { + if dex, found := apexNameToApexExportsInfoMap.javaLibraryDexPathOnHost(ctx, pair.apex, ModuleStemForDeapexing(pair.jarModule)); found { + return dex + } + // TODO: b/308174306 - Remove the legacy mechanism + if android.IsConfiguredJarForPlatform(pair.apex) || android.IsModulePrebuilt(pair.jarModule) { + // This gives us the dex jar with the hidden API flags encoded from the monolithic hidden API + // files or the dex jar extracted from a prebuilt APEX. We can't use this for a boot jar for + // a source APEX because there is no guarantee that it is the same as the jar packed into the + // APEX. In practice, they are the same when we are building from a full source tree, but they + // are different when we are building from a thin manifest (e.g., master-art), where there is + // no monolithic hidden API files at all. + return retrieveEncodedBootDexJarFromModule(ctx, pair.jarModule) + } else { + // Use exactly the same jar that is packed into the APEX. + fragment := getBootclasspathFragmentByApex(ctx, pair.apex) + if fragment == nil { + ctx.ModuleErrorf("Boot jar '%[1]s' is from APEX '%[2]s', but a bootclasspath_fragment for "+ + "APEX '%[2]s' doesn't exist or is not added as a dependency of dex_bootjars", + pair.jarModule.Name(), + pair.apex) + } + bootclasspathFragmentInfo, _ := android.OtherModuleProvider(ctx, fragment, BootclasspathFragmentApexContentInfoProvider) + jar, err := bootclasspathFragmentInfo.DexBootJarPathForContentModule(pair.jarModule) + if err != nil { + ctx.ModuleErrorf("%s", err) + } + return jar + } + return nil +} + // copyBootJarsToPredefinedLocations generates commands that will copy boot jars to predefined // paths in the global config. func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, srcBootDexJarsByModule bootDexJarByModule, dstBootJarsByModule map[string]android.WritablePath) { @@ -666,6 +901,66 @@ type bootImageVariantOutputs struct { config *bootImageVariant } +// Returns the profile file for an apex +// This information can come from two mechanisms +// 1. New: Direct deps to _selected_ apexes. The apexes return a BootclasspathFragmentApexContentInfo +// 2. Legacy: An edge to bootclasspath_fragment module. For prebuilt apexes, this serves as a hook and is populated by deapexers of prebuilt apxes +// TODO: b/308174306 - Once all mainline modules have been flagged, drop (2) +func getProfilePathForApex(ctx android.ModuleContext, apexName string, apexNameToBcpInfoMap map[string]android.ApexExportsInfo) android.Path { + if info, exists := apexNameToBcpInfoMap[apexName]; exists { + return info.ProfilePathOnHost + } + // TODO: b/308174306 - Remove the legacy mechanism + fragment := getBootclasspathFragmentByApex(ctx, apexName) + if fragment == nil { + ctx.ModuleErrorf("Boot image config imports profile from '%[2]s', but a "+ + "bootclasspath_fragment for APEX '%[2]s' doesn't exist or is not added as a "+ + "dependency of dex_bootjars", + apexName) + return nil + } + return fragment.(commonBootclasspathFragment).getProfilePath() +} + +func getApexNameToApexExportsInfoMap(ctx android.ModuleContext) apexNameToApexExportsInfoMap { + apexNameToApexExportsInfoMap := apexNameToApexExportsInfoMap{} + ctx.VisitDirectDepsWithTag(dexpreoptBootJarDepTag, func(am android.Module) { + if info, exists := android.OtherModuleProvider(ctx, am, android.ApexExportsInfoProvider); exists { + apexNameToApexExportsInfoMap[info.ApexName] = info + } + }) + return apexNameToApexExportsInfoMap +} + +func packageFileForTargetImage(ctx android.ModuleContext, image *bootImageVariant) { + if image.target.Os != ctx.Os() { + // This is not for the target device. + return + } + + for _, install := range image.installs { + packageFile(ctx, install) + } + + for _, install := range image.vdexInstalls { + if image.target.Arch.ArchType.Name != ctx.DeviceConfig().DeviceArch() { + // Note that the vdex files are identical between architectures. If the target image is + // not for the primary architecture create symlinks to share the vdex of the primary + // architecture with the other architectures. + // + // Assuming that the install path has the architecture name with it, replace the + // architecture name with the primary architecture name to find the source vdex file. + installPath, relDir, name := getModuleInstallPathInfo(ctx, install.To) + if name != "" { + srcRelDir := strings.Replace(relDir, image.target.Arch.ArchType.Name, ctx.DeviceConfig().DeviceArch(), 1) + ctx.InstallSymlink(installPath.Join(ctx, relDir), name, installPath.Join(ctx, srcRelDir, name)) + } + } else { + packageFile(ctx, install) + } + } +} + // Generate boot image build rules for a specific target. func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) bootImageVariantOutputs { @@ -687,10 +982,12 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p rule.Command().Text("rm").Flag("-f"). Flag(symbolsDir.Join(ctx, "*.art").String()). Flag(symbolsDir.Join(ctx, "*.oat").String()). + Flag(symbolsDir.Join(ctx, "*.vdex").String()). Flag(symbolsDir.Join(ctx, "*.invocation").String()) rule.Command().Text("rm").Flag("-f"). Flag(outputDir.Join(ctx, "*.art").String()). Flag(outputDir.Join(ctx, "*.oat").String()). + Flag(outputDir.Join(ctx, "*.vdex").String()). Flag(outputDir.Join(ctx, "*.invocation").String()) cmd := rule.Command() @@ -706,42 +1003,30 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p invocationPath := outputPath.ReplaceExtension(ctx, "invocation") + apexNameToApexExportsInfoMap := getApexNameToApexExportsInfoMap(ctx) + cmd.Tool(globalSoong.Dex2oat). Flag("--avoid-storing-invocation"). FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath). Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatImageXms). Flag("--runtime-arg").FlagWithArg("-Xmx", global.Dex2oatImageXmx) - if profile != nil { - cmd.FlagWithInput("--profile-file=", profile) - } - - fragments := make(map[string]commonBootclasspathFragment) - ctx.VisitDirectDepsWithTag(bootclasspathFragmentDepTag, func(child android.Module) { - fragment := child.(commonBootclasspathFragment) - if fragment.getImageName() != nil && android.IsModulePreferred(child) { - fragments[*fragment.getImageName()] = fragment + if image.isProfileGuided() && !global.DisableGenerateProfile { + if profile != nil { + cmd.FlagWithInput("--profile-file=", profile) } - }) - for _, profileImport := range image.profileImports { - fragment := fragments[profileImport.name] - if fragment == nil { - ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but a "+ - "bootclasspath_fragment with image name '%[2]s' doesn't exist or is not added as a "+ - "dependency of '%[1]s'", - image.name, - profileImport.name) - return bootImageVariantOutputs{} - } - if fragment.getProfilePath() == nil { - ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but '%[2]s' "+ - "doesn't provide a profile", - image.name, - profileImport.name) - return bootImageVariantOutputs{} + for _, apex := range image.profileImports { + importedProfile := getProfilePathForApex(ctx, apex, apexNameToApexExportsInfoMap) + if importedProfile == nil { + ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but '%[2]s' "+ + "doesn't provide a profile", + image.name, + apex) + return bootImageVariantOutputs{} + } + cmd.FlagWithInput("--profile-file=", importedProfile) } - cmd.FlagWithInput("--profile-file=", fragment.getProfilePath()) } dirtyImageFile := "frameworks/base/config/dirty-image-objects" @@ -789,7 +1074,6 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p Flag("--generate-build-id"). Flag("--image-format=lz4hc"). FlagWithArg("--oat-symbols=", symbolsFile.String()). - Flag("--strip"). FlagWithArg("--oat-file=", outputPath.String()). FlagWithArg("--oat-location=", oatLocation). FlagWithArg("--image=", imagePath.String()). @@ -799,6 +1083,11 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p Flag("--force-determinism"). Flag("--abort-on-hard-verifier-error") + // We don't strip on host to make perf tools work. + if image.target.Os == android.Android { + cmd.Flag("--strip") + } + // If the image is profile-guided but the profile is disabled, we omit "--compiler-filter" to // leave the decision to dex2oat to pick the compiler filter. if !(image.isProfileGuided() && global.DisableGenerateProfile) { @@ -816,8 +1105,8 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p cmd.FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch]) } - if global.EnableUffdGc { - cmd.Flag("--runtime-arg").Flag("-Xgc:CMC") + if image.target.Os == android.Android { + cmd.Text("$(cat").Input(globalSoong.UffdGcFlag).Text(")") } if global.BootFlags != "" { @@ -866,9 +1155,10 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p image.installs = rule.Installs() image.vdexInstalls = vdexInstalls image.unstrippedInstalls = unstrippedInstalls + packageFileForTargetImage(ctx, image) // Only set the licenseMetadataFile from the active module. - if isActiveModule(ctx.Module()) { + if isActiveModule(ctx, ctx.Module()) { image.licenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile()) } @@ -881,11 +1171,7 @@ const failureMessage = `ERROR: Dex2oat failed to compile a boot image. It is likely that the boot classpath is inconsistent. Rebuild with ART_BOOT_IMAGE_EXTRA_ARGS="--runtime-arg -verbose:verifier" to see verification errors.` -func bootImageProfileRule(ctx android.ModuleContext, image *bootImageConfig) android.WritablePath { - if !image.isProfileGuided() { - return nil - } - +func bootImageProfileRuleCommon(ctx android.ModuleContext, name string, dexFiles android.Paths, dexLocations []string) android.WritablePath { globalSoong := dexpreopt.GetGlobalSoongConfig(ctx) global := dexpreopt.GetGlobalConfig(ctx) @@ -912,39 +1198,58 @@ func bootImageProfileRule(ctx android.ModuleContext, image *bootImageConfig) and if path := android.ExistentPathForSource(ctx, extraProfile); path.Valid() { profiles = append(profiles, path.Path()) } - bootImageProfile := image.dir.Join(ctx, "boot-image-profile.txt") + bootImageProfile := android.PathForModuleOut(ctx, name, "boot-image-profile.txt") rule.Command().Text("cat").Inputs(profiles).Text(">").Output(bootImageProfile) - profile := image.dir.Join(ctx, "boot.prof") + profile := android.PathForModuleOut(ctx, name, "boot.prof") rule.Command(). Text(`ANDROID_LOG_TAGS="*:e"`). Tool(globalSoong.Profman). Flag("--output-profile-type=boot"). FlagWithInput("--create-profile-from=", bootImageProfile). - FlagForEachInput("--apk=", image.dexPathsDeps.Paths()). - FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps). + FlagForEachInput("--apk=", dexFiles). + FlagForEachArg("--dex-location=", dexLocations). FlagWithOutput("--reference-profile-file=", profile) - if image == defaultBootImageConfig(ctx) { - rule.Install(profile, "/system/etc/boot-image.prof") - image.profileInstalls = append(image.profileInstalls, rule.Installs()...) - image.profileLicenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile()) + rule.Build("bootJarsProfile_"+name, "profile boot jars "+name) + + return profile +} + +type profileInstallInfo 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 +} + +var profileInstallInfoProvider = blueprint.NewProvider[profileInstallInfo]() + +func bootImageProfileRule(ctx android.ModuleContext, image *bootImageConfig) (android.WritablePath, android.RuleBuilderInstalls) { + if !image.isProfileGuided() { + return nil, nil } - rule.Build("bootJarsProfile", "profile boot jars") + profile := bootImageProfileRuleCommon(ctx, image.name, image.dexPathsDeps.Paths(), image.getAnyAndroidVariant().dexLocationsDeps) - return profile + if image == defaultBootImageConfig(ctx) { + rule := android.NewRuleBuilder(pctx, ctx) + rule.Install(profile, "/system/etc/boot-image.prof") + return profile, rule.Installs() + } + return profile, nil } // bootFrameworkProfileRule generates the rule to create the boot framework profile and // returns a path to the generated file. -func bootFrameworkProfileRule(ctx android.ModuleContext, image *bootImageConfig) android.WritablePath { +func bootFrameworkProfileRule(ctx android.ModuleContext, image *bootImageConfig) (android.WritablePath, android.RuleBuilderInstalls) { globalSoong := dexpreopt.GetGlobalSoongConfig(ctx) global := dexpreopt.GetGlobalConfig(ctx) if global.DisableGenerateProfile || ctx.Config().UnbundledBuild() { - return nil + return nil, nil } defaultProfile := "frameworks/base/config/boot-profile.txt" @@ -964,14 +1269,13 @@ 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 + return profile, rule.Installs() } func dumpOatRules(ctx android.ModuleContext, image *bootImageConfig) { var allPhonies android.Paths + name := image.name + globalSoong := dexpreopt.GetGlobalSoongConfig(ctx) for _, image := range image.variants { arch := image.target.Arch.ArchType suffix := arch.String() @@ -980,36 +1284,40 @@ func dumpOatRules(ctx android.ModuleContext, image *bootImageConfig) { suffix = "host-" + suffix } // Create a rule to call oatdump. - output := android.PathForOutput(ctx, "boot."+suffix+".oatdump.txt") + output := android.PathForOutput(ctx, name+"."+suffix+".oatdump.txt") rule := android.NewRuleBuilder(pctx, ctx) imageLocationsOnHost, _ := image.imageLocations() - rule.Command(). + + cmd := rule.Command(). BuiltTool("oatdump"). FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPathsDeps.Paths(), ":"). FlagWithList("--runtime-arg -Xbootclasspath-locations:", image.dexLocationsDeps, ":"). FlagWithArg("--image=", strings.Join(imageLocationsOnHost, ":")).Implicits(image.imagesDeps.Paths()). FlagWithOutput("--output=", output). FlagWithArg("--instruction-set=", arch.String()) - rule.Build("dump-oat-boot-"+suffix, "dump oat boot "+arch.String()) + if image.target.Os == android.Android { + cmd.Text("$(cat").Input(globalSoong.UffdGcFlag).Text(")") + } + rule.Build("dump-oat-"+name+"-"+suffix, "dump oat "+name+" "+arch.String()) // Create a phony rule that depends on the output file and prints the path. - phony := android.PathForPhony(ctx, "dump-oat-boot-"+suffix) + phony := android.PathForPhony(ctx, "dump-oat-"+name+"-"+suffix) rule = android.NewRuleBuilder(pctx, ctx) rule.Command(). Implicit(output). ImplicitOutput(phony). Text("echo").FlagWithArg("Output in ", output.String()) - rule.Build("phony-dump-oat-boot-"+suffix, "dump oat boot "+arch.String()) + rule.Build("phony-dump-oat-"+name+"-"+suffix, "dump oat "+name+" "+arch.String()) allPhonies = append(allPhonies, phony) } - phony := android.PathForPhony(ctx, "dump-oat-boot") + phony := android.PathForPhony(ctx, "dump-oat-"+name) ctx.Build(pctx, android.BuildParams{ Rule: android.Phony, Output: phony, Inputs: allPhonies, - Description: "dump-oat-boot", + Description: "dump-oat-" + name, }) } @@ -1030,9 +1338,11 @@ 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()) + if profileInstallInfo, ok := android.SingletonModuleProvider(ctx, d, profileInstallInfoProvider); ok { + ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", profileInstallInfo.profileInstalls.String()) + if profileInstallInfo.profileLicenseMetadataFile.Valid() { + ctx.Strict("DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA", profileInstallInfo.profileLicenseMetadataFile.String()) + } } if SkipDexpreoptBootJars(ctx) { @@ -1044,11 +1354,9 @@ func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) { ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(dexPaths.Strings(), " ")) ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(dexLocations, " ")) - var imageNames []string // The primary ART boot image is exposed to Make for testing (gtests) and benchmarking // (golem) purposes. for _, current := range append(d.otherImages, image) { - imageNames = append(imageNames, current.name) for _, variant := range current.variants { suffix := "" if variant.target.Os.Class == android.Host { @@ -1069,8 +1377,6 @@ func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) { ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICE"+current.name, strings.Join(imageLocationsOnDevice, ":")) ctx.Strict("DEXPREOPT_IMAGE_ZIP_"+current.name, current.zip.String()) } - // Ensure determinism. - sort.Strings(imageNames) - ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(imageNames, " ")) + ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(getImageNames(), " ")) } } diff --git a/java/dexpreopt_check.go b/java/dexpreopt_check.go index 83c088cd4..33be60352 100644 --- a/java/dexpreopt_check.go +++ b/java/dexpreopt_check.go @@ -28,7 +28,7 @@ func init() { } func RegisterDexpreoptCheckBuildComponents(ctx android.RegistrationContext) { - ctx.RegisterSingletonModuleType("dexpreopt_systemserver_check", dexpreoptSystemserverCheckFactory) + ctx.RegisterParallelSingletonModuleType("dexpreopt_systemserver_check", dexpreoptSystemserverCheckFactory) } // A build-time check to verify if all compilation artifacts of system server jars are installed @@ -68,7 +68,7 @@ func (m *dexpreoptSystemserverCheck) GenerateAndroidBuildActions(ctx android.Mod // 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() { + if global.DisablePreopt || global.OnlyPreoptArtBootImage || len(targets) == 0 || ctx.Config().UnbundledBuild() { return } diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go index 9100e87c6..dc0973cdf 100644 --- a/java/dexpreopt_config.go +++ b/java/dexpreopt_config.go @@ -40,57 +40,67 @@ func dexpreoptTargets(ctx android.PathContext) []android.Target { } var ( - bootImageConfigKey = android.NewOnceKey("bootImageConfig") - bootImageConfigRawKey = android.NewOnceKey("bootImageConfigRaw") - artBootImageName = "art" - frameworkBootImageName = "boot" - mainlineBootImageName = "mainline" - bootImageStem = "boot" + bootImageConfigKey = android.NewOnceKey("bootImageConfig") + bootImageConfigRawKey = android.NewOnceKey("bootImageConfigRaw") + frameworkBootImageName = "boot" + mainlineBootImageName = "mainline" + bootImageStem = "boot" + ProfileInstallPathInApex = "etc/boot-image.prof" ) +// getImageNames returns an ordered list of image names. The order doesn't matter but needs to be +// deterministic. The names listed here must match the map keys returned by genBootImageConfigs. +func getImageNames() []string { + return []string{"art", "boot", "mainline"} +} + func genBootImageConfigRaw(ctx android.PathContext) map[string]*bootImageConfig { return ctx.Config().Once(bootImageConfigRawKey, func() interface{} { global := dexpreopt.GetGlobalConfig(ctx) - artModules := global.ArtApexJars - frameworkModules := global.BootJars // This includes `artModules`. + artBootImageName := "art" // Keep this local to avoid accidental references. + frameworkModules := global.BootJars // This includes `global.ArtApexJars`. mainlineBcpModules := global.ApexBootJars frameworkSubdir := "system/framework" - // ART config for the primary boot image in the ART apex. - // It includes the Core Libraries. + profileImports := []string{"com.android.art"} + + // ART boot image for testing only. Do not rely on it to make any build-time decision. artCfg := bootImageConfig{ - name: artBootImageName, - stem: bootImageStem, - installDir: "apex/art_boot_images/javalib", - profileInstallPathInApex: "etc/boot-image.prof", - modules: artModules, - preloadedClassesFile: "art/build/boot/preloaded-classes", - compilerFilter: "speed-profile", - singleImage: false, + name: artBootImageName, + enabledIfExists: "art-bootclasspath-fragment", + stem: bootImageStem, + installDir: "apex/art_boot_images/javalib", + modules: global.TestOnlyArtBootImageJars, + preloadedClassesFile: "art/build/boot/preloaded-classes", + compilerFilter: "speed-profile", + singleImage: false, + profileImports: profileImports, } // Framework config for the boot image extension. // It includes framework libraries and depends on the ART config. frameworkCfg := bootImageConfig{ name: frameworkBootImageName, + enabledIfExists: "platform-bootclasspath", stem: bootImageStem, installDir: frameworkSubdir, modules: frameworkModules, preloadedClassesFile: "frameworks/base/config/preloaded-classes", compilerFilter: "speed-profile", singleImage: false, - profileImports: []*bootImageConfig{&artCfg}, + profileImports: profileImports, } mainlineCfg := bootImageConfig{ - extends: &frameworkCfg, - name: mainlineBootImageName, - stem: bootImageStem, - installDir: frameworkSubdir, - modules: mainlineBcpModules, - compilerFilter: "verify", - singleImage: true, + extends: &frameworkCfg, + name: mainlineBootImageName, + enabledIfExists: "platform-bootclasspath", + stem: bootImageStem, + installDir: frameworkSubdir, + modules: mainlineBcpModules, + compilerFilter: "verify", + singleImage: true, } return map[string]*bootImageConfig{ @@ -105,8 +115,7 @@ func genBootImageConfigRaw(ctx android.PathContext) map[string]*bootImageConfig func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig { return ctx.Config().Once(bootImageConfigKey, func() interface{} { targets := dexpreoptTargets(ctx) - archType := ctx.Config().Targets[android.Android][0].Arch.ArchType - deviceDir := android.PathForOutput(ctx, toDexpreoptDirName(archType)) + deviceDir := android.PathForOutput(ctx, dexpreopt.GetDexpreoptDirName(ctx)) configs := genBootImageConfigRaw(ctx) @@ -181,10 +190,6 @@ func calculateDepsRecursive(c *bootImageConfig, targets []android.Target, visite } } -func artBootImageConfig(ctx android.PathContext) *bootImageConfig { - return genBootImageConfigs(ctx)[artBootImageName] -} - func defaultBootImageConfig(ctx android.PathContext) *bootImageConfig { return genBootImageConfigs(ctx)[frameworkBootImageName] } @@ -193,65 +198,35 @@ func mainlineBootImageConfig(ctx android.PathContext) *bootImageConfig { return genBootImageConfigs(ctx)[mainlineBootImageName] } -// Apex boot config allows to access build/install paths of apex boot jars without going -// through the usual trouble of registering dependencies on those modules and extracting build paths -// from those dependencies. -type apexBootConfig struct { - // A list of apex boot jars. - modules android.ConfiguredJarList - - // A list of predefined build paths to apex boot jars. They are configured very early, - // before the modules for these jars are processed and the actual paths are generated, and - // later on a singleton adds commands to copy actual jars to the predefined paths. - dexPaths android.WritablePaths - - // Map from module name (without prebuilt_ prefix) to the predefined build path. - dexPathsByModule map[string]android.WritablePath - - // A list of dex locations (a.k.a. on-device paths) to the boot jars. - dexLocations []string -} - -var updatableBootConfigKey = android.NewOnceKey("apexBootConfig") - -// Returns apex boot config. -func GetApexBootConfig(ctx android.PathContext) apexBootConfig { - return ctx.Config().Once(updatableBootConfigKey, func() interface{} { - apexBootJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars - archType := ctx.Config().Targets[android.Android][0].Arch.ArchType - dir := android.PathForOutput(ctx, toDexpreoptDirName(archType), "apex_bootjars") - dexPaths := apexBootJars.BuildPaths(ctx, dir) - dexPathsByModuleName := apexBootJars.BuildPathsByModule(ctx, dir) - - dexLocations := apexBootJars.DevicePaths(ctx.Config(), android.Android) - - return apexBootConfig{apexBootJars, dexPaths, dexPathsByModuleName, dexLocations} - }).(apexBootConfig) +// isProfileProviderApex returns true if this apex provides a boot image profile. +func isProfileProviderApex(ctx android.PathContext, apexName string) bool { + for _, config := range genBootImageConfigs(ctx) { + for _, profileImport := range config.profileImports { + if profileImport == apexName { + return true + } + } + } + return false } // Returns a list of paths and a list of locations for the boot jars used in dexpreopt (to be // passed in -Xbootclasspath and -Xbootclasspath-locations arguments for dex2oat). func bcpForDexpreopt(ctx android.PathContext, withUpdatable bool) (android.WritablePaths, []string) { - // Non-updatable boot jars (they are used both in the boot image and in dexpreopt). bootImage := defaultBootImageConfig(ctx) + if withUpdatable { + bootImage = mainlineBootImageConfig(ctx) + } + dexPaths := bootImage.dexPathsDeps // The dex locations for all Android variants are identical. dexLocations := bootImage.getAnyAndroidVariant().dexLocationsDeps - if withUpdatable { - // Apex boot jars (they are used only in dexpreopt, but not in the boot image). - apexBootConfig := GetApexBootConfig(ctx) - dexPaths = append(dexPaths, apexBootConfig.dexPaths...) - dexLocations = append(dexLocations, apexBootConfig.dexLocations...) - } - return dexPaths, dexLocations } var defaultBootclasspathKey = android.NewOnceKey("defaultBootclasspath") -var copyOf = android.CopyOf - func init() { android.RegisterMakeVarsProvider(pctx, dexpreoptConfigMakevars) } @@ -259,7 +234,3 @@ func init() { func dexpreoptConfigMakevars(ctx android.MakeVarsContext) { ctx.Strict("DEXPREOPT_BOOT_JARS_MODULES", strings.Join(defaultBootImageConfig(ctx).modules.CopyOfApexJarPairs(), ":")) } - -func toDexpreoptDirName(arch android.ArchType) string { - return "dexpreopt_" + arch.String() -} diff --git a/java/dexpreopt_config_test.go b/java/dexpreopt_config_test.go index cd7f295c8..44d2127fd 100644 --- a/java/dexpreopt_config_test.go +++ b/java/dexpreopt_config_test.go @@ -16,6 +16,7 @@ package java import ( "runtime" + "sort" "testing" "android/soong/android" @@ -35,3 +36,22 @@ func TestBootImageConfig(t *testing.T) { CheckFrameworkBootImageConfig(t, result) CheckMainlineBootImageConfig(t, result) } + +func TestImageNames(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForBootImageConfigTest, + ).RunTest(t) + + names := getImageNames() + sort.Strings(names) + + ctx := &android.TestPathContext{TestResult: result} + configs := genBootImageConfigs(ctx) + namesFromConfigs := make([]string, 0, len(configs)) + for name, _ := range configs { + namesFromConfigs = append(namesFromConfigs, name) + } + sort.Strings(namesFromConfigs) + + android.AssertArrayString(t, "getImageNames vs genBootImageConfigs", names, namesFromConfigs) +} diff --git a/java/dexpreopt_config_testing.go b/java/dexpreopt_config_testing.go index 6f3aa2be8..104829f5f 100644 --- a/java/dexpreopt_config_testing.go +++ b/java/dexpreopt_config_testing.go @@ -29,6 +29,7 @@ import ( "testing" "android/soong/android" + "android/soong/dexpreopt" ) // PrepareForBootImageConfigTest is the minimal set of preparers that are needed to be able to use @@ -36,7 +37,17 @@ import ( var PrepareForBootImageConfigTest = android.GroupFixturePreparers( android.PrepareForTestWithArchMutator, android.PrepareForTestAccessingMakeVars, + PrepareForTestWithDexpreopt, FixtureConfigureBootJars("com.android.art:core1", "com.android.art:core2", "platform:framework"), + dexpreopt.FixtureSetTestOnlyArtBootImageJars("com.android.art:core1", "com.android.art:core2", "platform:extra1"), + android.FixtureAddTextFile("extra1/Android.bp", ` + java_library { + name: "extra1", + srcs: ["extra1.java"], + installable: true, + } + `), + android.FixtureAddFile("extra1/extra1.java", nil), ) var PrepareApexBootJarConfigs = FixtureConfigureApexBootJars( @@ -44,18 +55,18 @@ var PrepareApexBootJarConfigs = FixtureConfigureApexBootJars( var PrepareApexBootJarConfigsAndModules = android.GroupFixturePreparers( PrepareApexBootJarConfigs, - prepareApexBootJarModule("com.android.foo", "framework-foo"), - prepareApexBootJarModule("com.android.bar", "framework-bar"), + PrepareApexBootJarModule("com.android.foo", "framework-foo"), + PrepareApexBootJarModule("com.android.bar", "framework-bar"), ) var ApexBootJarFragmentsForPlatformBootclasspath = fmt.Sprintf(` { apex: "%[1]s", - module: "%[1]s-bootclasspathfragment", + module: "%[1]s-bootclasspath-fragment", }, { apex: "%[2]s", - module: "%[2]s-bootclasspathfragment", + module: "%[2]s-bootclasspath-fragment", }, `, "com.android.foo", "com.android.bar") @@ -64,15 +75,22 @@ var ApexBootJarDexJarPaths = []string{ "out/soong/.intermediates/packages/modules/com.android.foo/framework-foo/android_common_apex10000/aligned/framework-foo.jar", } -func prepareApexBootJarModule(apexName string, moduleName string) android.FixturePreparer { +func PrepareApexBootJarModule(apexName string, moduleName string) android.FixturePreparer { moduleSourceDir := fmt.Sprintf("packages/modules/%s", apexName) + fragmentName := apexName + "-bootclasspath-fragment" + imageNameProp := "" + if apexName == "com.android.art" { + fragmentName = "art-bootclasspath-fragment" + imageNameProp = `image_name: "art",` + } + return android.GroupFixturePreparers( android.FixtureAddTextFile(moduleSourceDir+"/Android.bp", fmt.Sprintf(` apex { name: "%[1]s", key: "%[1]s.key", bootclasspath_fragments: [ - "%[1]s-bootclasspathfragment", + "%[3]s", ], updatable: false, } @@ -84,7 +102,8 @@ func prepareApexBootJarModule(apexName string, moduleName string) android.Fixtur } bootclasspath_fragment { - name: "%[1]s-bootclasspathfragment", + name: "%[3]s", + %[4]s contents: ["%[2]s"], apex_available: ["%[1]s"], hidden_api: { @@ -100,7 +119,7 @@ func prepareApexBootJarModule(apexName string, moduleName string) android.Fixtur compile_dex: true, apex_available: ["%[1]s"], } - `, apexName, moduleName)), + `, apexName, moduleName, fragmentName, imageNameProp)), android.FixtureMergeMockFs(android.MockFS{ fmt.Sprintf("%s/apex_manifest.json", moduleSourceDir): nil, fmt.Sprintf("%s/%s.avbpubkey", moduleSourceDir, apexName): nil, @@ -192,7 +211,7 @@ func CheckArtBootImageConfig(t *testing.T, result *android.TestResult) { // getArtImageConfig gets the ART bootImageConfig that was created during the test. func getArtImageConfig(result *android.TestResult) *bootImageConfig { pathCtx := &android.TestPathContext{TestResult: result} - imageConfig := artBootImageConfig(pathCtx) + imageConfig := genBootImageConfigs(pathCtx)["art"] return imageConfig } @@ -210,15 +229,15 @@ func checkArtBootImageConfig(t *testing.T, result *android.TestResult, mutated b symbolsDir: "out/soong/dexpreopt_arm64/dex_artjars_unstripped", installDir: "apex/art_boot_images/javalib", profileInstallPathInApex: "etc/boot-image.prof", - modules: android.CreateTestConfiguredJarList([]string{"com.android.art:core1", "com.android.art:core2"}), - dexPaths: []string{"out/soong/dexpreopt_arm64/dex_artjars_input/core1.jar", "out/soong/dexpreopt_arm64/dex_artjars_input/core2.jar"}, - dexPathsDeps: []string{"out/soong/dexpreopt_arm64/dex_artjars_input/core1.jar", "out/soong/dexpreopt_arm64/dex_artjars_input/core2.jar"}, + modules: android.CreateTestConfiguredJarList([]string{"com.android.art:core1", "com.android.art:core2", "platform:extra1"}), + dexPaths: []string{"out/soong/dexpreopt_arm64/dex_artjars_input/core1.jar", "out/soong/dexpreopt_arm64/dex_artjars_input/core2.jar", "out/soong/dexpreopt_arm64/dex_artjars_input/extra1.jar"}, + dexPathsDeps: []string{"out/soong/dexpreopt_arm64/dex_artjars_input/core1.jar", "out/soong/dexpreopt_arm64/dex_artjars_input/core2.jar", "out/soong/dexpreopt_arm64/dex_artjars_input/extra1.jar"}, zip: "out/soong/dexpreopt_arm64/dex_artjars/art.zip", variants: []*expectedVariant{ { archType: android.Arm64, - dexLocations: []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"}, - dexLocationsDeps: []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"}, + dexLocations: []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar", "/system/framework/extra1.jar"}, + dexLocationsDeps: []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar", "/system/framework/extra1.jar"}, imagePathOnHost: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art", imagePathOnDevice: "/apex/art_boot_images/javalib/arm64/boot.art", imagesDeps: []string{ @@ -228,6 +247,9 @@ func checkArtBootImageConfig(t *testing.T, result *android.TestResult, mutated b "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art", "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat", "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex", + "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.art", + "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.oat", + "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.vdex", }, installs: []normalizedInstall{ { @@ -246,6 +268,14 @@ func checkArtBootImageConfig(t *testing.T, result *android.TestResult, mutated b from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat", to: "/apex/art_boot_images/javalib/arm64/boot-core2.oat", }, + { + from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.art", + to: "/apex/art_boot_images/javalib/arm64/boot-extra1.art", + }, + { + from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.oat", + to: "/apex/art_boot_images/javalib/arm64/boot-extra1.oat", + }, }, vdexInstalls: []normalizedInstall{ { @@ -256,6 +286,10 @@ func checkArtBootImageConfig(t *testing.T, result *android.TestResult, mutated b from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex", to: "/apex/art_boot_images/javalib/arm64/boot-core2.vdex", }, + { + from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.vdex", + to: "/apex/art_boot_images/javalib/arm64/boot-extra1.vdex", + }, }, unstrippedInstalls: []normalizedInstall{ { @@ -266,13 +300,17 @@ func checkArtBootImageConfig(t *testing.T, result *android.TestResult, mutated b from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-core2.oat", to: "/apex/art_boot_images/javalib/arm64/boot-core2.oat", }, + { + from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-extra1.oat", + to: "/apex/art_boot_images/javalib/arm64/boot-extra1.oat", + }, }, licenseMetadataFile: expectedLicenseMetadataFile, }, { archType: android.Arm, - dexLocations: []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"}, - dexLocationsDeps: []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"}, + dexLocations: []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar", "/system/framework/extra1.jar"}, + dexLocationsDeps: []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar", "/system/framework/extra1.jar"}, imagePathOnHost: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art", imagePathOnDevice: "/apex/art_boot_images/javalib/arm/boot.art", imagesDeps: []string{ @@ -282,6 +320,9 @@ func checkArtBootImageConfig(t *testing.T, result *android.TestResult, mutated b "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art", "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat", "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex", + "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.art", + "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.oat", + "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.vdex", }, installs: []normalizedInstall{ { @@ -300,6 +341,14 @@ func checkArtBootImageConfig(t *testing.T, result *android.TestResult, mutated b from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat", to: "/apex/art_boot_images/javalib/arm/boot-core2.oat", }, + { + from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.art", + to: "/apex/art_boot_images/javalib/arm/boot-extra1.art", + }, + { + from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.oat", + to: "/apex/art_boot_images/javalib/arm/boot-extra1.oat", + }, }, vdexInstalls: []normalizedInstall{ { @@ -310,6 +359,10 @@ func checkArtBootImageConfig(t *testing.T, result *android.TestResult, mutated b from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex", to: "/apex/art_boot_images/javalib/arm/boot-core2.vdex", }, + { + from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.vdex", + to: "/apex/art_boot_images/javalib/arm/boot-extra1.vdex", + }, }, unstrippedInstalls: []normalizedInstall{ { @@ -320,13 +373,17 @@ func checkArtBootImageConfig(t *testing.T, result *android.TestResult, mutated b from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-core2.oat", to: "/apex/art_boot_images/javalib/arm/boot-core2.oat", }, + { + from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-extra1.oat", + to: "/apex/art_boot_images/javalib/arm/boot-extra1.oat", + }, }, licenseMetadataFile: expectedLicenseMetadataFile, }, { archType: android.X86_64, - dexLocations: []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"}, - dexLocationsDeps: []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"}, + dexLocations: []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar", "host/linux-x86/system/framework/extra1.jar"}, + dexLocationsDeps: []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar", "host/linux-x86/system/framework/extra1.jar"}, imagePathOnHost: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art", imagePathOnDevice: "/apex/art_boot_images/javalib/x86_64/boot.art", imagesDeps: []string{ @@ -336,6 +393,9 @@ func checkArtBootImageConfig(t *testing.T, result *android.TestResult, mutated b "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art", "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat", "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex", + "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.art", + "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.oat", + "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.vdex", }, installs: []normalizedInstall{ { @@ -352,6 +412,13 @@ func checkArtBootImageConfig(t *testing.T, result *android.TestResult, mutated b from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat", to: "/apex/art_boot_images/javalib/x86_64/boot-core2.oat", }, + { + from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.art", + to: "/apex/art_boot_images/javalib/x86_64/boot-extra1.art", + }, { + from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.oat", + to: "/apex/art_boot_images/javalib/x86_64/boot-extra1.oat", + }, }, vdexInstalls: []normalizedInstall{ { @@ -362,6 +429,10 @@ func checkArtBootImageConfig(t *testing.T, result *android.TestResult, mutated b from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex", to: "/apex/art_boot_images/javalib/x86_64/boot-core2.vdex", }, + { + from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.vdex", + to: "/apex/art_boot_images/javalib/x86_64/boot-extra1.vdex", + }, }, unstrippedInstalls: []normalizedInstall{ { @@ -372,13 +443,17 @@ func checkArtBootImageConfig(t *testing.T, result *android.TestResult, mutated b from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat", to: "/apex/art_boot_images/javalib/x86_64/boot-core2.oat", }, + { + from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.oat", + to: "/apex/art_boot_images/javalib/x86_64/boot-extra1.oat", + }, }, licenseMetadataFile: expectedLicenseMetadataFile, }, { archType: android.X86, - dexLocations: []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"}, - dexLocationsDeps: []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"}, + dexLocations: []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar", "host/linux-x86/system/framework/extra1.jar"}, + dexLocationsDeps: []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar", "host/linux-x86/system/framework/extra1.jar"}, imagePathOnHost: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art", imagePathOnDevice: "/apex/art_boot_images/javalib/x86/boot.art", imagesDeps: []string{ @@ -388,6 +463,9 @@ func checkArtBootImageConfig(t *testing.T, result *android.TestResult, mutated b "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art", "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat", "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex", + "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.art", + "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.oat", + "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.vdex", }, installs: []normalizedInstall{ { @@ -404,6 +482,13 @@ func checkArtBootImageConfig(t *testing.T, result *android.TestResult, mutated b from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat", to: "/apex/art_boot_images/javalib/x86/boot-core2.oat", }, + { + from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.art", + to: "/apex/art_boot_images/javalib/x86/boot-extra1.art", + }, { + from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.oat", + to: "/apex/art_boot_images/javalib/x86/boot-extra1.oat", + }, }, vdexInstalls: []normalizedInstall{ { @@ -414,6 +499,10 @@ func checkArtBootImageConfig(t *testing.T, result *android.TestResult, mutated b from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex", to: "/apex/art_boot_images/javalib/x86/boot-core2.vdex", }, + { + from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.vdex", + to: "/apex/art_boot_images/javalib/x86/boot-extra1.vdex", + }, }, unstrippedInstalls: []normalizedInstall{ { @@ -424,13 +513,17 @@ func checkArtBootImageConfig(t *testing.T, result *android.TestResult, mutated b from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat", to: "/apex/art_boot_images/javalib/x86/boot-core2.oat", }, + { + from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.oat", + to: "/apex/art_boot_images/javalib/x86/boot-extra1.oat", + }, }, licenseMetadataFile: expectedLicenseMetadataFile, }, }, } - checkBootImageConfig(t, imageConfig, mutated, expected) + checkBootImageConfig(t, result, imageConfig, mutated, expected) } // getFrameworkImageConfig gets the framework bootImageConfig that was created during the test. @@ -805,13 +898,13 @@ func checkFrameworkBootImageConfig(t *testing.T, result *android.TestResult, mut }, }, profileInstalls: []normalizedInstall{ + {from: "out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof", to: "/system/etc/boot-image.prof"}, {from: "out/soong/dexpreopt_arm64/dex_bootjars/boot.bprof", to: "/system/etc/boot-image.bprof"}, - {from: "out/soong/dexpreopt_arm64/dex_bootjars/boot.prof", to: "/system/etc/boot-image.prof"}, }, profileLicenseMetadataFile: expectedLicenseMetadataFile, } - checkBootImageConfig(t, imageConfig, mutated, expected) + checkBootImageConfig(t, result, imageConfig, mutated, expected) } // getMainlineImageConfig gets the framework bootImageConfig that was created during the test. @@ -1090,7 +1183,7 @@ func CheckMainlineBootImageConfig(t *testing.T, result *android.TestResult) { profileLicenseMetadataFile: expectedLicenseMetadataFile, } - checkBootImageConfig(t, imageConfig, false, expected) + checkBootImageConfig(t, result, imageConfig, false, expected) } // clearMutatedFields clears fields in the expectedConfig that correspond to fields in the @@ -1118,32 +1211,36 @@ func clearMutatedFields(expected *expectedConfig) { // zero value so that they will match the unmodified values in the boot image. // // It runs the checks in an image specific subtest of the current test. -func checkBootImageConfig(t *testing.T, imageConfig *bootImageConfig, mutated bool, expected *expectedConfig) { +func checkBootImageConfig(t *testing.T, result *android.TestResult, imageConfig *bootImageConfig, mutated bool, expected *expectedConfig) { if !mutated { clearMutatedFields(expected) } t.Run(imageConfig.name, func(t *testing.T) { - nestedCheckBootImageConfig(t, imageConfig, expected) + nestedCheckBootImageConfig(t, result, imageConfig, mutated, expected) }) } // nestedCheckBootImageConfig does the work of comparing the image against the expected values and // is run in an image specific subtest. -func nestedCheckBootImageConfig(t *testing.T, imageConfig *bootImageConfig, expected *expectedConfig) { +func nestedCheckBootImageConfig(t *testing.T, result *android.TestResult, imageConfig *bootImageConfig, mutated bool, expected *expectedConfig) { android.AssertStringEquals(t, "name", expected.name, imageConfig.name) android.AssertStringEquals(t, "stem", expected.stem, imageConfig.stem) android.AssertPathRelativeToTopEquals(t, "dir", expected.dir, imageConfig.dir) android.AssertPathRelativeToTopEquals(t, "symbolsDir", expected.symbolsDir, imageConfig.symbolsDir) android.AssertStringEquals(t, "installDir", expected.installDir, imageConfig.installDir) - android.AssertStringEquals(t, "profileInstallPathInApex", expected.profileInstallPathInApex, imageConfig.profileInstallPathInApex) android.AssertDeepEquals(t, "modules", expected.modules, imageConfig.modules) android.AssertPathsRelativeToTopEquals(t, "dexPaths", expected.dexPaths, imageConfig.dexPaths.Paths()) android.AssertPathsRelativeToTopEquals(t, "dexPathsDeps", expected.dexPathsDeps, imageConfig.dexPathsDeps.Paths()) // dexPathsByModule is just a different representation of the other information in the config. android.AssertPathRelativeToTopEquals(t, "zip", expected.zip, imageConfig.zip) - assertInstallsEqual(t, "profileInstalls", expected.profileInstalls, imageConfig.profileInstalls) - android.AssertStringEquals(t, "profileLicenseMetadataFile", expected.profileLicenseMetadataFile, imageConfig.profileLicenseMetadataFile.RelativeToTop().String()) + + if !mutated { + dexBootJarModule := result.ModuleForTests("dex_bootjars", "android_common") + profileInstallInfo, _ := android.SingletonModuleProvider(result, dexBootJarModule.Module(), profileInstallInfoProvider) + assertInstallsEqual(t, "profileInstalls", expected.profileInstalls, profileInstallInfo.profileInstalls) + android.AssertStringEquals(t, "profileLicenseMetadataFile", expected.profileLicenseMetadataFile, profileInstallInfo.profileLicenseMetadataFile.RelativeToTop().String()) + } android.AssertIntEquals(t, "variant count", 4, len(imageConfig.variants)) for i, variant := range imageConfig.variants { @@ -1195,10 +1292,10 @@ DEXPREOPT_BOOTCLASSPATH_DEX_FILES=out/soong/dexpreopt_arm64/dex_bootjars_input/c DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS=/apex/com.android.art/javalib/core1.jar /apex/com.android.art/javalib/core2.jar /system/framework/framework.jar DEXPREOPT_BOOT_JARS_MODULES=com.android.art:core1:com.android.art:core2:platform:framework DEXPREOPT_GEN=out/host/linux-x86/bin/dexpreopt_gen -DEXPREOPT_IMAGE_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art:/apex/art_boot_images/javalib/arm/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art:/apex/art_boot_images/javalib/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat -DEXPREOPT_IMAGE_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art:/apex/art_boot_images/javalib/arm64/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art:/apex/art_boot_images/javalib/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat -DEXPREOPT_IMAGE_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art:/apex/art_boot_images/javalib/x86/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art:/apex/art_boot_images/javalib/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat -DEXPREOPT_IMAGE_BUILT_INSTALLED_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art:/apex/art_boot_images/javalib/x86_64/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat:/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art:/apex/art_boot_images/javalib/x86_64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat:/apex/art_boot_images/javalib/x86_64/boot-core2.oat +DEXPREOPT_IMAGE_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art:/apex/art_boot_images/javalib/arm/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art:/apex/art_boot_images/javalib/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.art:/apex/art_boot_images/javalib/arm/boot-extra1.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.oat:/apex/art_boot_images/javalib/arm/boot-extra1.oat +DEXPREOPT_IMAGE_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art:/apex/art_boot_images/javalib/arm64/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art:/apex/art_boot_images/javalib/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.art:/apex/art_boot_images/javalib/arm64/boot-extra1.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.oat:/apex/art_boot_images/javalib/arm64/boot-extra1.oat +DEXPREOPT_IMAGE_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art:/apex/art_boot_images/javalib/x86/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art:/apex/art_boot_images/javalib/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.art:/apex/art_boot_images/javalib/x86/boot-extra1.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.oat:/apex/art_boot_images/javalib/x86/boot-extra1.oat +DEXPREOPT_IMAGE_BUILT_INSTALLED_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art:/apex/art_boot_images/javalib/x86_64/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat:/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art:/apex/art_boot_images/javalib/x86_64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat:/apex/art_boot_images/javalib/x86_64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.art:/apex/art_boot_images/javalib/x86_64/boot-extra1.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.oat:/apex/art_boot_images/javalib/x86_64/boot-extra1.oat DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_arm=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art:/system/framework/arm/boot.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.oat:/system/framework/arm/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.art:/system/framework/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.oat:/system/framework/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.art:/system/framework/arm/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.oat:/system/framework/arm/boot-framework.oat DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_arm64=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art:/system/framework/arm64/boot.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.oat:/system/framework/arm64/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.art:/system/framework/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.oat:/system/framework/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.art:/system/framework/arm64/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.oat:/system/framework/arm64/boot-framework.oat DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_host_x86=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art:/system/framework/x86/boot.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.oat:/system/framework/x86/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.art:/system/framework/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.oat:/system/framework/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art:/system/framework/x86/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat:/system/framework/x86/boot-framework.oat @@ -1207,10 +1304,10 @@ DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_arm=out/soong/dexpreopt_arm64/dex_mainl DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_arm64=out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art:/system/framework/arm64/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.oat:/system/framework/arm64/boot-framework-foo.oat DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_host_x86=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art:/system/framework/x86/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.oat:/system/framework/x86/boot-framework-foo.oat DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_host_x86_64=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art:/system/framework/x86_64/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.oat:/system/framework/x86_64/boot-framework-foo.oat -DEXPREOPT_IMAGE_DEPS_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex -DEXPREOPT_IMAGE_DEPS_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex -DEXPREOPT_IMAGE_DEPS_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex -DEXPREOPT_IMAGE_DEPS_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex +DEXPREOPT_IMAGE_DEPS_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.vdex +DEXPREOPT_IMAGE_DEPS_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.vdex +DEXPREOPT_IMAGE_DEPS_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.vdex +DEXPREOPT_IMAGE_DEPS_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.vdex DEXPREOPT_IMAGE_DEPS_boot_arm=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.vdex DEXPREOPT_IMAGE_DEPS_boot_arm64=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.vdex DEXPREOPT_IMAGE_DEPS_boot_host_x86=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex @@ -1223,14 +1320,14 @@ DEXPREOPT_IMAGE_LICENSE_METADATA_art_arm=%[1]s DEXPREOPT_IMAGE_LICENSE_METADATA_art_arm64=%[1]s DEXPREOPT_IMAGE_LICENSE_METADATA_art_host_x86=%[1]s DEXPREOPT_IMAGE_LICENSE_METADATA_art_host_x86_64=%[1]s -DEXPREOPT_IMAGE_LICENSE_METADATA_boot_arm=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic -DEXPREOPT_IMAGE_LICENSE_METADATA_boot_arm64=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic -DEXPREOPT_IMAGE_LICENSE_METADATA_boot_host_x86=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic -DEXPREOPT_IMAGE_LICENSE_METADATA_boot_host_x86_64=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic -DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_arm=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic -DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_arm64=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic -DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_host_x86=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic -DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_host_x86_64=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic +DEXPREOPT_IMAGE_LICENSE_METADATA_boot_arm=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic +DEXPREOPT_IMAGE_LICENSE_METADATA_boot_arm64=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic +DEXPREOPT_IMAGE_LICENSE_METADATA_boot_host_x86=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic +DEXPREOPT_IMAGE_LICENSE_METADATA_boot_host_x86_64=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic +DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_arm=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic +DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_arm64=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic +DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_host_x86=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic +DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_host_x86_64=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEart=/apex/art_boot_images/javalib/boot.art DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEboot=/system/framework/boot.art DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEmainline=/system/framework/boot.art:/system/framework/boot-framework-foo.art @@ -1238,12 +1335,12 @@ DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTart=out/soong/dexpreopt_arm64/dex_artjars/andro DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTboot=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/boot.art DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTmainline=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/boot.art:out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/boot-framework-foo.art DEXPREOPT_IMAGE_NAMES=art boot mainline -DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED=out/soong/dexpreopt_arm64/dex_bootjars/boot.bprof:/system/etc/boot-image.bprof out/soong/dexpreopt_arm64/dex_bootjars/boot.prof:/system/etc/boot-image.prof -DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic -DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat -DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat -DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat -DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat:/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat:/apex/art_boot_images/javalib/x86_64/boot-core2.oat +DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED=out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof:/system/etc/boot-image.prof out/soong/dexpreopt_arm64/dex_bootjars/boot.bprof:/system/etc/boot-image.bprof +DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA=out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic +DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-extra1.oat:/apex/art_boot_images/javalib/arm/boot-extra1.oat +DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-extra1.oat:/apex/art_boot_images/javalib/arm64/boot-extra1.oat +DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.oat:/apex/art_boot_images/javalib/x86/boot-extra1.oat +DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat:/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat:/apex/art_boot_images/javalib/x86_64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.oat:/apex/art_boot_images/javalib/x86_64/boot-extra1.oat DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_arm=out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm/boot.oat:/system/framework/arm/boot.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm/boot-core2.oat:/system/framework/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm/boot-framework.oat:/system/framework/arm/boot-framework.oat DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_arm64=out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot.oat:/system/framework/arm64/boot.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-core2.oat:/system/framework/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-framework.oat:/system/framework/arm64/boot-framework.oat DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_host_x86=out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot.oat:/system/framework/x86/boot.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot-core2.oat:/system/framework/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot-framework.oat:/system/framework/x86/boot-framework.oat @@ -1252,10 +1349,10 @@ DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_arm=out/soong/dexpreopt_arm6 DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_arm64=out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/android/system/framework/arm64/boot-framework-foo.oat:/system/framework/arm64/boot-framework-foo.oat DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_host_x86=out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/linux_glibc/system/framework/x86/boot-framework-foo.oat:/system/framework/x86/boot-framework-foo.oat DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_host_x86_64=out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/linux_glibc/system/framework/x86_64/boot-framework-foo.oat:/system/framework/x86_64/boot-framework-foo.oat -DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex:/apex/art_boot_images/javalib/arm/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex:/apex/art_boot_images/javalib/arm/boot-core2.vdex -DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex:/apex/art_boot_images/javalib/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex:/apex/art_boot_images/javalib/arm64/boot-core2.vdex -DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex:/apex/art_boot_images/javalib/x86/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex:/apex/art_boot_images/javalib/x86/boot-core2.vdex -DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex:/apex/art_boot_images/javalib/x86_64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex:/apex/art_boot_images/javalib/x86_64/boot-core2.vdex +DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex:/apex/art_boot_images/javalib/arm/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex:/apex/art_boot_images/javalib/arm/boot-core2.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-extra1.vdex:/apex/art_boot_images/javalib/arm/boot-extra1.vdex +DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex:/apex/art_boot_images/javalib/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex:/apex/art_boot_images/javalib/arm64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-extra1.vdex:/apex/art_boot_images/javalib/arm64/boot-extra1.vdex +DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex:/apex/art_boot_images/javalib/x86/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex:/apex/art_boot_images/javalib/x86/boot-core2.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-extra1.vdex:/apex/art_boot_images/javalib/x86/boot-extra1.vdex +DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex:/apex/art_boot_images/javalib/x86_64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex:/apex/art_boot_images/javalib/x86_64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-extra1.vdex:/apex/art_boot_images/javalib/x86_64/boot-extra1.vdex DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_arm=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.vdex:/system/framework/arm/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.vdex:/system/framework/arm/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.vdex:/system/framework/arm/boot-framework.vdex DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_arm64=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.vdex:/system/framework/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.vdex:/system/framework/arm64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.vdex:/system/framework/arm64/boot-framework.vdex DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_host_x86=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.vdex:/system/framework/x86/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.vdex:/system/framework/x86/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex:/system/framework/x86/boot-framework.vdex diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go index fedd5640e..73e33f4fb 100644 --- a/java/dexpreopt_test.go +++ b/java/dexpreopt_test.go @@ -410,7 +410,7 @@ func TestAndroidMkEntriesForApex(t *testing.T) { verifyEntries(t, "entriesList[0]", "service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.odex", - "/dexpreopt/oat/arm64/javalib.odex", + "/dexpreopt/service-foo/oat/arm64/javalib.odex", "/system/framework/oat/arm64", "apex@com.android.apex1@javalib@service-foo.jar@classes.odex", entriesList[0]) @@ -418,7 +418,7 @@ func TestAndroidMkEntriesForApex(t *testing.T) { verifyEntries(t, "entriesList[1]", "service-foo-dexpreopt-arm64-apex@com.android.apex1@javalib@service-foo.jar@classes.vdex", - "/dexpreopt/oat/arm64/javalib.vdex", + "/dexpreopt/service-foo/oat/arm64/javalib.vdex", "/system/framework/oat/arm64", "apex@com.android.apex1@javalib@service-foo.jar@classes.vdex", entriesList[1]) @@ -459,7 +459,7 @@ func TestGenerateProfileEvenIfDexpreoptIsDisabled(t *testing.T) { ctx := result.TestContext dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeRule("dexpreopt") - expected := []string{"out/soong/.intermediates/foo/android_common/dexpreopt/profile.prof"} + expected := []string{"out/soong/.intermediates/foo/android_common/dexpreopt/foo/profile.prof"} android.AssertArrayString(t, "outputs", expected, dexpreopt.AllOutputs()) } diff --git a/java/droiddoc.go b/java/droiddoc.go index dbe021d00..730f23696 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -135,6 +135,9 @@ type DroiddocProperties struct { // At some point, this might be improved to show more warnings. Todo_file *string `android:"path"` + // A file containing a baseline for allowed lint errors. + Lint_baseline *string `android:"path"` + // directory under current module source that provide additional resources (images). Resourcesdir *string @@ -179,6 +182,17 @@ func InitDroiddocModule(module android.DefaultableModule, hod android.HostOrDevi func apiCheckEnabled(ctx android.ModuleContext, apiToCheck ApiToCheck, apiVersionTag string) bool { if ctx.Config().IsEnvTrue("WITHOUT_CHECK_API") { + if ctx.Config().BuildFromTextStub() { + ctx.ModuleErrorf("Generating stubs from api signature files is not available " + + "with WITHOUT_CHECK_API=true, as sync between the source Java files and the " + + "api signature files is not guaranteed.\n" + + "In order to utilize WITHOUT_CHECK_API, generate stubs from the source Java " + + "files with BUILD_FROM_SOURCE_STUB=true.\n" + + "However, the usage of WITHOUT_CHECK_API is not preferred as the incremental " + + "build is slower when generating stubs from the source Java files.\n" + + "Consider updating the api signature files and generating the stubs from " + + "them instead.") + } return false } else if String(apiToCheck.Api_file) != "" && String(apiToCheck.Removed_api_file) != "" { return true @@ -205,17 +219,8 @@ type Javadoc struct { docZip android.WritablePath stubsSrcJar android.WritablePath -} -func (j *Javadoc) OutputFiles(tag string) (android.Paths, error) { - switch tag { - case "": - return android.Paths{j.stubsSrcJar}, nil - case ".docs.zip": - return android.Paths{j.docZip}, nil - default: - return nil, fmt.Errorf("unsupported module reference tag %q", tag) - } + exportableStubsSrcJar android.WritablePath } // javadoc converts .java source files to documentation using javadoc. @@ -238,8 +243,6 @@ func JavadocHostFactory() android.Module { return module } -var _ android.OutputFileProducer = (*Javadoc)(nil) - func (j *Javadoc) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec { return android.SdkSpecFrom(ctx, String(j.properties.Sdk_version)) } @@ -299,7 +302,7 @@ func (j *Javadoc) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.Op } flags = append(flags, android.JoinWithPrefix(aidlIncludes.Strings(), "-I")) - flags = append(flags, "-I"+android.PathForModuleSrc(ctx).String()) + flags = append(flags, "-I"+ctx.ModuleDir()) if src := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "src"); src.Valid() { flags = append(flags, "-I"+src.String()) } @@ -360,8 +363,7 @@ func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps { switch tag { case bootClasspathTag: - if ctx.OtherModuleHasProvider(module, JavaInfoProvider) { - dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo) + if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok { deps.bootClasspath = append(deps.bootClasspath, dep.ImplementationJars...) } else if sm, ok := module.(SystemModulesProvider); ok { // A system modules dependency has been added to the bootclasspath @@ -373,19 +375,19 @@ func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps { case libTag, sdkLibTag: if dep, ok := module.(SdkLibraryDependency); ok { deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...) - } else if ctx.OtherModuleHasProvider(module, JavaInfoProvider) { - dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo) + } else if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok { deps.classpath = append(deps.classpath, dep.HeaderJars...) deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...) + deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.AconfigIntermediateCacheOutputPaths...) } else if dep, ok := module.(android.SourceFileProducer); ok { checkProducesJars(ctx, dep) deps.classpath = append(deps.classpath, dep.Srcs()...) } else { ctx.ModuleErrorf("depends on non-java module %q", otherName) } + case java9LibTag: - if ctx.OtherModuleHasProvider(module, JavaInfoProvider) { - dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo) + if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok { deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars...) } else { ctx.ModuleErrorf("depends on non-java module %q", otherName) @@ -397,6 +399,18 @@ func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps { sm := module.(SystemModulesProvider) outputDir, outputDeps := sm.OutputDirAndDeps() deps.systemModules = &systemModules{outputDir, outputDeps} + case aconfigDeclarationTag: + if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey); ok { + deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPath) + } else if dep, ok := android.OtherModuleProvider(ctx, module, android.CodegenInfoProvider); ok { + deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPaths...) + } else { + ctx.ModuleErrorf("Only aconfig_declarations and aconfig_declarations_group "+ + "module type is allowed for flags_packages property, but %s is neither "+ + "of these supported module types", + module.Name(), + ) + } } }) // do not pass exclude_srcs directly when expanding srcFiles since exclude_srcs @@ -404,6 +418,19 @@ func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps { srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs) j.implicits = append(j.implicits, srcFiles...) + // Module can depend on a java_aconfig_library module using the ":module_name{.tag}" syntax. + // Find the corresponding aconfig_declarations module name for such case. + for _, src := range j.properties.Srcs { + if moduleName, tag := android.SrcIsModuleWithTag(src); moduleName != "" { + otherModule := android.GetModuleFromPathDep(ctx, moduleName, tag) + if otherModule != nil { + if dep, ok := android.OtherModuleProvider(ctx, otherModule, android.CodegenInfoProvider); ok { + deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPaths...) + } + } + } + } + filterByPackage := func(srcs []android.Path, filterPackages []string) []android.Path { if filterPackages == nil { return srcs @@ -545,6 +572,9 @@ func (j *Javadoc) GenerateAndroidBuildActions(ctx android.ModuleContext) { zipSyncCleanupCmd(rule, srcJarDir) rule.Build("javadoc", "javadoc") + + ctx.SetOutputFiles(android.Paths{j.stubsSrcJar}, "") + ctx.SetOutputFiles(android.Paths{j.docZip}, ".docs.zip") } // Droiddoc @@ -576,15 +606,6 @@ func DroiddocHostFactory() android.Module { return module } -func (d *Droiddoc) OutputFiles(tag string) (android.Paths, error) { - switch tag { - case "", ".docs.zip": - return android.Paths{d.Javadoc.docZip}, nil - default: - return nil, fmt.Errorf("unsupported module reference tag %q", tag) - } -} - func (d *Droiddoc) DepsMutator(ctx android.BottomUpMutatorContext) { d.Javadoc.addDeps(ctx) @@ -608,6 +629,11 @@ func (d *Droiddoc) doclavaDocsFlags(ctx android.ModuleContext, cmd *android.Rule FlagWithArg("-Xmaxerrs ", "10"). FlagWithArg("-Xmaxwarns ", "10"). Flag("-J--add-exports=jdk.javadoc/jdk.javadoc.internal.doclets.formats.html=ALL-UNNAMED"). + Flag("-J--add-exports=jdk.javadoc/jdk.javadoc.internal.tool=ALL-UNNAMED"). + Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED"). + Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED"). + Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED"). + Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED"). Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"). FlagWithArg("-hdf page.build ", ctx.Config().BuildId()+"-$(cat "+buildNumberFile.String()+")").OrderOnly(buildNumberFile). FlagWithArg("-hdf page.now ", `"$(date -d @$(cat `+ctx.Config().Getenv("BUILD_DATETIME_FILE")+`) "+%d %b %Y %k:%M")" `) @@ -659,6 +685,10 @@ func (d *Droiddoc) doclavaDocsFlags(ctx android.ModuleContext, cmd *android.Rule ImplicitOutput(android.PathForModuleOut(ctx, String(d.properties.Todo_file))) } + if String(d.properties.Lint_baseline) != "" { + cmd.FlagWithInput("-lintbaseline ", android.PathForModuleSrc(ctx, String(d.properties.Lint_baseline))) + } + if String(d.properties.Resourcesdir) != "" { // TODO: should we add files under resourcesDir to the implicits? It seems that // resourcesDir is one sub dir of htmlDir @@ -694,7 +724,6 @@ func javadocCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs andro cmd := rule.Command(). BuiltTool("soong_javac_wrapper").Tool(config.JavadocCmd(ctx)). Flag(config.JavacVmFlags). - FlagWithArg("-encoding ", "UTF-8"). FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "javadoc.rsp"), srcs). FlagWithInput("@", srcJarList) @@ -828,6 +857,9 @@ func (d *Droiddoc) GenerateAndroidBuildActions(ctx android.ModuleContext) { zipSyncCleanupCmd(rule, srcJarDir) rule.Build("javadoc", desc) + + ctx.SetOutputFiles(android.Paths{d.Javadoc.docZip}, "") + ctx.SetOutputFiles(android.Paths{d.Javadoc.docZip}, ".docs.zip") } // Exported Droiddoc Directory diff --git a/java/droiddoc_test.go b/java/droiddoc_test.go index 8d1f5917c..e5846407f 100644 --- a/java/droiddoc_test.go +++ b/java/droiddoc_test.go @@ -69,11 +69,7 @@ func TestDroiddoc(t *testing.T) { "bar-doc/a.java": nil, "bar-doc/b.java": nil, }) - barStubs := ctx.ModuleForTests("bar-stubs", "android_common") - barStubsOutputs, err := barStubs.Module().(*Droidstubs).OutputFiles("") - if err != nil { - t.Errorf("Unexpected error %q retrieving \"bar-stubs\" output file", err) - } + barStubsOutputs := ctx.ModuleForTests("bar-stubs", "android_common").OutputFiles(t, "") if len(barStubsOutputs) != 1 { t.Errorf("Expected one output from \"bar-stubs\" got %s", barStubsOutputs) } diff --git a/java/droidstubs.go b/java/droidstubs.go index 8a521aabb..a8e0a22e5 100644 --- a/java/droidstubs.go +++ b/java/droidstubs.go @@ -18,13 +18,11 @@ import ( "fmt" "path/filepath" "regexp" - "sort" "strings" "github.com/google/blueprint/proptools" "android/soong/android" - "android/soong/bazel" "android/soong/java/config" "android/soong/remoteexec" ) @@ -32,6 +30,41 @@ import ( // The values allowed for Droidstubs' Api_levels_sdk_type var allowedApiLevelSdkTypes = []string{"public", "system", "module-lib", "system-server"} +type StubsType int + +const ( + Everything StubsType = iota + Runtime + Exportable + Unavailable +) + +func (s StubsType) String() string { + switch s { + case Everything: + return "everything" + case Runtime: + return "runtime" + case Exportable: + return "exportable" + default: + return "" + } +} + +func StringToStubsType(s string) StubsType { + switch strings.ToLower(s) { + case Everything.String(): + return Everything + case Runtime.String(): + return Runtime + case Exportable.String(): + return Exportable + default: + return Unavailable + } +} + func init() { RegisterStubsBuildComponents(android.InitRegistrationContext) } @@ -45,14 +78,22 @@ func RegisterStubsBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory) } +type stubsArtifacts struct { + nullabilityWarningsFile android.WritablePath + annotationsZip android.WritablePath + apiVersionsXml android.WritablePath + metadataZip android.WritablePath + metadataDir android.WritablePath +} + // Droidstubs type Droidstubs struct { Javadoc + embeddableInModuleAndImport - properties DroidstubsProperties - apiFile android.Path - removedApiFile android.Path - nullabilityWarningsFile android.WritablePath + properties DroidstubsProperties + apiFile android.Path + removedApiFile android.Path checkCurrentApiTimestamp android.WritablePath updateCurrentApiTimestamp android.WritablePath @@ -62,11 +103,11 @@ type Droidstubs struct { checkNullabilityWarningsTimestamp android.WritablePath - annotationsZip android.WritablePath - apiVersionsXml android.WritablePath + everythingArtifacts stubsArtifacts + exportableArtifacts stubsArtifacts - metadataZip android.WritablePath - metadataDir android.WritablePath + exportableApiFile android.WritablePath + exportableRemovedApiFile android.WritablePath } type DroidstubsProperties struct { @@ -123,7 +164,7 @@ type DroidstubsProperties struct { Generate_stubs *bool // if set to true, provides a hint to the build system that this rule uses a lot of memory, - // whicih can be used for scheduling purposes + // which can be used for scheduling purposes High_mem *bool // if set to true, Metalava will allow framework SDK to contain API levels annotations. @@ -152,26 +193,64 @@ type DroidstubsProperties struct { // API surface of this module. If set, the module contributes to an API surface. // For the full list of available API surfaces, refer to soong/android/sdk_version.go Api_surface *string + + // a list of aconfig_declarations module names that the stubs generated in this module + // depend on. + Aconfig_declarations []string } // Used by xsd_config type ApiFilePath interface { - ApiFilePath() android.Path + ApiFilePath(StubsType) (android.Path, error) } type ApiStubsSrcProvider interface { - StubsSrcJar() android.Path + StubsSrcJar(StubsType) (android.Path, error) } // Provider of information about API stubs, used by java_sdk_library. type ApiStubsProvider interface { - AnnotationsZip() android.Path + AnnotationsZip(StubsType) (android.Path, error) ApiFilePath - RemovedApiFilePath() android.Path + RemovedApiFilePath(StubsType) (android.Path, error) ApiStubsSrcProvider } +type currentApiTimestampProvider interface { + CurrentApiTimestamp() android.Path +} + +type annotationFlagsParams struct { + migratingNullability bool + validatingNullability bool + nullabilityWarningsFile android.WritablePath + annotationsZip android.WritablePath +} +type stubsCommandParams struct { + srcJarDir android.ModuleOutPath + stubsDir android.OptionalPath + stubsSrcJar android.WritablePath + metadataZip android.WritablePath + metadataDir android.WritablePath + apiVersionsXml android.WritablePath + nullabilityWarningsFile android.WritablePath + annotationsZip android.WritablePath + stubConfig stubsCommandConfigParams +} +type stubsCommandConfigParams struct { + stubsType StubsType + javaVersion javaVersion + deps deps + checkApi bool + generateStubs bool + doApiLint bool + doCheckReleased bool + writeSdkValues bool + migratingNullability bool + validatingNullability bool +} + // droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be // documented, filtering out hidden classes and methods. The resulting .java files are intended to be passed to // a droiddoc module to generate documentation. @@ -180,6 +259,7 @@ func DroidstubsFactory() android.Module { module.AddProperties(&module.properties, &module.Javadoc.properties) + module.initModuleAndImport(module) InitDroiddocModule(module, android.HostAndDeviceSupported) @@ -203,46 +283,100 @@ func DroidstubsHostFactory() android.Module { return module } -func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) { - switch tag { - case "": - return android.Paths{d.stubsSrcJar}, nil - case ".docs.zip": - return android.Paths{d.docZip}, nil - case ".api.txt", android.DefaultDistTag: - // This is the default dist path for dist properties that have no tag property. - return android.Paths{d.apiFile}, nil - case ".removed-api.txt": - return android.Paths{d.removedApiFile}, nil - case ".annotations.zip": - return android.Paths{d.annotationsZip}, nil - case ".api_versions.xml": - return android.Paths{d.apiVersionsXml}, nil +func (d *Droidstubs) AnnotationsZip(stubsType StubsType) (ret android.Path, err error) { + switch stubsType { + case Everything: + ret, err = d.everythingArtifacts.annotationsZip, nil + case Exportable: + ret, err = d.exportableArtifacts.annotationsZip, nil default: - return nil, fmt.Errorf("unsupported module reference tag %q", tag) + ret, err = nil, fmt.Errorf("annotations zip not supported for the stub type %s", stubsType.String()) } + return ret, err } -func (d *Droidstubs) AnnotationsZip() android.Path { - return d.annotationsZip +func (d *Droidstubs) ApiFilePath(stubsType StubsType) (ret android.Path, err error) { + switch stubsType { + case Everything: + ret, err = d.apiFile, nil + case Exportable: + ret, err = d.exportableApiFile, nil + default: + ret, err = nil, fmt.Errorf("api file path not supported for the stub type %s", stubsType.String()) + } + if ret == nil && err == nil { + err = fmt.Errorf("api file is null for the stub type %s", stubsType.String()) + } + return ret, err } -func (d *Droidstubs) ApiFilePath() android.Path { - return d.apiFile +func (d *Droidstubs) ApiVersionsXmlFilePath(stubsType StubsType) (ret android.Path, err error) { + switch stubsType { + case Everything: + ret, err = d.everythingArtifacts.apiVersionsXml, nil + case Exportable: + ret, err = d.exportableArtifacts.apiVersionsXml, nil + default: + ret, err = nil, fmt.Errorf("api versions xml file path not supported for the stub type %s", stubsType.String()) + } + if ret == nil && err == nil { + err = fmt.Errorf("api versions xml file is null for the stub type %s", stubsType.String()) + } + return ret, err +} + +func (d *Droidstubs) DocZip(stubsType StubsType) (ret android.Path, err error) { + switch stubsType { + case Everything: + ret, err = d.docZip, nil + default: + ret, err = nil, fmt.Errorf("docs zip not supported for the stub type %s", stubsType.String()) + } + if ret == nil && err == nil { + err = fmt.Errorf("docs zip is null for the stub type %s", stubsType.String()) + } + return ret, err +} + +func (d *Droidstubs) RemovedApiFilePath(stubsType StubsType) (ret android.Path, err error) { + switch stubsType { + case Everything: + ret, err = d.removedApiFile, nil + case Exportable: + ret, err = d.exportableRemovedApiFile, nil + default: + ret, err = nil, fmt.Errorf("removed api file path not supported for the stub type %s", stubsType.String()) + } + if ret == nil && err == nil { + err = fmt.Errorf("removed api file is null for the stub type %s", stubsType.String()) + } + return ret, err } -func (d *Droidstubs) RemovedApiFilePath() android.Path { - return d.removedApiFile +func (d *Droidstubs) StubsSrcJar(stubsType StubsType) (ret android.Path, err error) { + switch stubsType { + case Everything: + ret, err = d.stubsSrcJar, nil + case Exportable: + ret, err = d.exportableStubsSrcJar, nil + default: + ret, err = nil, fmt.Errorf("stubs srcjar not supported for the stub type %s", stubsType.String()) + } + if ret == nil && err == nil { + err = fmt.Errorf("stubs srcjar is null for the stub type %s", stubsType.String()) + } + return ret, err } -func (d *Droidstubs) StubsSrcJar() android.Path { - return d.stubsSrcJar +func (d *Droidstubs) CurrentApiTimestamp() android.Path { + return d.checkCurrentApiTimestamp } var metalavaMergeAnnotationsDirTag = dependencyTag{name: "metalava-merge-annotations-dir"} var metalavaMergeInclusionAnnotationsDirTag = dependencyTag{name: "metalava-merge-inclusion-annotations-dir"} var metalavaAPILevelsAnnotationsDirTag = dependencyTag{name: "metalava-api-levels-annotations-dir"} var metalavaAPILevelsModuleTag = dependencyTag{name: "metalava-api-levels-module-tag"} +var metalavaCurrentApiTimestampTag = dependencyTag{name: "metalava-current-api-timestamp-tag"} func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) { d.Javadoc.addDeps(ctx) @@ -265,39 +399,57 @@ func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) { } } + if len(d.properties.Aconfig_declarations) != 0 { + for _, aconfigDeclarationModuleName := range d.properties.Aconfig_declarations { + ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfigDeclarationModuleName) + } + } + if d.properties.Api_levels_module != nil { ctx.AddDependency(ctx.Module(), metalavaAPILevelsModuleTag, proptools.String(d.properties.Api_levels_module)) } } -func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath) { - if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") || - apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") || - String(d.properties.Api_filename) != "" { - filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt") - uncheckedApiFile := android.PathForModuleOut(ctx, "metalava", filename) - cmd.FlagWithOutput("--api ", uncheckedApiFile) - d.apiFile = uncheckedApiFile +func (d *Droidstubs) sdkValuesFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, metadataDir android.WritablePath) { + cmd.FlagWithArg("--sdk-values ", metadataDir.String()) +} + +func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath, stubsType StubsType, checkApi bool) { + + apiFileName := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt") + uncheckedApiFile := android.PathForModuleOut(ctx, stubsType.String(), apiFileName) + cmd.FlagWithOutput("--api ", uncheckedApiFile) + if checkApi || String(d.properties.Api_filename) != "" { + if stubsType == Everything { + d.apiFile = uncheckedApiFile + } else if stubsType == Exportable { + d.exportableApiFile = uncheckedApiFile + } } else if sourceApiFile := proptools.String(d.properties.Check_api.Current.Api_file); sourceApiFile != "" { - // If check api is disabled then make the source file available for export. - d.apiFile = android.PathForModuleSrc(ctx, sourceApiFile) + if stubsType == Everything { + // If check api is disabled then make the source file available for export. + d.apiFile = android.PathForModuleSrc(ctx, sourceApiFile) + } else if stubsType == Exportable { + d.exportableApiFile = uncheckedApiFile + } } - if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") || - apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") || - String(d.properties.Removed_api_filename) != "" { - filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt") - uncheckedRemovedFile := android.PathForModuleOut(ctx, "metalava", filename) - cmd.FlagWithOutput("--removed-api ", uncheckedRemovedFile) - d.removedApiFile = uncheckedRemovedFile + removedApiFileName := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt") + uncheckedRemovedFile := android.PathForModuleOut(ctx, stubsType.String(), removedApiFileName) + cmd.FlagWithOutput("--removed-api ", uncheckedRemovedFile) + if checkApi || String(d.properties.Removed_api_filename) != "" { + if stubsType == Everything { + d.removedApiFile = uncheckedRemovedFile + } else if stubsType == Exportable { + d.exportableRemovedApiFile = uncheckedRemovedFile + } } else if sourceRemovedApiFile := proptools.String(d.properties.Check_api.Current.Removed_api_file); sourceRemovedApiFile != "" { - // If check api is disabled then make the source removed api file available for export. - d.removedApiFile = android.PathForModuleSrc(ctx, sourceRemovedApiFile) - } - - if Bool(d.properties.Write_sdk_values) { - d.metadataDir = android.PathForModuleOut(ctx, "metalava", "metadata") - cmd.FlagWithArg("--sdk-values ", d.metadataDir.String()) + if stubsType == Everything { + // If check api is disabled then make the source removed api file available for export. + d.removedApiFile = android.PathForModuleSrc(ctx, sourceRemovedApiFile) + } else if stubsType == Exportable { + d.exportableRemovedApiFile = uncheckedRemovedFile + } } if stubsDir.Valid() { @@ -312,46 +464,30 @@ func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuil } } -func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) { +func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, params annotationFlagsParams) { if Bool(d.properties.Annotations_enabled) { - cmd.Flag("--include-annotations") - - cmd.FlagWithArg("--exclude-annotation ", "androidx.annotation.RequiresApi") + cmd.Flag(config.MetalavaAnnotationsFlags) - validatingNullability := - strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") || - String(d.properties.Validate_nullability_from_list) != "" - - migratingNullability := String(d.properties.Previous_api) != "" - if migratingNullability { - previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api)) - cmd.FlagWithInput("--migrate-nullness ", previousApi) + if params.migratingNullability { + previousApiFiles := android.PathsForModuleSrc(ctx, []string{String(d.properties.Previous_api)}) + cmd.FlagForEachInput("--migrate-nullness ", previousApiFiles) } if s := String(d.properties.Validate_nullability_from_list); s != "" { cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s)) } - if validatingNullability { - d.nullabilityWarningsFile = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_nullability_warnings.txt") - cmd.FlagWithOutput("--nullability-warnings-txt ", d.nullabilityWarningsFile) + if params.validatingNullability { + cmd.FlagWithOutput("--nullability-warnings-txt ", params.nullabilityWarningsFile) } - d.annotationsZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_annotations.zip") - cmd.FlagWithOutput("--extract-annotations ", d.annotationsZip) + cmd.FlagWithOutput("--extract-annotations ", params.annotationsZip) if len(d.properties.Merge_annotations_dirs) != 0 { d.mergeAnnoDirFlags(ctx, cmd) } - // TODO(tnorbye): find owners to fix these warnings when annotation was enabled. - cmd.FlagWithArg("--hide ", "HiddenTypedefConstant"). - FlagWithArg("--hide ", "SuperfluousPrefix"). - FlagWithArg("--hide ", "AnnotationExtraction"). - // b/222738070 - FlagWithArg("--hide ", "BannedThrow"). - // b/223382732 - FlagWithArg("--hide ", "ChangedDefault") + cmd.Flag(config.MetalavaAnnotationsWarningsFlags) } } @@ -377,15 +513,21 @@ func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *a }) } -func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) { +func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, apiVersionsXml android.WritablePath) { var apiVersions android.Path if proptools.Bool(d.properties.Api_levels_annotations_enabled) { - d.apiLevelsGenerationFlags(ctx, cmd) - apiVersions = d.apiVersionsXml + d.apiLevelsGenerationFlags(ctx, cmd, stubsType, apiVersionsXml) + apiVersions = apiVersionsXml } else { ctx.VisitDirectDepsWithTag(metalavaAPILevelsModuleTag, func(m android.Module) { if s, ok := m.(*Droidstubs); ok { - apiVersions = s.apiVersionsXml + if stubsType == Everything { + apiVersions = s.everythingArtifacts.apiVersionsXml + } else if stubsType == Exportable { + apiVersions = s.exportableArtifacts.apiVersionsXml + } else { + ctx.ModuleErrorf("%s stubs type does not generate api-versions.xml file", stubsType.String()) + } } else { ctx.PropertyErrorf("api_levels_module", "module %q is not a droidstubs module", ctx.OtherModuleName(m)) @@ -399,36 +541,87 @@ func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *a } } -func (d *Droidstubs) apiLevelsGenerationFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) { +// AndroidPlusUpdatableJar is the name of some extra jars added into `module-lib` and +// `system-server` directories that contain all the APIs provided by the platform and updatable +// modules because the `android.jar` files do not. See b/337836752. +const AndroidPlusUpdatableJar = "android-plus-updatable.jar" + +func (d *Droidstubs) apiLevelsGenerationFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, apiVersionsXml android.WritablePath) { if len(d.properties.Api_levels_annotations_dirs) == 0 { ctx.PropertyErrorf("api_levels_annotations_dirs", "has to be non-empty if api levels annotations was enabled!") } - d.apiVersionsXml = android.PathForModuleOut(ctx, "metalava", "api-versions.xml") - cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml) + cmd.FlagWithOutput("--generate-api-levels ", apiVersionsXml) filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar") + // TODO: Avoid the duplication of API surfaces, reuse apiScope. + // 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 + apiLevelsSdkType := proptools.StringDefault(d.properties.Api_levels_sdk_type, "public") + switch apiLevelsSdkType { + case "system-server": + sdkDirs = []string{"system-server", "module-lib", "system", "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 + } + + // Construct a pattern to match the appropriate extensions that should be included in the + // generated api-versions.xml file. + // + // Use the first item in the sdkDirs array as that is the sdk type for the target API levels + // being generated but has the advantage over `Api_levels_sdk_type` as it has been validated. + // The exception is for system-server which needs to include module-lib and system-server. That + // is because while system-server extends module-lib the system-server extension directory only + // contains service-* modules which provide system-server APIs it does not list the modules which + // only provide a module-lib, so they have to be included separately. + extensionSurfacesPattern := sdkDirs[0] + if apiLevelsSdkType == "system-server" { + // Take the first two items in sdkDirs, which are system-server and module-lib, and construct + // a pattern that will match either. + extensionSurfacesPattern = strings.Join(sdkDirs[0:2], "|") + } + extensionsPattern := fmt.Sprintf(`/extensions/[0-9]+/(%s)/.*\.jar`, extensionSurfacesPattern) + var dirs []string var extensions_dir string ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) { if t, ok := m.(*ExportedDroiddocDir); ok { - extRegex := regexp.MustCompile(t.dir.String() + `/extensions/[0-9]+/public/.*\.jar`) + extRegex := regexp.MustCompile(t.dir.String() + extensionsPattern) // Grab the first extensions_dir and we find while scanning ExportedDroiddocDir.deps; // ideally this should be read from prebuiltApis.properties.Extensions_* for _, dep := range t.deps { + // Check to see if it matches an extension first. + depBase := dep.Base() if extRegex.MatchString(dep.String()) && d.properties.Extensions_info_file != nil { if extensions_dir == "" { extensions_dir = t.dir.String() + "/extensions" } cmd.Implicit(dep) - } - if dep.Base() == filename { + } else if depBase == filename { + // Check to see if it matches a dessert release for an SDK, e.g. Android, Car, Wear, etc.. cmd.Implicit(dep) - } - if filename != "android.jar" && dep.Base() == "android.jar" { + } else if depBase == AndroidPlusUpdatableJar && d.properties.Extensions_info_file != nil { + // The output api-versions.xml has been requested to include information on SDK + // extensions. That means it also needs to include + // so + // The module-lib and system-server directories should use `android-plus-updatable.jar` + // instead of `android.jar`. See AndroidPlusUpdatableJar for more information. + cmd.Implicit(dep) + } else if filename != "android.jar" && depBase == "android.jar" { // Metalava implicitly searches these patterns: // prebuilts/tools/common/api-versions/android-%/android.jar // prebuilts/sdk/%/public/android.jar @@ -446,29 +639,25 @@ func (d *Droidstubs) apiLevelsGenerationFlags(ctx android.ModuleContext, cmd *an } }) - // 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 "system-server": - sdkDirs = []string{"system-server", "module-lib", "system", "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 - } - + // Generate the list of --android-jar-pattern options. The order matters so the first one which + // matches will be the one that is used for a specific api level.. for _, sdkDir := range sdkDirs { for _, dir := range dirs { - cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, filename)) + addPattern := func(jarFilename string) { + cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, jarFilename)) + } + + if sdkDir == "module-lib" || sdkDir == "system-server" { + // The module-lib and system-server android.jars do not include the updatable modules (as + // doing so in the source would introduce dependency cycles and the prebuilts have to + // match the sources). So, instead an additional `android-plus-updatable.jar` will be used + // that does include the updatable modules and this pattern will match that. This pattern + // is added in addition to the following pattern to decouple this change from the change + // to add the `android-plus-updatable.jar`. + addPattern(AndroidPlusUpdatableJar) + } + + addPattern(filename) } } @@ -483,12 +672,29 @@ func (d *Droidstubs) apiLevelsGenerationFlags(ctx android.ModuleContext, cmd *an } } +func (d *Droidstubs) apiCompatibilityFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType) { + if len(d.Javadoc.properties.Out) > 0 { + ctx.PropertyErrorf("out", "out property may not be combined with check_api") + } + + apiFiles := android.PathsForModuleSrc(ctx, []string{String(d.properties.Check_api.Last_released.Api_file)}) + removedApiFiles := android.PathsForModuleSrc(ctx, []string{String(d.properties.Check_api.Last_released.Removed_api_file)}) + + cmd.FlagForEachInput("--check-compatibility:api:released ", apiFiles) + cmd.FlagForEachInput("--check-compatibility:removed:released ", removedApiFiles) + + baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file) + if baselineFile.Valid() { + cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path()) + } +} + 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, - srcJarList android.Path, bootclasspath, classpath classpath, homeDir android.WritablePath) *android.RuleBuilderCommand { +func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths, + srcJarList android.Path, homeDir android.WritablePath, params stubsCommandConfigParams) *android.RuleBuilderCommand { rule.Command().Text("rm -rf").Flag(homeDir.String()) rule.Command().Text("mkdir -p").Flag(homeDir.String()) @@ -498,87 +704,137 @@ func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersi if metalavaUseRbe(ctx) { rule.Remoteable(android.RemoteRuleSupports{RBE: true}) execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy) + compare := ctx.Config().IsEnvTrue("RBE_METALAVA_COMPARE") + remoteUpdateCache := !ctx.Config().IsEnvFalse("RBE_METALAVA_REMOTE_UPDATE_CACHE") labels := map[string]string{"type": "tool", "name": "metalava"} // TODO: metalava pool rejects these jobs pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "java16") rule.Rewrapper(&remoteexec.REParams{ - Labels: labels, - ExecStrategy: execStrategy, - ToolchainInputs: []string{config.JavaCmd(ctx).String()}, - Platform: map[string]string{remoteexec.PoolKey: pool}, + Labels: labels, + ExecStrategy: execStrategy, + ToolchainInputs: []string{config.JavaCmd(ctx).String()}, + Platform: map[string]string{remoteexec.PoolKey: pool}, + Compare: compare, + NumLocalRuns: 1, + NumRemoteRuns: 1, + NoRemoteUpdateCache: !remoteUpdateCache, }) } cmd.BuiltTool("metalava").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "metalava.jar")). Flag(config.JavacVmFlags). - Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED"). - FlagWithArg("-encoding ", "UTF-8"). - FlagWithArg("-source ", javaVersion.String()). - FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "metalava.rsp"), srcs). + Flag(config.MetalavaAddOpens). + FlagWithArg("--java-source ", params.javaVersion.String()). + FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, fmt.Sprintf("%s.metalava.rsp", params.stubsType.String())), srcs). FlagWithInput("@", srcJarList) - if len(bootclasspath) > 0 { - cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":") - } - - if len(classpath) > 0 { - cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":") + // Metalava does not differentiate between bootclasspath and classpath and has not done so for + // years, so it is unlikely to change any time soon. + combinedPaths := append(([]android.Path)(nil), params.deps.bootClasspath.Paths()...) + combinedPaths = append(combinedPaths, params.deps.classpath.Paths()...) + if len(combinedPaths) > 0 { + cmd.FlagWithInputList("--classpath ", combinedPaths, ":") } - cmd.Flag("--no-banner"). - Flag("--color"). - Flag("--quiet"). - Flag("--format=v2"). - FlagWithArg("--repeat-errors-max ", "10"). - FlagWithArg("--hide ", "UnresolvedImport"). - FlagWithArg("--hide ", "InvalidNullabilityOverride"). - // b/223382732 - FlagWithArg("--hide ", "ChangedDefault") + cmd.Flag(config.MetalavaFlags) return cmd } -func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { - deps := d.Javadoc.collectDeps(ctx) +// Pass flagged apis related flags to metalava. When aconfig_declarations property is not +// defined for a module, simply revert all flagged apis annotations. If aconfig_declarations +// property is defined, apply transformations and only revert the flagged apis that are not +// enabled via release configurations and are not specified in aconfig_declarations +func generateRevertAnnotationArgs(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, aconfigFlagsPaths android.Paths) { - javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), android.SdkContext(d)) + if len(aconfigFlagsPaths) == 0 { + cmd.Flag("--revert-annotation android.annotation.FlaggedApi") + return + } - // Create rule for metalava + releasedFlaggedApisFile := android.PathForModuleOut(ctx, fmt.Sprintf("released-flagged-apis-%s.txt", stubsType.String())) + revertAnnotationsFile := android.PathForModuleOut(ctx, fmt.Sprintf("revert-annotations-%s.txt", stubsType.String())) - srcJarDir := android.PathForModuleOut(ctx, "metalava", "srcjars") + var filterArgs string + switch stubsType { + // No flagged apis specific flags need to be passed to metalava when generating + // everything stubs + case Everything: + return - rule := android.NewRuleBuilder(pctx, ctx) + case Runtime: + filterArgs = "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'" - rule.Sbox(android.PathForModuleOut(ctx, "metalava"), - android.PathForModuleOut(ctx, "metalava.sbox.textproto")). - SandboxInputs() + case Exportable: + // When the build flag RELEASE_EXPORT_RUNTIME_APIS is set to true, apis marked with + // the flagged apis that have read_write permissions are exposed on top of the enabled + // and read_only apis. This is to support local override of flag values at runtime. + if ctx.Config().ReleaseExportRuntimeApis() { + filterArgs = "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'" + } else { + filterArgs = "--filter='state:ENABLED+permission:READ_ONLY'" + } + } + + ctx.Build(pctx, android.BuildParams{ + Rule: gatherReleasedFlaggedApisRule, + Inputs: aconfigFlagsPaths, + Output: releasedFlaggedApisFile, + Description: fmt.Sprintf("%s gather aconfig flags", stubsType), + Args: map[string]string{ + "flags_path": android.JoinPathsWithPrefix(aconfigFlagsPaths, "--cache "), + "filter_args": filterArgs, + }, + }) + + ctx.Build(pctx, android.BuildParams{ + Rule: generateMetalavaRevertAnnotationsRule, + Input: releasedFlaggedApisFile, + Output: revertAnnotationsFile, + Description: fmt.Sprintf("%s revert annotations", stubsType), + }) + cmd.FlagWithInput("@", revertAnnotationsFile) +} + +func (d *Droidstubs) commonMetalavaStubCmd(ctx android.ModuleContext, rule *android.RuleBuilder, + params stubsCommandParams) *android.RuleBuilderCommand { if BoolDefault(d.properties.High_mem, false) { // This metalava run uses lots of memory, restrict the number of metalava jobs that can run in parallel. rule.HighMem() } - generateStubs := BoolDefault(d.properties.Generate_stubs, true) - var stubsDir android.OptionalPath - if generateStubs { - d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-"+"stubs.srcjar") - stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, "metalava", "stubsDir")) - rule.Command().Text("rm -rf").Text(stubsDir.String()) - rule.Command().Text("mkdir -p").Text(stubsDir.String()) + if params.stubConfig.generateStubs { + rule.Command().Text("rm -rf").Text(params.stubsDir.String()) + rule.Command().Text("mkdir -p").Text(params.stubsDir.String()) } - srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars) + srcJarList := zipSyncCmd(ctx, rule, params.srcJarDir, d.Javadoc.srcJars) - homeDir := android.PathForModuleOut(ctx, "metalava", "home") - cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList, - deps.bootClasspath, deps.classpath, homeDir) + homeDir := android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "home") + cmd := metalavaCmd(ctx, rule, d.Javadoc.srcFiles, srcJarList, homeDir, params.stubConfig) cmd.Implicits(d.Javadoc.implicits) - d.stubsFlags(ctx, cmd, stubsDir) + d.stubsFlags(ctx, cmd, params.stubsDir, params.stubConfig.stubsType, params.stubConfig.checkApi) + + if params.stubConfig.writeSdkValues { + d.sdkValuesFlags(ctx, cmd, params.metadataDir) + } + + annotationParams := annotationFlagsParams{ + migratingNullability: params.stubConfig.migratingNullability, + validatingNullability: params.stubConfig.validatingNullability, + nullabilityWarningsFile: params.nullabilityWarningsFile, + annotationsZip: params.annotationsZip, + } - d.annotationsFlags(ctx, cmd) + d.annotationsFlags(ctx, cmd, annotationParams) d.inclusionAnnotationsFlags(ctx, cmd) - d.apiLevelsAnnotationsFlags(ctx, cmd) + d.apiLevelsAnnotationsFlags(ctx, cmd, params.stubConfig.stubsType, params.apiVersionsXml) + + if params.stubConfig.doCheckReleased { + d.apiCompatibilityFlags(ctx, cmd, params.stubConfig.stubsType) + } d.expandArgs(ctx, cmd) @@ -586,36 +842,118 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { cmd.ImplicitOutput(android.PathForModuleGen(ctx, o)) } - // Add options for the other optional tasks: API-lint and check-released. - // We generate separate timestamp files for them. + return cmd +} - doApiLint := false - doCheckReleased := false +// Sandbox rule for generating the everything stubs and other artifacts +func (d *Droidstubs) everythingStubCmd(ctx android.ModuleContext, params stubsCommandConfigParams) { + srcJarDir := android.PathForModuleOut(ctx, Everything.String(), "srcjars") + rule := android.NewRuleBuilder(pctx, ctx) + rule.Sbox(android.PathForModuleOut(ctx, Everything.String()), + android.PathForModuleOut(ctx, "metalava.sbox.textproto")). + SandboxInputs() - // Add API lint options. + var stubsDir android.OptionalPath + if params.generateStubs { + stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, Everything.String(), "stubsDir")) + d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"-"+"stubs.srcjar") + } - if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) { - doApiLint = true + if params.writeSdkValues { + d.everythingArtifacts.metadataDir = android.PathForModuleOut(ctx, Everything.String(), "metadata") + d.everythingArtifacts.metadataZip = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"-metadata.zip") + } - newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since) - if newSince.Valid() { - cmd.FlagWithInput("--api-lint ", newSince.Path()) - } else { - cmd.Flag("--api-lint") + if Bool(d.properties.Annotations_enabled) { + if params.validatingNullability { + d.everythingArtifacts.nullabilityWarningsFile = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"_nullability_warnings.txt") + } + d.everythingArtifacts.annotationsZip = android.PathForModuleOut(ctx, Everything.String(), ctx.ModuleName()+"_annotations.zip") + } + if Bool(d.properties.Api_levels_annotations_enabled) { + d.everythingArtifacts.apiVersionsXml = android.PathForModuleOut(ctx, Everything.String(), "api-versions.xml") + } + + commonCmdParams := stubsCommandParams{ + srcJarDir: srcJarDir, + stubsDir: stubsDir, + stubsSrcJar: d.Javadoc.stubsSrcJar, + metadataDir: d.everythingArtifacts.metadataDir, + apiVersionsXml: d.everythingArtifacts.apiVersionsXml, + nullabilityWarningsFile: d.everythingArtifacts.nullabilityWarningsFile, + annotationsZip: d.everythingArtifacts.annotationsZip, + stubConfig: params, + } + + cmd := d.commonMetalavaStubCmd(ctx, rule, commonCmdParams) + + d.everythingOptionalCmd(ctx, cmd, params.doApiLint, params.doCheckReleased) + + if params.generateStubs { + rule.Command(). + BuiltTool("soong_zip"). + Flag("-write_if_changed"). + Flag("-jar"). + FlagWithOutput("-o ", d.Javadoc.stubsSrcJar). + FlagWithArg("-C ", stubsDir.String()). + FlagWithArg("-D ", stubsDir.String()) + } + + if params.writeSdkValues { + rule.Command(). + BuiltTool("soong_zip"). + Flag("-write_if_changed"). + Flag("-d"). + FlagWithOutput("-o ", d.everythingArtifacts.metadataZip). + FlagWithArg("-C ", d.everythingArtifacts.metadataDir.String()). + FlagWithArg("-D ", d.everythingArtifacts.metadataDir.String()) + } + + // TODO: We don't really need two separate API files, but this is a reminiscence of how + // we used to run metalava separately for API lint and the "last_released" check. Unify them. + if params.doApiLint { + rule.Command().Text("touch").Output(d.apiLintTimestamp) + } + if params.doCheckReleased { + rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp) + } + + // TODO(b/183630617): rewrapper doesn't support restat rules + if !metalavaUseRbe(ctx) { + rule.Restat() + } + + zipSyncCleanupCmd(rule, srcJarDir) + + rule.Build("metalava", "metalava merged") +} + +// Sandbox rule for generating the everything artifacts that are not run by +// default but only run based on the module configurations +func (d *Droidstubs) everythingOptionalCmd(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, doApiLint bool, doCheckReleased bool) { + + // Add API lint options. + treatDocumentationIssuesAsErrors := false + if doApiLint { + var newSince android.Paths + if d.properties.Check_api.Api_lint.New_since != nil { + newSince = android.PathsForModuleSrc(ctx, []string{proptools.String(d.properties.Check_api.Api_lint.New_since)}) } - d.apiLintReport = android.PathForModuleOut(ctx, "metalava", "api_lint_report.txt") + cmd.Flag("--api-lint") + cmd.FlagForEachInput("--api-lint-previous-api ", newSince) + d.apiLintReport = android.PathForModuleOut(ctx, Everything.String(), "api_lint_report.txt") cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) // TODO: Change to ":api-lint" // TODO(b/154317059): Clean up this allowlist by baselining and/or checking in last-released. if d.Name() != "android.car-system-stubs-docs" && d.Name() != "android.car-stubs-docs" { - cmd.Flag("--lints-as-errors") + treatDocumentationIssuesAsErrors = true cmd.Flag("--warnings-as-errors") // Most lints are actually warnings. } baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file) - updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "api_lint_baseline.txt") - d.apiLintTimestamp = android.PathForModuleOut(ctx, "metalava", "api_lint.timestamp") + updatedBaselineOutput := android.PathForModuleOut(ctx, Everything.String(), "api_lint_baseline.txt") + d.apiLintTimestamp = android.PathForModuleOut(ctx, Everything.String(), "api_lint.timestamp") // Note this string includes a special shell quote $' ... ', which decodes the "\n"s. // @@ -655,30 +993,18 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { cmd.FlagWithArg("--error-message:api-lint ", msg) } - // Add "check released" options. (Detect incompatible API changes from the last public release) - - if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") { - doCheckReleased = true - - if len(d.Javadoc.properties.Out) > 0 { - ctx.PropertyErrorf("out", "out property may not be combined with check_api") - } + if !treatDocumentationIssuesAsErrors { + treatDocumentationIssuesAsWarningErrorWhenNew(cmd) + } - apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file)) - removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file)) + // Add "check released" options. (Detect incompatible API changes from the last public release) + if doCheckReleased { baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file) - updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "last_released_baseline.txt") - - d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_last_released_api.timestamp") - - cmd.FlagWithInput("--check-compatibility:api:released ", apiFile) - cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile) - + d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_last_released_api.timestamp") if baselineFile.Valid() { - cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path()) + updatedBaselineOutput := android.PathForModuleOut(ctx, Everything.String(), "last_released_baseline.txt") cmd.FlagWithOutput("--update-baseline:compatibility:released ", updatedBaselineOutput) } - // Note this string includes quote ($' ... '), which decodes the "\n"s. msg := `$'\n******************************\n` + `You have tried to change the API from what has been previously released in\n` + @@ -688,34 +1014,121 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { cmd.FlagWithArg("--error-message:compatibility:released ", msg) } - if generateStubs { + if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") { + // Pass the current API file into metalava so it can use it as the basis for determining how to + // generate the output signature files (both api and removed). + currentApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file)) + cmd.FlagWithInput("--use-same-format-as ", currentApiFile) + } +} + +// HIDDEN_DOCUMENTATION_ISSUES is the set of documentation related issues that should always be +// hidden as they are very noisy and provide little value. +var HIDDEN_DOCUMENTATION_ISSUES = []string{ + "Deprecated", + "IntDef", + "Nullable", +} + +func treatDocumentationIssuesAsWarningErrorWhenNew(cmd *android.RuleBuilderCommand) { + // Treat documentation issues as warnings, but error when new. + cmd.Flag("--error-when-new-category").Flag("Documentation") + + // Hide some documentation issues that generated a lot of noise for little benefit. + cmd.FlagForEachArg("--hide ", HIDDEN_DOCUMENTATION_ISSUES) +} + +// Sandbox rule for generating exportable stubs and other artifacts +func (d *Droidstubs) exportableStubCmd(ctx android.ModuleContext, params stubsCommandConfigParams) { + optionalCmdParams := stubsCommandParams{ + stubConfig: params, + } + + if params.generateStubs { + d.Javadoc.exportableStubsSrcJar = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"-"+"stubs.srcjar") + optionalCmdParams.stubsSrcJar = d.Javadoc.exportableStubsSrcJar + } + + if params.writeSdkValues { + d.exportableArtifacts.metadataZip = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"-metadata.zip") + d.exportableArtifacts.metadataDir = android.PathForModuleOut(ctx, params.stubsType.String(), "metadata") + optionalCmdParams.metadataZip = d.exportableArtifacts.metadataZip + optionalCmdParams.metadataDir = d.exportableArtifacts.metadataDir + } + + if Bool(d.properties.Annotations_enabled) { + if params.validatingNullability { + d.exportableArtifacts.nullabilityWarningsFile = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"_nullability_warnings.txt") + optionalCmdParams.nullabilityWarningsFile = d.exportableArtifacts.nullabilityWarningsFile + } + d.exportableArtifacts.annotationsZip = android.PathForModuleOut(ctx, params.stubsType.String(), ctx.ModuleName()+"_annotations.zip") + optionalCmdParams.annotationsZip = d.exportableArtifacts.annotationsZip + } + if Bool(d.properties.Api_levels_annotations_enabled) { + d.exportableArtifacts.apiVersionsXml = android.PathForModuleOut(ctx, params.stubsType.String(), "api-versions.xml") + optionalCmdParams.apiVersionsXml = d.exportableArtifacts.apiVersionsXml + } + + if params.checkApi || String(d.properties.Api_filename) != "" { + filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt") + d.exportableApiFile = android.PathForModuleOut(ctx, params.stubsType.String(), filename) + } + + if params.checkApi || String(d.properties.Removed_api_filename) != "" { + filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_api.txt") + d.exportableRemovedApiFile = android.PathForModuleOut(ctx, params.stubsType.String(), filename) + } + + d.optionalStubCmd(ctx, optionalCmdParams) +} + +func (d *Droidstubs) optionalStubCmd(ctx android.ModuleContext, params stubsCommandParams) { + + params.srcJarDir = android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "srcjars") + rule := android.NewRuleBuilder(pctx, ctx) + rule.Sbox(android.PathForModuleOut(ctx, params.stubConfig.stubsType.String()), + android.PathForModuleOut(ctx, fmt.Sprintf("metalava_%s.sbox.textproto", params.stubConfig.stubsType.String()))). + SandboxInputs() + + if params.stubConfig.generateStubs { + params.stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "stubsDir")) + } + + cmd := d.commonMetalavaStubCmd(ctx, rule, params) + + generateRevertAnnotationArgs(ctx, cmd, params.stubConfig.stubsType, params.stubConfig.deps.aconfigProtoFiles) + + if params.stubConfig.doApiLint { + // Pass the lint baseline file as an input to resolve the lint errors. + // The exportable stubs generation does not update the lint baseline file. + // Lint baseline file update is handled by the everything stubs + baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file) + if baselineFile.Valid() { + cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path()) + } + } + + // Treat documentation issues as warnings, but error when new. + treatDocumentationIssuesAsWarningErrorWhenNew(cmd) + + if params.stubConfig.generateStubs { rule.Command(). BuiltTool("soong_zip"). Flag("-write_if_changed"). Flag("-jar"). - FlagWithOutput("-o ", d.Javadoc.stubsSrcJar). - FlagWithArg("-C ", stubsDir.String()). - FlagWithArg("-D ", stubsDir.String()) + FlagWithOutput("-o ", params.stubsSrcJar). + FlagWithArg("-C ", params.stubsDir.String()). + FlagWithArg("-D ", params.stubsDir.String()) } - if Bool(d.properties.Write_sdk_values) { - d.metadataZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-metadata.zip") + if params.stubConfig.writeSdkValues { rule.Command(). BuiltTool("soong_zip"). Flag("-write_if_changed"). Flag("-d"). - FlagWithOutput("-o ", d.metadataZip). - FlagWithArg("-C ", d.metadataDir.String()). - FlagWithArg("-D ", d.metadataDir.String()) - } - - // TODO: We don't really need two separate API files, but this is a reminiscence of how - // we used to run metalava separately for API lint and the "last_released" check. Unify them. - if doApiLint { - rule.Command().Text("touch").Output(d.apiLintTimestamp) - } - if doCheckReleased { - rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp) + FlagWithOutput("-o ", params.metadataZip). + FlagWithArg("-C ", params.metadataDir.String()). + FlagWithArg("-D ", params.metadataDir.String()) } // TODO(b/183630617): rewrapper doesn't support restat rules @@ -723,9 +1136,53 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { rule.Restat() } - zipSyncCleanupCmd(rule, srcJarDir) + zipSyncCleanupCmd(rule, params.srcJarDir) - rule.Build("metalava", "metalava merged") + rule.Build(fmt.Sprintf("metalava_%s", params.stubConfig.stubsType.String()), "metalava merged") +} + +func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { + deps := d.Javadoc.collectDeps(ctx) + + javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), android.SdkContext(d)) + generateStubs := BoolDefault(d.properties.Generate_stubs, true) + + // Add options for the other optional tasks: API-lint and check-released. + // We generate separate timestamp files for them. + doApiLint := BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) + doCheckReleased := apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") + + writeSdkValues := Bool(d.properties.Write_sdk_values) + + annotationsEnabled := Bool(d.properties.Annotations_enabled) + + migratingNullability := annotationsEnabled && String(d.properties.Previous_api) != "" + validatingNullability := annotationsEnabled && (strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") || + String(d.properties.Validate_nullability_from_list) != "") + + checkApi := apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") || + apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") + + stubCmdParams := stubsCommandConfigParams{ + javaVersion: javaVersion, + deps: deps, + checkApi: checkApi, + generateStubs: generateStubs, + doApiLint: doApiLint, + doCheckReleased: doCheckReleased, + writeSdkValues: writeSdkValues, + migratingNullability: migratingNullability, + validatingNullability: validatingNullability, + } + stubCmdParams.stubsType = Everything + // Create default (i.e. "everything" stubs) rule for metalava + d.everythingStubCmd(ctx, stubCmdParams) + + // The module generates "exportable" (and "runtime" eventually) stubs regardless of whether + // aconfig_declarations property is defined or not. If the property is not defined, the module simply + // strips all flagged apis to generate the "exportable" stubs + stubCmdParams.stubsType = Exportable + d.exportableStubCmd(ctx, stubCmdParams) if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") { @@ -741,7 +1198,7 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName()) } - d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_current_api.timestamp") + d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_current_api.timestamp") rule := android.NewRuleBuilder(pctx, ctx) @@ -768,6 +1225,12 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { ` m %s-update-current-api\n\n`+ ` To submit the revised current.txt to the main Android repository,\n`+ ` you will need approval.\n`+ + `If your build failed due to stub validation, you can resolve the errors with\n`+ + `either of the two choices above and try re-building the target.\n`+ + `If the mismatch between the stubs and the current.txt is intended,\n`+ + `you can try re-building the target by executing the following command:\n`+ + `m DISABLE_STUB_VALIDATION=true <your build target>.\n`+ + `Note that DISABLE_STUB_VALIDATION=true does not bypass checkapi.\n`+ `******************************\n`, ctx.ModuleName()) rule.Command(). @@ -779,7 +1242,7 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { rule.Build("metalavaCurrentApiCheck", "check current API") - d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "update_current_api.timestamp") + d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, Everything.String(), "update_current_api.timestamp") // update API rule rule = android.NewRuleBuilder(pctx, ctx) @@ -807,14 +1270,14 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { } if String(d.properties.Check_nullability_warnings) != "" { - if d.nullabilityWarningsFile == nil { + if d.everythingArtifacts.nullabilityWarningsFile == nil { ctx.PropertyErrorf("check_nullability_warnings", "Cannot specify check_nullability_warnings unless validating nullability") } checkNullabilityWarnings := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings)) - d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, "metalava", "check_nullability_warnings.timestamp") + d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, Everything.String(), "check_nullability_warnings.timestamp") msg := fmt.Sprintf(`\n******************************\n`+ `The warnings encountered during nullability annotation validation did\n`+ @@ -824,13 +1287,13 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { ` 2. Update the file of expected warnings by running:\n`+ ` cp %s %s\n`+ ` and submitting the updated file as part of your change.`, - d.nullabilityWarningsFile, checkNullabilityWarnings) + d.everythingArtifacts.nullabilityWarningsFile, checkNullabilityWarnings) rule := android.NewRuleBuilder(pctx, ctx) rule.Command(). Text("("). - Text("diff").Input(checkNullabilityWarnings).Input(d.nullabilityWarningsFile). + Text("diff").Input(checkNullabilityWarnings).Input(d.everythingArtifacts.nullabilityWarningsFile). Text("&&"). Text("touch").Output(d.checkNullabilityWarningsTimestamp). Text(") || ("). @@ -840,34 +1303,46 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { rule.Build("nullabilityWarningsCheck", "nullability warnings check") } -} -var _ android.ApiProvider = (*Droidstubs)(nil) - -type bazelJavaApiContributionAttributes struct { - Api bazel.LabelAttribute - Api_surface *string + d.setOutputFiles(ctx) } -func (d *Droidstubs) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) { - props := bazel.BazelTargetModuleProperties{ - Rule_class: "java_api_contribution", - Bzl_load_location: "//build/bazel/rules/apis:java_api_contribution.bzl", +// This method sets the outputFiles property, which is used to set the +// OutputFilesProvider later. +// Droidstubs' tag supports specifying with the stubs type. +// While supporting the pre-existing tags, it also supports tags with +// the stubs type prefix. Some examples are shown below: +// {.annotations.zip} - pre-existing behavior. Returns the path to the +// annotation zip. +// {.exportable} - Returns the path to the exportable stubs src jar. +// {.exportable.annotations.zip} - Returns the path to the exportable +// annotations zip file. +// {.runtime.api_versions.xml} - Runtime stubs does not generate api versions +// xml file. For unsupported combinations, the default everything output file +// is returned. +func (d *Droidstubs) setOutputFiles(ctx android.ModuleContext) { + tagToOutputFileFunc := map[string]func(StubsType) (android.Path, error){ + "": d.StubsSrcJar, + ".docs.zip": d.DocZip, + ".api.txt": d.ApiFilePath, + android.DefaultDistTag: d.ApiFilePath, + ".removed-api.txt": d.RemovedApiFilePath, + ".annotations.zip": d.AnnotationsZip, + ".api_versions.xml": d.ApiVersionsXmlFilePath, } - apiFile := d.properties.Check_api.Current.Api_file - // Do not generate a target if check_api is not set - if apiFile == nil { - return + stubsTypeToPrefix := map[StubsType]string{ + Everything: "", + Exportable: ".exportable", } - attrs := &bazelJavaApiContributionAttributes{ - Api: *bazel.MakeLabelAttribute( - android.BazelLabelForModuleSrcSingle(ctx, proptools.String(apiFile)).Label, - ), - Api_surface: proptools.StringPtr(bazelApiSurfaceName(d.Name())), + for _, tag := range android.SortedKeys(tagToOutputFileFunc) { + for _, stubType := range android.SortedKeys(stubsTypeToPrefix) { + tagWithPrefix := stubsTypeToPrefix[stubType] + tag + outputFile, err := tagToOutputFileFunc[tag](stubType) + if err == nil { + ctx.SetOutputFiles(android.Paths{outputFile}, tagWithPrefix) + } + } } - ctx.CreateBazelTargetModule(props, android.CommonAttributes{ - Name: android.ApiContributionTargetName(ctx.ModuleName()), - }, attrs) } func (d *Droidstubs) createApiContribution(ctx android.DefaultableHookContext) { @@ -895,7 +1370,7 @@ func (d *Droidstubs) createApiContribution(ctx android.DefaultableHookContext) { // use a strict naming convention var ( droidstubsModuleNamingToSdkKind = map[string]android.SdkKind{ - //public is commented out since the core libraries use public in their java_sdk_library names + // public is commented out since the core libraries use public in their java_sdk_library names "intracore": android.SdkIntraCore, "intra.core": android.SdkIntraCore, "system_server": android.SdkSystemServer, @@ -909,28 +1384,6 @@ var ( } ) -// A helper function that returns the api surface of the corresponding java_api_contribution Bazel target -// The api_surface is populated using the naming convention of the droidstubs module. -func bazelApiSurfaceName(name string) string { - // Sort the keys so that longer strings appear first - // Otherwise substrings like system will match both system and system_server - sortedKeys := make([]string, 0) - for key := range droidstubsModuleNamingToSdkKind { - sortedKeys = append(sortedKeys, key) - } - sort.Slice(sortedKeys, func(i, j int) bool { - return len(sortedKeys[i]) > len(sortedKeys[j]) - }) - for _, sortedKey := range sortedKeys { - if strings.Contains(name, sortedKey) { - sdkKind := droidstubsModuleNamingToSdkKind[sortedKey] - return sdkKind.String() + "api" - } - } - // Default is publicapi - return android.SdkPublic.String() + "api" -} - func StubsDefaultsFactory() android.Module { module := &DocDefaults{} @@ -948,11 +1401,31 @@ var _ android.PrebuiltInterface = (*PrebuiltStubsSources)(nil) type PrebuiltStubsSourcesProperties struct { Srcs []string `android:"path"` + + // Name of the source soong module that gets shadowed by this prebuilt + // If unspecified, follows the naming convention that the source module of + // the prebuilt is Name() without "prebuilt_" prefix + Source_module_name *string + + // Non-nil if this prebuilt stub srcs module was dynamically created by a java_sdk_library_import + // The name is the undecorated name of the java_sdk_library as it appears in the blueprint file + // (without any prebuilt_ prefix) + Created_by_java_sdk_library_name *string `blueprint:"mutated"` +} + +func (j *PrebuiltStubsSources) BaseModuleName() string { + return proptools.StringDefault(j.properties.Source_module_name, j.ModuleBase.Name()) +} + +func (j *PrebuiltStubsSources) CreatedByJavaSdkLibraryName() *string { + return j.properties.Created_by_java_sdk_library_name } type PrebuiltStubsSources struct { android.ModuleBase android.DefaultableModuleBase + embeddableInModuleAndImport + prebuilt android.Prebuilt properties PrebuiltStubsSourcesProperties @@ -960,17 +1433,8 @@ type PrebuiltStubsSources struct { stubsSrcJar android.Path } -func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) { - switch tag { - case "": - return android.Paths{p.stubsSrcJar}, nil - default: - return nil, fmt.Errorf("unsupported module reference tag %q", tag) - } -} - -func (d *PrebuiltStubsSources) StubsSrcJar() android.Path { - return d.stubsSrcJar +func (d *PrebuiltStubsSources) StubsSrcJar(_ StubsType) (android.Path, error) { + return d.stubsSrcJar, nil } func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -1007,6 +1471,11 @@ func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleCon rule.Build("zip src", "Create srcjar from prebuilt source") p.stubsSrcJar = outPath } + + ctx.SetOutputFiles(android.Paths{p.stubsSrcJar}, "") + // prebuilt droidstubs does not output "exportable" stubs. + // Output the "everything" stubs srcjar file if the tag is ".exportable". + ctx.SetOutputFiles(android.Paths{p.stubsSrcJar}, ".exportable") } func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt { @@ -1031,6 +1500,7 @@ func PrebuiltStubsSourcesFactory() android.Module { module := &PrebuiltStubsSources{} module.AddProperties(&module.properties) + module.initModuleAndImport(module) android.InitPrebuiltModule(module, &module.properties.Srcs) InitDroiddocModule(module, android.HostAndDeviceSupported) diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go index 7a04d7326..6a14f3645 100644 --- a/java/droidstubs_test.go +++ b/java/droidstubs_test.go @@ -22,6 +22,8 @@ import ( "testing" "android/soong/android" + + "github.com/google/blueprint/proptools" ) func TestDroidstubs(t *testing.T) { @@ -82,7 +84,7 @@ func TestDroidstubs(t *testing.T) { for _, c := range testcases { m := ctx.ModuleForTests(c.moduleName, "android_common") manifest := m.Output("metalava.sbox.textproto") - sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest) + sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, manifest) cmdline := String(sboxProto.Commands[0].Command) android.AssertStringContainsEquals(t, "api-versions generation flag", cmdline, "--generate-api-levels", c.generate_xml) if c.expectedJarFilename != "" { @@ -131,7 +133,7 @@ func getAndroidJarPatternsForDroidstubs(t *testing.T, sdkType string) []string { m := ctx.ModuleForTests("foo-stubs", "android_common") manifest := m.Output("metalava.sbox.textproto") - cmd := String(android.RuleBuilderSboxProtoForTests(t, manifest).Commands[0].Command) + cmd := String(android.RuleBuilderSboxProtoForTests(t, ctx, manifest).Commands[0].Command) r := regexp.MustCompile(`--android-jar-pattern [^ ]+/android.jar`) return r.FindAllString(cmd, -1) } @@ -210,8 +212,8 @@ func TestDroidstubsSandbox(t *testing.T) { t.Errorf("Expected inputs %q, got %q", w, g) } - manifest := android.RuleBuilderSboxProtoForTests(t, m.Output("metalava.sbox.textproto")) - if g, w := manifest.Commands[0].GetCommand(), "reference __SBOX_SANDBOX_DIR__/out/.intermediates/foo/gen/foo.txt"; !strings.Contains(g, w) { + manifest := android.RuleBuilderSboxProtoForTests(t, ctx, m.Output("metalava.sbox.textproto")) + if g, w := manifest.Commands[0].GetCommand(), "reference __SBOX_SANDBOX_DIR__/out/soong/.intermediates/foo/gen/foo.txt"; !strings.Contains(g, w) { t.Errorf("Expected command to contain %q, got %q", w, g) } } @@ -300,53 +302,11 @@ func TestDroidstubsWithSdkExtensions(t *testing.T) { }) m := ctx.ModuleForTests("baz-stubs", "android_common") manifest := m.Output("metalava.sbox.textproto") - cmdline := String(android.RuleBuilderSboxProtoForTests(t, manifest).Commands[0].Command) + cmdline := String(android.RuleBuilderSboxProtoForTests(t, ctx, manifest).Commands[0].Command) android.AssertStringDoesContain(t, "sdk-extensions-root present", cmdline, "--sdk-extensions-root sdk/extensions") android.AssertStringDoesContain(t, "sdk-extensions-info present", cmdline, "--sdk-extensions-info sdk/extensions/info.txt") } -func TestApiSurfaceFromDroidStubsName(t *testing.T) { - testCases := []struct { - desc string - name string - expectedApiSurface string - }{ - { - desc: "Default is publicapi", - name: "mydroidstubs", - expectedApiSurface: "publicapi", - }, - { - desc: "name contains system substring", - name: "mydroidstubs.system.suffix", - expectedApiSurface: "systemapi", - }, - { - desc: "name contains system_server substring", - name: "mydroidstubs.system_server.suffix", - expectedApiSurface: "system-serverapi", - }, - { - desc: "name contains module_lib substring", - name: "mydroidstubs.module_lib.suffix", - expectedApiSurface: "module-libapi", - }, - { - desc: "name contains test substring", - name: "mydroidstubs.test.suffix", - expectedApiSurface: "testapi", - }, - { - desc: "name contains intra.core substring", - name: "mydroidstubs.intra.core.suffix", - expectedApiSurface: "intracoreapi", - }, - } - for _, tc := range testCases { - android.AssertStringEquals(t, tc.desc, tc.expectedApiSurface, bazelApiSurfaceName(tc.name)) - } -} - func TestDroidStubsApiContributionGeneration(t *testing.T) { ctx, _ := testJavaWithFS(t, ` droidstubs { @@ -377,6 +337,7 @@ func TestGeneratedApiContributionVisibilityTest(t *testing.T) { name: "bar", api_surface: "public", api_contributions: ["foo.api.contribution"], + stubs_type: "everything", } ` ctx, _ := testJavaWithFS(t, ` @@ -390,7 +351,7 @@ func TestGeneratedApiContributionVisibilityTest(t *testing.T) { removed_api_file: "A/removed.txt", } }, - visibility: ["//a"], + visibility: ["//a", "//b"], } `, map[string][]byte{ @@ -403,3 +364,101 @@ func TestGeneratedApiContributionVisibilityTest(t *testing.T) { ctx.ModuleForTests("bar", "android_common") } + +func TestAconfigDeclarations(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + }), + android.FixtureMergeMockFs(map[string][]byte{ + "a/A.java": nil, + "a/current.txt": nil, + "a/removed.txt": nil, + }), + ).RunTestWithBp(t, ` + aconfig_declarations { + name: "bar", + package: "com.example.package", + container: "com.android.foo", + srcs: [ + "bar.aconfig", + ], + } + droidstubs { + name: "foo", + srcs: ["a/A.java"], + api_surface: "public", + check_api: { + current: { + api_file: "a/current.txt", + removed_api_file: "a/removed.txt", + } + }, + aconfig_declarations: [ + "bar", + ], + } + `) + + // Check that droidstubs depend on aconfig_declarations + android.AssertBoolEquals(t, "foo expected to depend on bar", + CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar"), true) + + m := result.ModuleForTests("foo", "android_common") + android.AssertStringDoesContain(t, "foo generates revert annotations file", + strings.Join(m.AllOutputs(), ""), "revert-annotations-exportable.txt") + + // revert-annotations.txt passed to exportable stubs generation metalava command + manifest := m.Output("metalava_exportable.sbox.textproto") + cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command) + android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "revert-annotations-exportable.txt") + + android.AssertStringDoesContain(t, "foo generates exportable stubs jar", + strings.Join(m.AllOutputs(), ""), "exportable/foo-stubs.srcjar") +} + +func TestReleaseExportRuntimeApis(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildFlags = map[string]string{ + "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", + } + variables.ExportRuntimeApis = proptools.BoolPtr(true) + }), + android.FixtureMergeMockFs(map[string][]byte{ + "a/A.java": nil, + "a/current.txt": nil, + "a/removed.txt": nil, + }), + ).RunTestWithBp(t, ` + aconfig_declarations { + name: "bar", + package: "com.example.package", + container: "com.android.foo", + srcs: [ + "bar.aconfig", + ], + } + droidstubs { + name: "foo", + srcs: ["a/A.java"], + api_surface: "public", + check_api: { + current: { + api_file: "a/current.txt", + removed_api_file: "a/removed.txt", + } + }, + aconfig_declarations: [ + "bar", + ], + } + `) + + m := result.ModuleForTests("foo", "android_common") + + rule := m.Output("released-flagged-apis-exportable.txt") + exposeWritableApisFilter := "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'" + android.AssertStringEquals(t, "Filter argument expected to contain READ_WRITE permissions", exposeWritableApisFilter, rule.Args["filter_args"]) +} diff --git a/java/fuzz.go b/java/fuzz.go index 2b9e22b8e..d37c55804 100644 --- a/java/fuzz.go +++ b/java/fuzz.go @@ -30,15 +30,19 @@ import ( const ( hostString = "host" targetString = "target" + deviceString = "device" ) +// Any shared libs for these deps will also be packaged +var artDeps = []string{"libdl_android"} + func init() { RegisterJavaFuzzBuildComponents(android.InitRegistrationContext) } func RegisterJavaFuzzBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("java_fuzz", JavaFuzzFactory) - ctx.RegisterSingletonType("java_fuzz_packaging", javaFuzzPackagingFactory) + ctx.RegisterParallelSingletonType("java_fuzz_packaging", javaFuzzPackagingFactory) } type JavaFuzzTest struct { @@ -60,6 +64,8 @@ func JavaFuzzFactory() android.Module { module.Module.properties.Installable = proptools.BoolPtr(true) module.Module.dexpreopter.isTest = true module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true) + module.Module.sourceProperties.Test_only = proptools.BoolPtr(true) + module.Module.sourceProperties.Top_level_test_target = true android.AddLoadHook(module, func(ctx android.LoadHookContext) { disableLinuxBionic := struct { @@ -78,7 +84,18 @@ func JavaFuzzFactory() android.Module { } func (j *JavaFuzzTest) DepsMutator(ctx android.BottomUpMutatorContext) { + if j.Os().Class.String() == deviceString { + j.testProperties.Jni_libs = append(j.testProperties.Jni_libs, artDeps...) + } + if len(j.testProperties.Jni_libs) > 0 { + if j.fuzzPackagedModule.FuzzProperties.Fuzz_config == nil { + config := &fuzz.FuzzConfig{} + j.fuzzPackagedModule.FuzzProperties.Fuzz_config = config + } + // this will be used by the ingestion pipeline to determine the version + // of jazzer to add to the fuzzer package + j.fuzzPackagedModule.FuzzProperties.Fuzz_config.IsJni = proptools.BoolPtr(true) for _, target := range ctx.MultiTargets() { sharedLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"}) ctx.AddFarVariationDependencies(sharedLibVariations, jniLibTag, j.testProperties.Jni_libs...) @@ -106,7 +123,7 @@ func (j *JavaFuzzTest) GenerateAndroidBuildActions(ctx android.ModuleContext) { _, sharedDeps := cc.CollectAllSharedDependencies(ctx) for _, dep := range sharedDeps { - sharedLibInfo := ctx.OtherModuleProvider(dep, cc.SharedLibraryInfoProvider).(cc.SharedLibraryInfo) + sharedLibInfo, _ := android.OtherModuleProvider(ctx, dep, cc.SharedLibraryInfoProvider) if sharedLibInfo.SharedLibrary != nil { arch := "lib" if sharedLibInfo.Target.Arch.ArchType.Multilib == "lib64" { @@ -150,7 +167,9 @@ func (s *javaFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) { } hostOrTargetString := "target" - if javaFuzzModule.Host() { + if javaFuzzModule.Target().HostCross { + hostOrTargetString = "host_cross" + } else if javaFuzzModule.Host() { hostOrTargetString = "host" } @@ -160,7 +179,7 @@ func (s *javaFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) { javaFuzzModule.ApexModuleBase, } - if ok := fuzz.IsValid(fuzzModuleValidator); !ok { + if ok := fuzz.IsValid(ctx, fuzzModuleValidator); !ok { return } @@ -175,11 +194,15 @@ func (s *javaFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) { files = s.PackageArtifacts(ctx, module, javaFuzzModule.fuzzPackagedModule, archDir, builder) // Add .jar - files = append(files, fuzz.FileToZip{javaFuzzModule.implementationJarFile, ""}) + if !javaFuzzModule.Host() { + files = append(files, fuzz.FileToZip{SourceFilePath: javaFuzzModule.implementationJarFile, DestinationPathPrefix: "classes"}) + } + + files = append(files, fuzz.FileToZip{SourceFilePath: javaFuzzModule.outputFile}) // Add jni .so files for _, fPath := range javaFuzzModule.jniFilePaths { - files = append(files, fuzz.FileToZip{fPath, ""}) + files = append(files, fuzz.FileToZip{SourceFilePath: fPath}) } archDirs[archOs], ok = s.BuildZipFile(ctx, module, javaFuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs) diff --git a/java/fuzz_test.go b/java/fuzz_test.go index dd1e96b3e..f29c91327 100644 --- a/java/fuzz_test.go +++ b/java/fuzz_test.go @@ -71,8 +71,8 @@ func TestJavaFuzz(t *testing.T) { } 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") + barOut := filepath.Join("out", "soong", ".intermediates", "bar", osCommonTarget, "javac-header", "bar.jar") + bazOut := filepath.Join("out", "soong", ".intermediates", "baz", osCommonTarget, "javac-header", "baz.jar") android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], barOut) android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], bazOut) diff --git a/java/gen.go b/java/gen.go index 638da255a..1b4f4c7dc 100644 --- a/java/gen.go +++ b/java/gen.go @@ -27,7 +27,6 @@ import ( func init() { pctx.SourcePathVariable("logtagsCmd", "build/make/tools/java-event-log-tags.py") - pctx.SourcePathVariable("mergeLogtagsCmd", "build/make/tools/merge-event-log-tags.py") pctx.SourcePathVariable("logtagsLib", "build/make/tools/event_log_tags.py") } @@ -37,12 +36,6 @@ var ( Command: "$logtagsCmd -o $out $in", CommandDeps: []string{"$logtagsCmd", "$logtagsLib"}, }) - - mergeLogtags = pctx.AndroidStaticRule("mergeLogtags", - blueprint.RuleParams{ - Command: "$mergeLogtagsCmd -o $out $in", - CommandDeps: []string{"$mergeLogtagsCmd", "$logtagsLib"}, - }) ) func genAidl(ctx android.ModuleContext, aidlFiles android.Paths, aidlGlobalFlags string, aidlIndividualFlags map[string]string, deps android.Paths) android.Paths { @@ -129,19 +122,7 @@ func genAidlIncludeFlags(ctx android.PathContext, srcFiles android.Paths, exclud baseDir = filepath.Clean(baseDir) baseDirSeen := android.InList(baseDir, baseDirs) || android.InList(baseDir, excludeDirsStrings) - // For go/bp2build mixed builds, a file may be listed under a - // directory in the Bazel output tree that is symlinked to a - // directory under the android source tree. We should only - // include one copy of this directory so that the AIDL tool - // doesn't find multiple definitions of the same AIDL class. - // This code comes into effect when filegroups are used in mixed builds. - bazelPathPrefix := android.PathForBazelOut(ctx, "").String() - bazelBaseDir, err := filepath.Rel(bazelPathPrefix, baseDir) - bazelBaseDirSeen := err == nil && - android.InList(bazelBaseDir, baseDirs) || - android.InList(bazelBaseDir, excludeDirsStrings) - - if baseDir != "" && !baseDirSeen && !bazelBaseDirSeen { + if baseDir != "" && !baseDirSeen { baseDirs = append(baseDirs, baseDir) } } @@ -190,37 +171,9 @@ func (j *Module) genSources(ctx android.ModuleContext, srcFiles android.Paths, outSrcFiles = append(outSrcFiles, srcJarFiles...) } - return outSrcFiles -} - -func LogtagsSingleton() android.Singleton { - return &logtagsSingleton{} -} - -type logtagsProducer interface { - logtags() android.Paths -} - -func (j *Module) logtags() android.Paths { - return j.logtagsSrcs -} - -var _ logtagsProducer = (*Module)(nil) - -type logtagsSingleton struct{} - -func (l *logtagsSingleton) GenerateBuildActions(ctx android.SingletonContext) { - var allLogtags android.Paths - ctx.VisitAllModules(func(module android.Module) { - if logtags, ok := module.(logtagsProducer); ok { - allLogtags = append(allLogtags, logtags.logtags()...) - } + android.SetProvider(ctx, android.LogtagsProviderKey, &android.LogtagsInfo{ + Logtags: j.logtagsSrcs, }) - ctx.Build(pctx, android.BuildParams{ - Rule: mergeLogtags, - Description: "merge logtags", - Output: android.PathForIntermediates(ctx, "all-event-log-tags.txt"), - Inputs: allLogtags, - }) + return outSrcFiles } diff --git a/java/generated_java_library.go b/java/generated_java_library.go new file mode 100644 index 000000000..d5e6d8fec --- /dev/null +++ b/java/generated_java_library.go @@ -0,0 +1,116 @@ +// Copyright 2023 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package java + +import ( + "android/soong/android" +) + +type GeneratedJavaLibraryModule struct { + Library + callbacks GeneratedJavaLibraryCallbacks + moduleName string + + // true if we've already called DepsMutator. Can't call AddLibrary or AddSharedLibrary + // after DepsMutator. + depsMutatorDone bool +} + +type GeneratedJavaLibraryCallbacks interface { + // Called from inside DepsMutator, gives a chance to AddDependencies + DepsMutator(module *GeneratedJavaLibraryModule, ctx android.BottomUpMutatorContext) + + // Called from inside GenerateAndroidBuildActions. Add the build rules to + // make the srcjar, and return the path to it. + GenerateSourceJarBuildActions(module *GeneratedJavaLibraryModule, ctx android.ModuleContext) (android.Path, android.Path) +} + +// GeneratedJavaLibraryModuleFactory provides a utility for modules that are generated +// source code, including ones outside the java package to build jar files +// from that generated source. +// +// To use GeneratedJavaLibraryModule, call GeneratedJavaLibraryModuleFactory with +// a callback interface and a properties object to add to the module. +// +// These modules will have some properties blocked, and it will be an error if +// modules attempt to set them. See the list of property names in GeneratedAndroidBuildActions +// for the list of those properties. +func GeneratedJavaLibraryModuleFactory(moduleName string, callbacks GeneratedJavaLibraryCallbacks, properties interface{}) android.Module { + module := &GeneratedJavaLibraryModule{ + callbacks: callbacks, + moduleName: moduleName, + } + module.addHostAndDeviceProperties() + module.initModuleAndImport(module) + android.InitApexModule(module) + InitJavaModule(module, android.HostAndDeviceSupported) + if properties != nil { + module.AddProperties(properties) + } + return module +} + +// Add a java shared library as a dependency, as if they had said `libs: [ "name" ]` +func (module *GeneratedJavaLibraryModule) AddSharedLibrary(name string) { + if module.depsMutatorDone { + panic("GeneratedJavaLibraryModule.AddLibrary called after DepsMutator") + } + module.Library.properties.Libs = append(module.Library.properties.Libs, name) +} + +// Add a java shared library as a dependency, as if they had said `libs: [ "name" ]` +func (module *GeneratedJavaLibraryModule) AddStaticLibrary(name string) { + if module.depsMutatorDone { + panic("GeneratedJavaLibraryModule.AddStaticLibrary called after DepsMutator") + } + module.Library.properties.Static_libs = append(module.Library.properties.Static_libs, name) +} + +func (module *GeneratedJavaLibraryModule) DepsMutator(ctx android.BottomUpMutatorContext) { + module.callbacks.DepsMutator(module, ctx) + module.depsMutatorDone = true + module.Library.DepsMutator(ctx) +} + +func checkPropertyEmpty(ctx android.ModuleContext, module *GeneratedJavaLibraryModule, name string, value []string) { + if len(value) != 0 { + ctx.PropertyErrorf(name, "%s not allowed on %s", name, module.moduleName) + } +} + +func (module *GeneratedJavaLibraryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + // These modules are all-generated, so disallow these properties to keep it simple. + // No additional sources + checkPropertyEmpty(ctx, module, "srcs", module.Library.properties.Srcs) + checkPropertyEmpty(ctx, module, "common_srcs", module.Library.properties.Common_srcs) + checkPropertyEmpty(ctx, module, "exclude_srcs", module.Library.properties.Exclude_srcs) + checkPropertyEmpty(ctx, module, "java_resource_dirs", module.Library.properties.Java_resource_dirs) + checkPropertyEmpty(ctx, module, "exclude_java_resource_dirs", module.Library.properties.Exclude_java_resource_dirs) + // Restrict these for no good reason other than to limit the surface area. If there's a + // good use case put them back. + checkPropertyEmpty(ctx, module, "plugins", module.Library.properties.Plugins) + checkPropertyEmpty(ctx, module, "exported_plugins", module.Library.properties.Exported_plugins) + + srcJarPath, cacheOutputPath := module.callbacks.GenerateSourceJarBuildActions(module, ctx) + + module.Library.properties.Generated_srcjars = append(module.Library.properties.Generated_srcjars, srcJarPath) + module.Library.properties.Aconfig_Cache_files = append(module.Library.properties.Aconfig_Cache_files, cacheOutputPath) + module.Library.GenerateAndroidBuildActions(ctx) +} + +// Add a rule to the jarjar renaming rules. See RepackageProviderData. +func (module *GeneratedJavaLibraryModule) AddJarJarRenameRule(original string, renamed string) { + module.addJarJarRenameRule(original, renamed) +} diff --git a/java/generated_java_library_test.go b/java/generated_java_library_test.go new file mode 100644 index 000000000..a5c4be111 --- /dev/null +++ b/java/generated_java_library_test.go @@ -0,0 +1,65 @@ +// 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 ( + "testing" + + "android/soong/android" +) + +func JavaGenLibTestFactory() android.Module { + callbacks := &JavaGenLibTestCallbacks{} + return GeneratedJavaLibraryModuleFactory("test_java_gen_lib", callbacks, &callbacks.properties) +} + +type JavaGenLibTestProperties struct { + Foo string +} + +type JavaGenLibTestCallbacks struct { + properties JavaGenLibTestProperties +} + +func (callbacks *JavaGenLibTestCallbacks) DepsMutator(module *GeneratedJavaLibraryModule, ctx android.BottomUpMutatorContext) { +} + +func (callbacks *JavaGenLibTestCallbacks) GenerateSourceJarBuildActions(module *GeneratedJavaLibraryModule, ctx android.ModuleContext) (android.Path, android.Path) { + return android.PathForOutput(ctx, "blah.srcjar"), android.PathForOutput(ctx, "blah.pb") +} + +func testGenLib(t *testing.T, errorHandler android.FixtureErrorHandler, bp string) *android.TestResult { + return android.GroupFixturePreparers( + PrepareForIntegrationTestWithJava, + android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { + ctx.RegisterModuleType("test_java_gen_lib", JavaGenLibTestFactory) + }), + ). + ExtendWithErrorHandler(errorHandler). + RunTestWithBp(t, bp) +} + +func TestGenLib(t *testing.T) { + bp := ` + test_java_gen_lib { + name: "javagenlibtest", + foo: "bar", // Note: This won't parse if the property didn't get added + } + ` + result := testGenLib(t, android.FixtureExpectsNoErrors, bp) + + javagenlibtest := result.ModuleForTests("javagenlibtest", "android_common").Module().(*GeneratedJavaLibraryModule) + android.AssertPathsEndWith(t, "Generated_srcjars", []string{"/blah.srcjar"}, javagenlibtest.Library.properties.Generated_srcjars) +} diff --git a/java/genrule.go b/java/genrule.go index 208e1f43b..b84225fa5 100644 --- a/java/genrule.go +++ b/java/genrule.go @@ -65,7 +65,6 @@ func GenRuleFactory() android.Module { android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon) android.InitDefaultableModule(module) - android.InitBazelModule(module) return module } @@ -79,7 +78,6 @@ func GenRuleFactoryHost() android.Module { android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon) android.InitDefaultableModule(module) - android.InitBazelModule(module) return module } diff --git a/java/hiddenapi.go b/java/hiddenapi.go index d25096b15..5441a3b6e 100644 --- a/java/hiddenapi.go +++ b/java/hiddenapi.go @@ -44,7 +44,8 @@ type hiddenAPI struct { // // 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 OptionalDexJarPath + bootDexJarPath OptionalDexJarPath + bootDexJarPathErr error // 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 @@ -56,7 +57,10 @@ type hiddenAPI struct { uncompressDexState *bool } -func (h *hiddenAPI) bootDexJar() OptionalDexJarPath { +func (h *hiddenAPI) bootDexJar(ctx android.ModuleErrorfContext) OptionalDexJarPath { + if h.bootDexJarPathErr != nil { + ctx.ModuleErrorf(h.bootDexJarPathErr.Error()) + } return h.bootDexJarPath } @@ -77,7 +81,7 @@ type hiddenAPIModule interface { } type hiddenAPIIntf interface { - bootDexJar() OptionalDexJarPath + bootDexJar(ctx android.ModuleErrorfContext) OptionalDexJarPath classesJars() android.Paths uncompressDex() *bool } @@ -94,7 +98,7 @@ func (h *hiddenAPI) initHiddenAPI(ctx android.ModuleContext, dexJar OptionalDexJ // processing. classesJars := android.Paths{classesJar} ctx.VisitDirectDepsWithTag(hiddenApiAnnotationsTag, func(dep android.Module) { - javaInfo := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo) + javaInfo, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider) classesJars = append(classesJars, javaInfo.ImplementationJars...) }) h.classesJarPaths = classesJars @@ -106,7 +110,7 @@ func (h *hiddenAPI) initHiddenAPI(ctx android.ModuleContext, dexJar OptionalDexJ h.uncompressDexState = uncompressedDexState // If hiddenapi processing is disabled treat this as inactive. - if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") { + if ctx.Config().DisableHiddenApiChecks() { return } @@ -126,6 +130,11 @@ func (h *hiddenAPI) initHiddenAPI(ctx android.ModuleContext, dexJar OptionalDexJ h.active = isModuleInBootClassPath(ctx, module) } +// Store any error encountered during the initialization of hiddenapi structure (e.g. unflagged co-existing prebuilt apexes) +func (h *hiddenAPI) initHiddenAPIError(err error) { + h.bootDexJarPathErr = err +} + func isModuleInBootClassPath(ctx android.BaseModuleContext, module android.Module) bool { // Get the configured platform and apex boot jars. nonApexBootJars := ctx.Config().NonApexBootJars() @@ -305,7 +314,7 @@ func hiddenAPIEncodeDex(ctx android.ModuleContext, dexInput, flagsCSV android.Pa }) if uncompressDex { - TransformZipAlign(ctx, output, encodeRuleOutput) + TransformZipAlign(ctx, output, encodeRuleOutput, nil) } return output diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go index c6176e67c..4144de82b 100644 --- a/java/hiddenapi_modular.go +++ b/java/hiddenapi_modular.go @@ -19,6 +19,7 @@ import ( "strings" "android/soong/android" + "android/soong/dexpreopt" "github.com/google/blueprint" ) @@ -38,10 +39,14 @@ type HiddenAPIScope struct { // The option needed to passed to "hiddenapi list". hiddenAPIListOption string - // The name sof the source stub library modules that contain the API provided by the platform, + // The names of the source stub library modules that contain the API provided by the platform, // i.e. by modules that are not in an APEX. nonUpdatableSourceModule string + // The names of from-text stub library modules that contain the API provided by the platform, + // i.e. by modules that are not in an APEX. + nonUpdatableFromTextModule string + // The names of the prebuilt stub library modules that contain the API provided by the platform, // i.e. by modules that are not in an APEX. nonUpdatablePrebuiltModule string @@ -86,6 +91,9 @@ func (l *HiddenAPIScope) scopeSpecificStubModule(ctx android.BaseModuleContext, if ctx.Config().AlwaysUsePrebuiltSdks() { return l.nonUpdatablePrebuiltModule } else { + if l.nonUpdatableFromTextModule != "" && ctx.Config().BuildFromTextStub() { + return l.nonUpdatableFromTextModule + } return l.nonUpdatableSourceModule } } else { @@ -117,8 +125,9 @@ var ( hiddenAPIListOption: "--test-stub-classpath", }) ModuleLibHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{ - name: "module-lib", - sdkKind: android.SdkModule, + name: "module-lib", + sdkKind: android.SdkModule, + nonUpdatableFromTextModule: "android-non-updatable.stubs.test_module_lib", }) CorePlatformHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{ name: "core-platform", @@ -236,12 +245,22 @@ func hiddenAPIComputeMonolithicStubLibModules(config android.Config) map[*Hidden testStubModules = append(testStubModules, "sdk_test_current_android") } else { // Use stub modules built from source - publicStubModules = append(publicStubModules, android.SdkPublic.JavaLibraryName(config)) - systemStubModules = append(systemStubModules, android.SdkSystem.JavaLibraryName(config)) - testStubModules = append(testStubModules, android.SdkTest.JavaLibraryName(config)) + if config.ReleaseHiddenApiExportableStubs() { + publicStubModules = append(publicStubModules, android.SdkPublic.DefaultExportableJavaLibraryName()) + systemStubModules = append(systemStubModules, android.SdkSystem.DefaultExportableJavaLibraryName()) + testStubModules = append(testStubModules, android.SdkTest.DefaultExportableJavaLibraryName()) + } else { + publicStubModules = append(publicStubModules, android.SdkPublic.DefaultJavaLibraryName()) + systemStubModules = append(systemStubModules, android.SdkSystem.DefaultJavaLibraryName()) + testStubModules = append(testStubModules, android.SdkTest.DefaultJavaLibraryName()) + } } // We do not have prebuilts of the core platform api yet - corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs") + if config.ReleaseHiddenApiExportableStubs() { + corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs.exportable") + } else { + corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs") + } // Allow products to define their own stubs for custom product jars that apps can use. publicStubModules = append(publicStubModules, config.ProductHiddenAPIStubs()...) @@ -280,9 +299,14 @@ func hiddenAPIAddStubLibDependencies(ctx android.BottomUpMutatorContext, apiScop func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android.Module, kind android.SdkKind) android.Path { var dexJar OptionalDexJarPath if sdkLibrary, ok := module.(SdkLibraryDependency); ok { - dexJar = sdkLibrary.SdkApiStubDexJar(ctx, kind) + if ctx.Config().ReleaseHiddenApiExportableStubs() { + dexJar = sdkLibrary.SdkApiExportableStubDexJar(ctx, kind) + } else { + dexJar = sdkLibrary.SdkApiStubDexJar(ctx, kind) + } + } else if j, ok := module.(UsesLibraryDependency); ok { - dexJar = j.DexJarBuildPath() + dexJar = j.DexJarBuildPath(ctx) } else { ctx.ModuleErrorf("dependency %s of module type %s does not support providing a dex jar", module, ctx.OtherModuleType(module)) return nil @@ -350,7 +374,7 @@ 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(stubFlagSubsets) > 0 { + if !ctx.Config().DisableVerifyOverlaps() && len(stubFlagSubsets) > 0 { validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, stubFlagSubsets, HIDDENAPI_STUB_FLAGS_IMPL_FLAGS) @@ -411,122 +435,118 @@ type HiddenAPIFlagFileProperties struct { } } -type hiddenAPIFlagFileCategory struct { - // PropertyName is the name of the property for this category. - PropertyName string +type hiddenAPIFlagFileCategory int - // propertyValueReader retrieves the value of the property for this category from the set of - // properties. - propertyValueReader func(properties *HiddenAPIFlagFileProperties) []string +const ( + // The flag file category for removed members of the API. + // + // This is extracted from HiddenAPIFlagFileCategories as it is needed to add the dex signatures + // list of removed API members that are generated automatically from the removed.txt files provided + // by API stubs. + hiddenAPIFlagFileCategoryRemoved hiddenAPIFlagFileCategory = iota + hiddenAPIFlagFileCategoryUnsupported + hiddenAPIFlagFileCategoryMaxTargetRLowPriority + hiddenAPIFlagFileCategoryMaxTargetQ + hiddenAPIFlagFileCategoryMaxTargetP + hiddenAPIFlagFileCategoryMaxTargetOLowPriority + hiddenAPIFlagFileCategoryBlocked + hiddenAPIFlagFileCategoryUnsupportedPackages +) - // commandMutator adds the appropriate command line options for this category to the supplied - // command - commandMutator func(command *android.RuleBuilderCommand, path android.Path) +func (c hiddenAPIFlagFileCategory) PropertyName() string { + switch c { + case hiddenAPIFlagFileCategoryRemoved: + return "removed" + case hiddenAPIFlagFileCategoryUnsupported: + return "unsupported" + case hiddenAPIFlagFileCategoryMaxTargetRLowPriority: + return "max_target_r_low_priority" + case hiddenAPIFlagFileCategoryMaxTargetQ: + return "max_target_q" + case hiddenAPIFlagFileCategoryMaxTargetP: + return "max_target_p" + case hiddenAPIFlagFileCategoryMaxTargetOLowPriority: + return "max_target_o_low_priority" + case hiddenAPIFlagFileCategoryBlocked: + return "blocked" + case hiddenAPIFlagFileCategoryUnsupportedPackages: + return "unsupported_packages" + default: + panic(fmt.Sprintf("Unknown hidden api flag file category type: %d", c)) + } } -// The flag file category for removed members of the API. -// -// This is extracted from HiddenAPIFlagFileCategories as it is needed to add the dex signatures -// list of removed API members that are generated automatically from the removed.txt files provided -// by API stubs. -var hiddenAPIRemovedFlagFileCategory = &hiddenAPIFlagFileCategory{ - // See HiddenAPIFlagFileProperties.Removed - PropertyName: "removed", - propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { +// propertyValueReader retrieves the value of the property for this category from the set of properties. +func (c hiddenAPIFlagFileCategory) propertyValueReader(properties *HiddenAPIFlagFileProperties) []string { + switch c { + case hiddenAPIFlagFileCategoryRemoved: return properties.Hidden_api.Removed - }, - commandMutator: func(command *android.RuleBuilderCommand, path android.Path) { - command.FlagWithInput("--unsupported ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed") - }, + case hiddenAPIFlagFileCategoryUnsupported: + return properties.Hidden_api.Unsupported + case hiddenAPIFlagFileCategoryMaxTargetRLowPriority: + return properties.Hidden_api.Max_target_r_low_priority + case hiddenAPIFlagFileCategoryMaxTargetQ: + return properties.Hidden_api.Max_target_q + case hiddenAPIFlagFileCategoryMaxTargetP: + return properties.Hidden_api.Max_target_p + case hiddenAPIFlagFileCategoryMaxTargetOLowPriority: + return properties.Hidden_api.Max_target_o_low_priority + case hiddenAPIFlagFileCategoryBlocked: + return properties.Hidden_api.Blocked + case hiddenAPIFlagFileCategoryUnsupportedPackages: + return properties.Hidden_api.Unsupported_packages + default: + panic(fmt.Sprintf("Unknown hidden api flag file category type: %d", c)) + } } -type hiddenAPIFlagFileCategories []*hiddenAPIFlagFileCategory - -func (c hiddenAPIFlagFileCategories) byProperty(name string) *hiddenAPIFlagFileCategory { - for _, category := range c { - if category.PropertyName == name { - return category - } +// commandMutator adds the appropriate command line options for this category to the supplied command +func (c hiddenAPIFlagFileCategory) commandMutator(command *android.RuleBuilderCommand, path android.Path) { + switch c { + case hiddenAPIFlagFileCategoryRemoved: + command.FlagWithInput("--unsupported ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed") + case hiddenAPIFlagFileCategoryUnsupported: + command.FlagWithInput("--unsupported ", path) + case hiddenAPIFlagFileCategoryMaxTargetRLowPriority: + command.FlagWithInput("--max-target-r ", path).FlagWithArg("--tag ", "lo-prio") + case hiddenAPIFlagFileCategoryMaxTargetQ: + command.FlagWithInput("--max-target-q ", path) + case hiddenAPIFlagFileCategoryMaxTargetP: + command.FlagWithInput("--max-target-p ", path) + case hiddenAPIFlagFileCategoryMaxTargetOLowPriority: + command.FlagWithInput("--max-target-o ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio") + case hiddenAPIFlagFileCategoryBlocked: + command.FlagWithInput("--blocked ", path) + case hiddenAPIFlagFileCategoryUnsupportedPackages: + command.FlagWithInput("--unsupported ", path).Flag("--packages ") + default: + panic(fmt.Sprintf("Unknown hidden api flag file category type: %d", c)) } - panic(fmt.Errorf("no category exists with property name %q in %v", name, c)) } +type hiddenAPIFlagFileCategories []hiddenAPIFlagFileCategory + var HiddenAPIFlagFileCategories = hiddenAPIFlagFileCategories{ // See HiddenAPIFlagFileProperties.Unsupported - { - PropertyName: "unsupported", - propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { - return properties.Hidden_api.Unsupported - }, - commandMutator: func(command *android.RuleBuilderCommand, path android.Path) { - command.FlagWithInput("--unsupported ", path) - }, - }, - hiddenAPIRemovedFlagFileCategory, + hiddenAPIFlagFileCategoryUnsupported, + // See HiddenAPIFlagFileProperties.Removed + hiddenAPIFlagFileCategoryRemoved, // See HiddenAPIFlagFileProperties.Max_target_r_low_priority - { - PropertyName: "max_target_r_low_priority", - propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { - return properties.Hidden_api.Max_target_r_low_priority - }, - commandMutator: func(command *android.RuleBuilderCommand, path android.Path) { - command.FlagWithInput("--max-target-r ", path).FlagWithArg("--tag ", "lo-prio") - }, - }, + hiddenAPIFlagFileCategoryMaxTargetRLowPriority, // See HiddenAPIFlagFileProperties.Max_target_q - { - PropertyName: "max_target_q", - propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { - return properties.Hidden_api.Max_target_q - }, - commandMutator: func(command *android.RuleBuilderCommand, path android.Path) { - command.FlagWithInput("--max-target-q ", path) - }, - }, + hiddenAPIFlagFileCategoryMaxTargetQ, // See HiddenAPIFlagFileProperties.Max_target_p - { - PropertyName: "max_target_p", - propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { - return properties.Hidden_api.Max_target_p - }, - commandMutator: func(command *android.RuleBuilderCommand, path android.Path) { - command.FlagWithInput("--max-target-p ", path) - }, - }, + hiddenAPIFlagFileCategoryMaxTargetP, // See HiddenAPIFlagFileProperties.Max_target_o_low_priority - { - PropertyName: "max_target_o_low_priority", - propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { - return properties.Hidden_api.Max_target_o_low_priority - }, - commandMutator: func(command *android.RuleBuilderCommand, path android.Path) { - command.FlagWithInput("--max-target-o ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio") - }, - }, + hiddenAPIFlagFileCategoryMaxTargetOLowPriority, // See HiddenAPIFlagFileProperties.Blocked - { - PropertyName: "blocked", - propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { - return properties.Hidden_api.Blocked - }, - commandMutator: func(command *android.RuleBuilderCommand, path android.Path) { - command.FlagWithInput("--blocked ", path) - }, - }, + hiddenAPIFlagFileCategoryBlocked, // See HiddenAPIFlagFileProperties.Unsupported_packages - { - PropertyName: "unsupported_packages", - propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { - return properties.Hidden_api.Unsupported_packages - }, - commandMutator: func(command *android.RuleBuilderCommand, path android.Path) { - command.FlagWithInput("--unsupported ", path).Flag("--packages ") - }, - }, + hiddenAPIFlagFileCategoryUnsupportedPackages, } // FlagFilesByCategory maps a hiddenAPIFlagFileCategory to the paths to the files in that category. -type FlagFilesByCategory map[*hiddenAPIFlagFileCategory]android.Paths +type FlagFilesByCategory map[hiddenAPIFlagFileCategory]android.Paths // append the supplied flags files to the corresponding category in this map. func (s FlagFilesByCategory) append(other FlagFilesByCategory) { @@ -571,8 +591,7 @@ func (i *HiddenAPIInfo) mergeFromFragmentDeps(ctx android.ModuleContext, fragmen // Merge all the information from the fragments. The fragments form a DAG so it is possible that // this will introduce duplicates so they will be resolved after processing all the fragments. for _, fragment := range fragments { - if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) { - info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo) + if info, ok := android.OtherModuleProvider(ctx, fragment, HiddenAPIInfoProvider); ok { i.TransitiveStubDexJarsByScope.addStubDexJarsByModule(info.TransitiveStubDexJarsByScope) } } @@ -592,7 +611,7 @@ func (i *HiddenAPIInfo) FlagSubset() SignatureCsvSubset { return SignatureCsvSubset{i.FilteredFlagsPath, i.SignaturePatternsPath} } -var HiddenAPIInfoProvider = blueprint.NewProvider(HiddenAPIInfo{}) +var HiddenAPIInfoProvider = blueprint.NewProvider[HiddenAPIInfo]() // HiddenAPIInfoForSdk contains information provided by the hidden API processing for use // by the sdk snapshot. @@ -609,7 +628,7 @@ type HiddenAPIInfoForSdk struct { } // Provides hidden API info for the sdk snapshot. -var HiddenAPIInfoForSdkProvider = blueprint.NewProvider(HiddenAPIInfoForSdk{}) +var HiddenAPIInfoForSdkProvider = blueprint.NewProvider[HiddenAPIInfoForSdk]() // ModuleStubDexJars contains the stub dex jars provided by a single module. // @@ -647,7 +666,7 @@ func (s StubDexJarsByModule) addStubDexJar(ctx android.ModuleContext, module and // public version is provided by the art.module.public.api module. In those cases it is necessary // to treat all those modules as they were the same name, otherwise it will result in multiple // definitions of a single class being passed to hidden API processing which will cause an error. - if name == scope.nonUpdatablePrebuiltModule || name == scope.nonUpdatableSourceModule { + if name == scope.nonUpdatablePrebuiltModule || name == scope.nonUpdatableSourceModule || name == scope.nonUpdatableFromTextModule { // Treat all *android-non-updatable* modules as if they were part of an android-non-updatable // java_sdk_library. // TODO(b/192067200): Remove once android-non-updatable is a java_sdk_library or equivalent. @@ -741,7 +760,7 @@ type HiddenAPIPropertyInfo struct { SplitPackages []string } -var hiddenAPIPropertyInfoProvider = blueprint.NewProvider(HiddenAPIPropertyInfo{}) +var hiddenAPIPropertyInfoProvider = blueprint.NewProvider[HiddenAPIPropertyInfo]() // newHiddenAPIPropertyInfo creates a new initialized HiddenAPIPropertyInfo struct. func newHiddenAPIPropertyInfo() HiddenAPIPropertyInfo { @@ -769,8 +788,7 @@ func (i *HiddenAPIPropertyInfo) extractPackageRulesFromProperties(p *HiddenAPIPa func (i *HiddenAPIPropertyInfo) gatherPropertyInfo(ctx android.ModuleContext, contents []android.Module) { for _, module := range contents { - if ctx.OtherModuleHasProvider(module, hiddenAPIPropertyInfoProvider) { - info := ctx.OtherModuleProvider(module, hiddenAPIPropertyInfoProvider).(HiddenAPIPropertyInfo) + if info, ok := android.OtherModuleProvider(ctx, module, hiddenAPIPropertyInfoProvider); ok { i.FlagFilesByCategory.append(info.FlagFilesByCategory) i.PackagePrefixes = append(i.PackagePrefixes, info.PackagePrefixes...) i.SinglePackages = append(i.SinglePackages, info.SinglePackages...) @@ -941,6 +959,7 @@ type HiddenAPIOutput struct { HiddenAPIFlagOutput // The map from base module name to the path to the encoded boot dex file. + // This field is not available in prebuilt apexes EncodedBootDexFilesByModule bootDexJarByModule } @@ -991,14 +1010,14 @@ func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc st // If available then pass the automatically generated file containing dex signatures of removed // API members to the rule so they can be marked as removed. if generatedRemovedDexSignatures.Valid() { - hiddenAPIRemovedFlagFileCategory.commandMutator(command, generatedRemovedDexSignatures.Path()) + hiddenAPIFlagFileCategoryRemoved.commandMutator(command, generatedRemovedDexSignatures.Path()) } commitChangeForRestat(rule, tempPath, outputPath) // 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(flagSubsets) > 0 { + if !ctx.Config().DisableVerifyOverlaps() && len(flagSubsets) > 0 { validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, flagSubsets, HIDDENAPI_FLAGS_CSV_IMPL_FLAGS) @@ -1236,17 +1255,35 @@ func buildRuleToGenerateRemovedDexSignatures(ctx android.ModuleContext, suffix s rule := android.NewRuleBuilder(pctx, ctx) rule.Command(). BuiltTool("metalava"). - Flag("--no-banner"). + Text("signature-to-dex"). Inputs(removedTxtFiles). - FlagWithOutput("--dex-api ", output) + FlagWithOutput("--out ", output) rule.Build("modular-hiddenapi-removed-dex-signatures"+suffix, "modular hiddenapi removed dex signatures"+suffix) return android.OptionalPathForPath(output) } // extractBootDexJarsFromModules extracts the boot dex jars from the supplied modules. +// This information can come from two mechanisms +// 1. New: Direct deps to _selected_ apexes. The apexes contain a ApexExportsInfo +// 2. Legacy: An edge to java_sdk_library(_import) module. For prebuilt apexes, this serves as a hook and is populated by deapexers of prebuilt apxes +// TODO: b/308174306 - Once all mainline modules have been flagged, drop (2) func extractBootDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule { bootDexJars := bootDexJarByModule{} + + apexNameToApexExportsInfoMap := getApexNameToApexExportsInfoMap(ctx) + // For ART and mainline module jars, query apexNameToApexExportsInfoMap to get the dex file + apexJars := dexpreopt.GetGlobalConfig(ctx).ArtApexJars.AppendList(&dexpreopt.GetGlobalConfig(ctx).ApexBootJars) + for i := 0; i < apexJars.Len(); i++ { + if dex, found := apexNameToApexExportsInfoMap.javaLibraryDexPathOnHost(ctx, apexJars.Apex(i), apexJars.Jar(i)); found { + bootDexJars[apexJars.Jar(i)] = dex + } + } + + // TODO - b/308174306: Drop the legacy mechanism for _, module := range contents { + if _, exists := bootDexJars[android.RemoveOptionalPrebuiltPrefix(module.Name())]; exists { + continue + } hiddenAPIModule := hiddenAPIModuleFromModule(ctx, module) if hiddenAPIModule == nil { continue @@ -1317,7 +1354,7 @@ func extractBootDexInfoFromModules(ctx android.ModuleContext, contents []android // 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() + bootDexJar := module.bootDexJar(ctx) if !bootDexJar.Valid() { fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/boot-dex/%s.jar", module.Name())) handleMissingDexBootFile(ctx, module, fake, bootDexJar.InvalidReason()) @@ -1392,12 +1429,12 @@ func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.M // should not contribute to anything. So, rather than have a missing dex jar cause a Soong // failure defer the error reporting to Ninja. Unless the prebuilt build target is explicitly // built Ninja should never use the dex jar file. - if !isActiveModule(module) { + if !isActiveModule(ctx, module) { return true } if am, ok := module.(android.ApexModule); ok && am.InAnyApex() { - apexInfo := ctx.OtherModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo) + apexInfo, _ := android.OtherModuleProvider(ctx, module, android.ApexInfoProvider) if apexInfo.IsForPlatform() { return true } @@ -1432,7 +1469,9 @@ 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() OptionalDexJarPath }).DexJarBuildPath() + bootDexJar := module.(interface { + DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath + }).DexJarBuildPath(ctx) if !bootDexJar.Valid() { fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/encoded-dex/%s.jar", module.Name())) handleMissingDexBootFile(ctx, module, fake, bootDexJar.InvalidReason()) @@ -1440,13 +1479,3 @@ func retrieveEncodedBootDexJarFromModule(ctx android.ModuleContext, module andro } return bootDexJar.Path() } - -// extractEncodedDexJarsFromModules extracts the encoded dex jars from the supplied modules. -func extractEncodedDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule { - encodedDexJarsByModuleName := bootDexJarByModule{} - for _, module := range contents { - path := retrieveEncodedBootDexJarFromModule(ctx, module) - encodedDexJarsByModuleName.addPath(module, path) - } - return encodedDexJarsByModuleName -} diff --git a/java/hiddenapi_monolithic.go b/java/hiddenapi_monolithic.go index 5956e3c6c..1e30c5f82 100644 --- a/java/hiddenapi_monolithic.go +++ b/java/hiddenapi_monolithic.go @@ -67,9 +67,8 @@ func newMonolithicHiddenAPIInfo(ctx android.ModuleContext, flagFilesByCategory F case *ClasspathFragmentElement: fragment := e.Module() - if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) { - info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo) - monolithicInfo.append(&info) + if info, ok := android.OtherModuleProvider(ctx, fragment, HiddenAPIInfoProvider); ok { + monolithicInfo.append(ctx, fragment, &info) } else { ctx.ModuleErrorf("%s does not provide hidden API information", fragment) } @@ -80,14 +79,25 @@ func newMonolithicHiddenAPIInfo(ctx android.ModuleContext, flagFilesByCategory F } // append appends all the files from the supplied info to the corresponding files in this struct. -func (i *MonolithicHiddenAPIInfo) append(other *HiddenAPIInfo) { +func (i *MonolithicHiddenAPIInfo) append(ctx android.ModuleContext, otherModule android.Module, 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) - i.StubFlagSubsets = append(i.StubFlagSubsets, other.StubFlagSubset()) - i.FlagSubsets = append(i.FlagSubsets, other.FlagSubset()) + apexInfo, ok := android.OtherModuleProvider(ctx, otherModule, android.ApexInfoProvider) + if !ok { + ctx.ModuleErrorf("Could not determine min_version_version of %s\n", otherModule.Name()) + return + } + if apexInfo.MinSdkVersion.LessThanOrEqualTo(android.ApiLevelR) { + // Restrict verify_overlaps to R and older modules. + // The runtime in S does not have the same restriction that + // requires the hiddenapi flags to be generated in a monolithic + // invocation. + i.StubFlagSubsets = append(i.StubFlagSubsets, other.StubFlagSubset()) + i.FlagSubsets = append(i.FlagSubsets, other.FlagSubset()) + } } -var MonolithicHiddenAPIInfoProvider = blueprint.NewProvider(MonolithicHiddenAPIInfo{}) +var MonolithicHiddenAPIInfoProvider = blueprint.NewProvider[MonolithicHiddenAPIInfo]() diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go index 52934a327..7d21b7a61 100644 --- a/java/hiddenapi_singleton.go +++ b/java/hiddenapi_singleton.go @@ -25,7 +25,7 @@ func init() { } func RegisterHiddenApiSingletonComponents(ctx android.RegistrationContext) { - ctx.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory) + ctx.RegisterParallelSingletonType("hiddenapi", hiddenAPISingletonFactory) } var PrepareForTestWithHiddenApiBuildComponents = android.FixtureRegisterWithContext(RegisterHiddenApiSingletonComponents) @@ -121,8 +121,8 @@ type hiddenAPISingleton struct { // hiddenAPI singleton rules func (h *hiddenAPISingleton) GenerateBuildActions(ctx android.SingletonContext) { - // Don't run any hiddenapi rules if UNSAFE_DISABLE_HIDDENAPI_FLAGS=true - if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") { + // Don't run any hiddenapi rules if hiddenapi checks are disabled + if ctx.Config().DisableHiddenApiChecks() { return } @@ -150,6 +150,10 @@ func isModuleInConfiguredList(ctx android.BaseModuleContext, module android.Modu // Strip a prebuilt_ prefix so that this can match a prebuilt module that has not been renamed. name = android.RemoveOptionalPrebuiltPrefix(name) + // Strip the ".impl" suffix, so that the implementation library of the java_sdk_library is + // treated identical to the top level java_sdk_library. + name = strings.TrimSuffix(name, ".impl") + // Ignore any module that is not listed in the boot image configuration. index := configuredBootJars.IndexOfJar(name) if index == -1 { @@ -162,11 +166,11 @@ func isModuleInConfiguredList(ctx android.BaseModuleContext, module android.Modu return false } - apexInfo := ctx.OtherModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo) + apexInfo, _ := android.OtherModuleProvider(ctx, module, android.ApexInfoProvider) // Now match the apex part of the boot image configuration. requiredApex := configuredBootJars.Apex(index) - if requiredApex == "platform" || requiredApex == "system_ext" { + if android.IsConfiguredJarForPlatform(requiredApex) { if len(apexInfo.InApexVariants) != 0 { // A platform variant is required but this is for an apex so ignore it. return false diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go index ef792f970..62297978c 100644 --- a/java/hiddenapi_singleton_test.go +++ b/java/hiddenapi_singleton_test.go @@ -177,10 +177,10 @@ func TestHiddenAPISingletonSdks(t *testing.T) { { name: "testBundled", unbundledBuild: false, - publicStub: "android_stubs_current", - systemStub: "android_system_stubs_current", - testStub: "android_test_stubs_current", - corePlatformStub: "legacy.core.platform.api.stubs", + publicStub: "android_stubs_current_exportable", + systemStub: "android_system_stubs_current_exportable", + testStub: "android_test_stubs_current_exportable", + corePlatformStub: "legacy.core.platform.api.stubs.exportable", preparer: android.GroupFixturePreparers(), }, { name: "testUnbundled", @@ -188,7 +188,7 @@ func TestHiddenAPISingletonSdks(t *testing.T) { publicStub: "sdk_public_current_android", systemStub: "sdk_system_current_android", testStub: "sdk_test_current_android", - corePlatformStub: "legacy.core.platform.api.stubs", + corePlatformStub: "legacy.core.platform.api.stubs.exportable", preparer: PrepareForTestWithPrebuiltsOfCurrentApi, }, } @@ -198,10 +198,22 @@ func TestHiddenAPISingletonSdks(t *testing.T) { hiddenApiFixtureFactory, tc.preparer, prepareForTestWithDefaultPlatformBootclasspath, + // Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi + // file creation. + FixtureConfigureBootJars("platform:foo"), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.Always_use_prebuilt_sdks = proptools.BoolPtr(tc.unbundledBuild) + variables.BuildFlags = map[string]string{ + "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", + } }), - ).RunTest(t) + ).RunTestWithBp(t, ` + java_library { + name: "foo", + srcs: ["a.java"], + compile_dex: true, + } + `) hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common") hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags") @@ -300,7 +312,7 @@ func TestHiddenAPIEncoding_JavaSdkLibrary(t *testing.T) { `) checkDexEncoded := func(t *testing.T, name, unencodedDexJar, encodedDexJar string) { - moduleForTests := result.ModuleForTests(name, "android_common") + moduleForTests := result.ModuleForTests(name+".impl", "android_common") encodeDexRule := moduleForTests.Rule("hiddenAPIEncodeDex") actualUnencodedDexJar := encodeDexRule.Input @@ -309,24 +321,15 @@ 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().Path() + errCtx := moduleErrorfTestCtx{} + exportedDexJar := moduleForTests.Module().(UsesLibraryDependency).DexJarBuildPath(errCtx).Path() android.AssertPathRelativeToTopEquals(t, "encode embedded java_library", encodedDexJar, exportedDexJar) } // The java_library embedded with the java_sdk_library must be dex encoded. t.Run("foo", func(t *testing.T) { - expectedUnencodedDexJar := "out/soong/.intermediates/foo/android_common/aligned/foo.jar" - expectedEncodedDexJar := "out/soong/.intermediates/foo/android_common/hiddenapi/foo.jar" + expectedUnencodedDexJar := "out/soong/.intermediates/foo.impl/android_common/aligned/foo.jar" + expectedEncodedDexJar := "out/soong/.intermediates/foo.impl/android_common/hiddenapi/foo.jar" checkDexEncoded(t, "foo", expectedUnencodedDexJar, expectedEncodedDexJar) }) - - // The dex jar of the child implementation java_library of the java_sdk_library is not currently - // dex encoded. - t.Run("foo.impl", func(t *testing.T) { - fooImpl := result.ModuleForTests("foo.impl", "android_common") - encodeDexRule := fooImpl.MaybeRule("hiddenAPIEncodeDex") - if encodeDexRule.Rule != nil { - t.Errorf("foo.impl is not expected to be encoded") - } - }) } diff --git a/java/jacoco.go b/java/jacoco.go index f8012b852..696a0cc37 100644 --- a/java/jacoco.go +++ b/java/jacoco.go @@ -34,13 +34,11 @@ var ( `${config.Zip2ZipCmd} -i $in -o $strippedJar $stripSpec && ` + `${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.JacocoCLIJar} ` + ` instrument --quiet --dest $tmpDir $strippedJar && ` + - `${config.Ziptime} $tmpJar && ` + `${config.MergeZipsCmd} --ignore-duplicates -j $out $tmpJar $in`, CommandDeps: []string{ "${config.Zip2ZipCmd}", "${config.JavaCmd}", "${config.JacocoCLIJar}", - "${config.Ziptime}", "${config.MergeZipsCmd}", }, }, @@ -55,7 +53,7 @@ func jacocoDepsMutator(ctx android.BottomUpMutatorContext) { } j, ok := ctx.Module().(instrumentable) - if !ctx.Module().Enabled() || !ok { + if !ctx.Module().Enabled(ctx) || !ok { return } diff --git a/java/java.go b/java/java.go index 0eb251815..88b31b586 100644 --- a/java/java.go +++ b/java/java.go @@ -21,11 +21,12 @@ package java import ( "fmt" "path/filepath" + "slices" + "sort" "strings" - "android/soong/bazel" - "android/soong/bazel/cquery" "android/soong/remoteexec" + "android/soong/testing" "github.com/google/blueprint" "github.com/google/blueprint/proptools" @@ -62,6 +63,7 @@ func registerJavaBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("dex_import", DexImportFactory) ctx.RegisterModuleType("java_api_library", ApiLibraryFactory) ctx.RegisterModuleType("java_api_contribution", ApiContributionFactory) + ctx.RegisterModuleType("java_api_contribution_import", ApiContributionImportFactory) // This mutator registers dependencies on dex2oat for modules that should be // dexpreopted. This is done late when the final variants have been @@ -73,19 +75,26 @@ func registerJavaBuildComponents(ctx android.RegistrationContext) { ctx.BottomUp("jacoco_deps", jacocoDepsMutator).Parallel() }) - ctx.RegisterSingletonType("logtags", LogtagsSingleton) - ctx.RegisterSingletonType("kythe_java_extract", kytheExtractJavaFactory) + ctx.RegisterParallelSingletonType("kythe_java_extract", kytheExtractJavaFactory) } func RegisterJavaSdkMemberTypes() { // Register sdk member types. android.RegisterSdkMemberType(javaHeaderLibsSdkMemberType) android.RegisterSdkMemberType(javaLibsSdkMemberType) - android.RegisterSdkMemberType(javaBootLibsSdkMemberType) - android.RegisterSdkMemberType(javaSystemserverLibsSdkMemberType) + android.RegisterSdkMemberType(JavaBootLibsSdkMemberType) + android.RegisterSdkMemberType(JavaSystemserverLibsSdkMemberType) android.RegisterSdkMemberType(javaTestSdkMemberType) } +type StubsLinkType int + +const ( + Unknown StubsLinkType = iota + Stubs + Implementation +) + var ( // Supports adding java header libraries to module_exports and sdk. javaHeaderLibsSdkMemberType = &librarySdkMemberType{ @@ -145,7 +154,7 @@ var ( // either java_libs, or java_header_libs would end up exporting more information than was strictly // necessary. The java_boot_libs property to allow those modules to be exported as part of the // sdk/module_exports without exposing any unnecessary information. - javaBootLibsSdkMemberType = &librarySdkMemberType{ + JavaBootLibsSdkMemberType = &librarySdkMemberType{ android.SdkMemberTypeBase{ PropertyName: "java_boot_libs", SupportsSdk: true, @@ -184,7 +193,7 @@ var ( // 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{ + JavaSystemserverLibsSdkMemberType = &librarySdkMemberType{ android.SdkMemberTypeBase{ PropertyName: "java_systemserver_libs", SupportsSdk: true, @@ -224,17 +233,36 @@ var ( }, "jar_name", "partition", "main_class") ) +type ProguardSpecInfo struct { + // If true, proguard flags files will be exported to reverse dependencies across libs edges + // If false, proguard flags files will only be exported to reverse dependencies across + // static_libs edges. + Export_proguard_flags_files bool + + // TransitiveDepsProguardSpecFiles is a depset of paths to proguard flags files that are exported from + // all transitive deps. This list includes all proguard flags files from transitive static dependencies, + // and all proguard flags files from transitive libs dependencies which set `export_proguard_spec: true`. + ProguardFlagsFiles *android.DepSet[android.Path] + + // implementation detail to store transitive proguard flags files from exporting shared deps + UnconditionallyExportedProguardFlags *android.DepSet[android.Path] +} + +var ProguardSpecInfoProvider = blueprint.NewProvider[ProguardSpecInfo]() + // JavaInfo contains information about a java module for use by modules that depend on it. type JavaInfo struct { // HeaderJars is a list of jars that can be passed as the javac classpath in order to link // against this module. If empty, ImplementationJars should be used instead. HeaderJars android.Paths + RepackagedHeaderJars android.Paths + // set of header jars for all transitive libs deps - TransitiveLibsHeaderJars *android.DepSet + TransitiveLibsHeaderJars *android.DepSet[android.Path] // set of header jars for all transitive static libs deps - TransitiveStaticLibsHeaderJars *android.DepSet + TransitiveStaticLibsHeaderJars *android.DepSet[android.Path] // ImplementationAndResourceJars is a list of jars that contain the implementations of classes // in the module as well as any resources included in the module. @@ -258,6 +286,9 @@ type JavaInfo struct { // SrcJarDeps is a list of paths to depend on when packaging the sources of this module. SrcJarDeps android.Paths + // The source files of this module and all its transitive static dependencies. + TransitiveSrcFiles *android.DepSet[android.Path] + // ExportedPlugins is a list of paths that should be used as annotation processors for any // module that depends on this module. ExportedPlugins android.Paths @@ -273,9 +304,18 @@ type JavaInfo struct { // JacocoReportClassesFile is the path to a jar containing uninstrumented classes that will be // instrumented by jacoco. JacocoReportClassesFile android.Path + + // StubsLinkType provides information about whether the provided jars are stub jars or + // implementation jars. If the provider is set by java_sdk_library, the link type is "unknown" + // and selection between the stub jar vs implementation jar is deferred to SdkLibrary.sdkJars(...) + StubsLinkType StubsLinkType + + // AconfigIntermediateCacheOutputPaths is a path to the cache files collected from the + // java_aconfig_library modules that are statically linked to this module. + AconfigIntermediateCacheOutputPaths android.Paths } -var JavaInfoProvider = blueprint.NewProvider(JavaInfo{}) +var JavaInfoProvider = blueprint.NewProvider[JavaInfo]() // SyspropPublicStubInfo contains info about the sysprop public stub library that corresponds to // the sysprop implementation library. @@ -285,7 +325,7 @@ type SyspropPublicStubInfo struct { JavaInfo JavaInfo } -var SyspropPublicStubInfoProvider = blueprint.NewProvider(SyspropPublicStubInfo{}) +var SyspropPublicStubInfoProvider = blueprint.NewProvider[SyspropPublicStubInfo]() // Methods that need to be implemented for a module that is added to apex java_libs property. type ApexDependency interface { @@ -295,16 +335,11 @@ type ApexDependency interface { // Provides build path and install path to DEX jars. type UsesLibraryDependency interface { - DexJarBuildPath() OptionalDexJarPath + DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath DexJarInstallPath() android.Path ClassLoaderContexts() dexpreopt.ClassLoaderContextMap } -// Provides transitive Proguard flag files to downstream DEX jars. -type LibraryDependency interface { - ExportedProguardFlagFiles() android.Paths -} - // TODO(jungjw): Move this to kythe.go once it's created. type xref interface { XrefJavaFiles() android.Paths @@ -314,6 +349,12 @@ func (j *Module) XrefJavaFiles() android.Paths { return j.kytheFiles } +func (d dependencyTag) PropagateAconfigValidation() bool { + return d.static +} + +var _ android.PropagateAconfigValidationDependencyTag = dependencyTag{} + type dependencyTag struct { blueprint.BaseDependencyTag name string @@ -323,14 +364,16 @@ type dependencyTag struct { // True if the dependency is a toolchain, for example an annotation processor. toolchain bool + + static bool + + installable bool } -// installDependencyTag is a dependency tag that is annotated to cause the installed files of the -// dependency to be installed when the parent module is installed. -type installDependencyTag struct { - blueprint.BaseDependencyTag - android.InstallAlwaysNeededDependencyTag - name string +var _ android.InstallNeededDependencyTag = (*dependencyTag)(nil) + +func (d dependencyTag) InstallDepNeeded() bool { + return d.installable } func (d dependencyTag) LicenseAnnotations() []android.LicenseAnnotation { @@ -362,13 +405,13 @@ func makeUsesLibraryDependencyTag(sdkVersion int, optional bool) usesLibraryDepe } func IsJniDepTag(depTag blueprint.DependencyTag) bool { - return depTag == jniLibTag + return depTag == jniLibTag || depTag == jniInstallTag } var ( dataNativeBinsTag = dependencyTag{name: "dataNativeBins"} dataDeviceBinsTag = dependencyTag{name: "dataDeviceBins"} - staticLibTag = dependencyTag{name: "staticlib"} + staticLibTag = dependencyTag{name: "staticlib", static: true} libTag = dependencyTag{name: "javalib", runtimeLinked: true} sdkLibTag = dependencyTag{name: "sdklib", runtimeLinked: true} java9LibTag = dependencyTag{name: "java9lib", runtimeLinked: true} @@ -390,8 +433,9 @@ var ( syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"} javaApiContributionTag = dependencyTag{name: "java-api-contribution"} depApiSrcsTag = dependencyTag{name: "dep-api-srcs"} - jniInstallTag = installDependencyTag{name: "jni install"} - binaryInstallTag = installDependencyTag{name: "binary install"} + aconfigDeclarationTag = dependencyTag{name: "aconfig-declaration"} + jniInstallTag = dependencyTag{name: "jni install", runtimeLinked: true, installable: true} + binaryInstallTag = dependencyTag{name: "binary install", runtimeLinked: true, installable: true} usesLibReqTag = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, false) usesLibOptTag = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, true) usesLibCompat28OptTag = makeUsesLibraryDependencyTag(28, true) @@ -447,6 +491,7 @@ type jniLib struct { coverageFile android.OptionalPath unstrippedFile android.Path partition string + installPaths android.InstallPaths } func sdkDeps(ctx android.BottomUpMutatorContext, sdkContext android.SdkContext, d dexer) { @@ -456,7 +501,9 @@ func sdkDeps(ctx android.BottomUpMutatorContext, sdkContext android.SdkContext, ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...) ctx.AddVariationDependencies(nil, sdkLibTag, sdkDep.classpath...) if d.effectiveOptimizeEnabled() && sdkDep.hasStandardLibs() { - ctx.AddVariationDependencies(nil, proguardRaiseTag, config.LegacyCorePlatformBootclasspathLibraries...) + ctx.AddVariationDependencies(nil, proguardRaiseTag, + config.LegacyCorePlatformBootclasspathLibraries..., + ) } if d.effectiveOptimizeEnabled() && sdkDep.hasFrameworkLibs() { ctx.AddVariationDependencies(nil, proguardRaiseTag, config.FrameworkLibraries...) @@ -501,6 +548,7 @@ type deps struct { kotlinStdlib android.Paths kotlinAnnotations android.Paths kotlinPlugins android.Paths + aconfigProtoFiles android.Paths disableTurbine bool } @@ -519,6 +567,12 @@ func getJavaVersion(ctx android.ModuleContext, javaVersion string, sdkContext an return normalizeJavaVersion(ctx, javaVersion) } else if ctx.Device() { return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion(ctx)) + } else if ctx.Config().TargetsJava21() { + // Temporary experimental flag to be able to try and build with + // java version 21 options. The flag, if used, just sets Java + // 21 as the default version, leaving any components that + // target an older version intact. + return JAVA_VERSION_21 } else { return JAVA_VERSION_17 } @@ -539,14 +593,17 @@ const ( JAVA_VERSION_9 = 9 JAVA_VERSION_11 = 11 JAVA_VERSION_17 = 17 + JAVA_VERSION_21 = 21 ) func (v javaVersion) String() string { switch v { case JAVA_VERSION_6: - return "1.6" + // Java version 1.6 no longer supported, bumping to 1.8 + return "1.8" case JAVA_VERSION_7: - return "1.7" + // Java version 1.7 no longer supported, bumping to 1.8 + return "1.8" case JAVA_VERSION_8: return "1.8" case JAVA_VERSION_9: @@ -555,6 +612,8 @@ func (v javaVersion) String() string { return "11" case JAVA_VERSION_17: return "17" + case JAVA_VERSION_21: + return "21" default: return "unsupported" } @@ -563,10 +622,12 @@ func (v javaVersion) String() string { func (v javaVersion) StringForKotlinc() string { // $ ./external/kotlinc/bin/kotlinc -jvm-target foo // error: unknown JVM target version: foo - // Supported versions: 1.6, 1.8, 9, 10, 11, 12, 13, 14, 15, 16, 17 + // Supported versions: 1.8, 9, 10, 11, 12, 13, 14, 15, 16, 17 switch v { + case JAVA_VERSION_6: + return "1.8" case JAVA_VERSION_7: - return "1.6" + return "1.8" case JAVA_VERSION_9: return "9" default: @@ -582,9 +643,11 @@ func (v javaVersion) usesJavaModules() bool { func normalizeJavaVersion(ctx android.BaseModuleContext, javaVersion string) javaVersion { switch javaVersion { case "1.6", "6": - return JAVA_VERSION_6 + // Java version 1.6 no longer supported, bumping to 1.8 + return JAVA_VERSION_8 case "1.7", "7": - return JAVA_VERSION_7 + // Java version 1.7 no longer supported, bumping to 1.8 + return JAVA_VERSION_8 case "1.8", "8": return JAVA_VERSION_8 case "1.9", "9": @@ -593,6 +656,8 @@ func normalizeJavaVersion(ctx android.BaseModuleContext, javaVersion string) jav return JAVA_VERSION_11 case "17": return JAVA_VERSION_17 + case "21": + return JAVA_VERSION_21 case "10", "12", "13", "14", "15", "16": ctx.PropertyErrorf("java_version", "Java language level %s is not supported", javaVersion) return JAVA_VERSION_UNSUPPORTED @@ -609,19 +674,17 @@ func normalizeJavaVersion(ctx android.BaseModuleContext, javaVersion string) jav type Library struct { Module - exportedProguardFlagFiles android.Paths + combinedExportedProguardFlagsFile android.Path - InstallMixin func(ctx android.ModuleContext, installPath android.Path) (extraInstallDeps android.Paths) + InstallMixin func(ctx android.ModuleContext, installPath android.Path) (extraInstallDeps android.InstallPaths) } -var _ LibraryDependency = (*Library)(nil) +var _ android.ApexModule = (*Library)(nil) -func (j *Library) ExportedProguardFlagFiles() android.Paths { - return j.exportedProguardFlagFiles +func (j *Library) CheckDepsMinSdkVersion(ctx android.ModuleContext) { + CheckMinSdkVersion(ctx, j) } -var _ android.ApexModule = (*Library)(nil) - // Provides access to the list of permitted packages from apex boot jars. type PermittedPackagesForUpdatableBootJars interface { PermittedPackagesForUpdatableBootJars() []string @@ -633,9 +696,9 @@ func (j *Library) PermittedPackagesForUpdatableBootJars() []string { return j.properties.Permitted_packages } -func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bool { +func shouldUncompressDex(ctx android.ModuleContext, libName string, dexpreopter *dexpreopter) bool { // Store uncompressed (and aligned) any dex files from jars in APEXes. - if apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo); !apexInfo.IsForPlatform() { + if apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider); !apexInfo.IsForPlatform() { return true } @@ -644,10 +707,11 @@ func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bo return true } - // Store uncompressed dex files that are preopted on /system. - if !dexpreopter.dexpreoptDisabled(ctx) && (ctx.Host() || !dexpreopter.odexOnSystemOther(ctx, dexpreopter.installPath)) { + // Store uncompressed dex files that are preopted on /system or /system_other. + if !dexpreopter.dexpreoptDisabled(ctx, libName) { return true } + if ctx.Config().UncompressPrivAppDex() && inList(ctx.ModuleName(), ctx.Config().ModulesLoadedByPrivilegedModules()) { return true @@ -660,40 +724,268 @@ func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bo 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)) + dexer.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), dexpreopter)) } } -func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { +// list of java_library modules that set platform_apis: true +// this property is a no-op for java_library +// TODO (b/215379393): Remove this allowlist +var ( + aospPlatformApiAllowlist = map[string]bool{ + "adservices-test-scenarios": true, + "aidl-cpp-java-test-interface-java": true, + "aidl-test-extras-java": true, + "aidl-test-interface-java": true, + "aidl-test-interface-permission-java": true, + "aidl_test_java_client_permission": true, + "aidl_test_java_client_sdk1": true, + "aidl_test_java_client_sdk29": true, + "aidl_test_java_client": true, + "aidl_test_java_service_permission": true, + "aidl_test_java_service_sdk1": true, + "aidl_test_java_service_sdk29": true, + "aidl_test_java_service": true, + "aidl_test_loggable_interface-java": true, + "aidl_test_nonvintf_parcelable-V1-java": true, + "aidl_test_nonvintf_parcelable-V2-java": true, + "aidl_test_unstable_parcelable-java": true, + "aidl_test_vintf_parcelable-V1-java": true, + "aidl_test_vintf_parcelable-V2-java": true, + "android.aidl.test.trunk-V1-java": true, + "android.aidl.test.trunk-V2-java": true, + "android.frameworks.location.altitude-V1-java": true, + "android.frameworks.location.altitude-V2-java": true, + "android.frameworks.stats-V1-java": true, + "android.frameworks.stats-V2-java": true, + "android.frameworks.stats-V3-java": true, + "android.hardware.authsecret-V1-java": true, + "android.hardware.authsecret-V2-java": true, + "android.hardware.biometrics.common-V1-java": true, + "android.hardware.biometrics.common-V2-java": true, + "android.hardware.biometrics.common-V3-java": true, + "android.hardware.biometrics.common-V4-java": true, + "android.hardware.biometrics.face-V1-java": true, + "android.hardware.biometrics.face-V2-java": true, + "android.hardware.biometrics.face-V3-java": true, + "android.hardware.biometrics.face-V4-java": true, + "android.hardware.biometrics.fingerprint-V1-java": true, + "android.hardware.biometrics.fingerprint-V2-java": true, + "android.hardware.biometrics.fingerprint-V3-java": true, + "android.hardware.biometrics.fingerprint-V4-java": true, + "android.hardware.bluetooth.lmp_event-V1-java": true, + "android.hardware.confirmationui-V1-java": true, + "android.hardware.confirmationui-V2-java": true, + "android.hardware.gatekeeper-V1-java": true, + "android.hardware.gatekeeper-V2-java": true, + "android.hardware.gnss-V1-java": true, + "android.hardware.gnss-V2-java": true, + "android.hardware.gnss-V3-java": true, + "android.hardware.gnss-V4-java": true, + "android.hardware.graphics.common-V1-java": true, + "android.hardware.graphics.common-V2-java": true, + "android.hardware.graphics.common-V3-java": true, + "android.hardware.graphics.common-V4-java": true, + "android.hardware.graphics.common-V5-java": true, + "android.hardware.identity-V1-java": true, + "android.hardware.identity-V2-java": true, + "android.hardware.identity-V3-java": true, + "android.hardware.identity-V4-java": true, + "android.hardware.identity-V5-java": true, + "android.hardware.identity-V6-java": true, + "android.hardware.keymaster-V1-java": true, + "android.hardware.keymaster-V2-java": true, + "android.hardware.keymaster-V3-java": true, + "android.hardware.keymaster-V4-java": true, + "android.hardware.keymaster-V5-java": true, + "android.hardware.oemlock-V1-java": true, + "android.hardware.oemlock-V2-java": true, + "android.hardware.power.stats-V1-java": true, + "android.hardware.power.stats-V2-java": true, + "android.hardware.power.stats-V3-java": true, + "android.hardware.power-V1-java": true, + "android.hardware.power-V2-java": true, + "android.hardware.power-V3-java": true, + "android.hardware.power-V4-java": true, + "android.hardware.power-V5-java": true, + "android.hardware.rebootescrow-V1-java": true, + "android.hardware.rebootescrow-V2-java": true, + "android.hardware.security.authgraph-V1-java": true, + "android.hardware.security.keymint-V1-java": true, + "android.hardware.security.keymint-V2-java": true, + "android.hardware.security.keymint-V3-java": true, + "android.hardware.security.keymint-V4-java": true, + "android.hardware.security.secretkeeper-V1-java": true, + "android.hardware.security.secureclock-V1-java": true, + "android.hardware.security.secureclock-V2-java": true, + "android.hardware.thermal-V1-java": true, + "android.hardware.thermal-V2-java": true, + "android.hardware.threadnetwork-V1-java": true, + "android.hardware.weaver-V1-java": true, + "android.hardware.weaver-V2-java": true, + "android.hardware.weaver-V3-java": true, + "android.security.attestationmanager-java": true, + "android.security.authorization-java": true, + "android.security.compat-java": true, + "android.security.legacykeystore-java": true, + "android.security.maintenance-java": true, + "android.security.metrics-java": true, + "android.system.keystore2-V1-java": true, + "android.system.keystore2-V2-java": true, + "android.system.keystore2-V3-java": true, + "android.system.keystore2-V4-java": true, + "binderReadParcelIface-java": true, + "binderRecordReplayTestIface-java": true, + "car-experimental-api-static-lib": true, + "collector-device-lib-platform": true, + "com.android.car.oem": true, + "com.google.hardware.pixel.display-V10-java": true, + "com.google.hardware.pixel.display-V1-java": true, + "com.google.hardware.pixel.display-V2-java": true, + "com.google.hardware.pixel.display-V3-java": true, + "com.google.hardware.pixel.display-V4-java": true, + "com.google.hardware.pixel.display-V5-java": true, + "com.google.hardware.pixel.display-V6-java": true, + "com.google.hardware.pixel.display-V7-java": true, + "com.google.hardware.pixel.display-V8-java": true, + "com.google.hardware.pixel.display-V9-java": true, + "conscrypt-support": true, + "cts-keystore-test-util": true, + "cts-keystore-user-auth-helper-library": true, + "ctsmediautil": true, + "CtsNetTestsNonUpdatableLib": true, + "DpmWrapper": true, + "flickerlib-apphelpers": true, + "flickerlib-helpers": true, + "flickerlib-parsers": true, + "flickerlib": true, + "hardware.google.bluetooth.ccc-V1-java": true, + "hardware.google.bluetooth.sar-V1-java": true, + "monet": true, + "pixel-power-ext-V1-java": true, + "pixel-power-ext-V2-java": true, + "pixel_stateresidency_provider_aidl_interface-java": true, + "pixel-thermal-ext-V1-java": true, + "protolog-lib": true, + "RkpRegistrationCheck": true, + "rotary-service-javastream-protos": true, + "service_based_camera_extensions": true, + "statsd-helper-test": true, + "statsd-helper": true, + "test-piece-2-V1-java": true, + "test-piece-2-V2-java": true, + "test-piece-3-V1-java": true, + "test-piece-3-V2-java": true, + "test-piece-3-V3-java": true, + "test-piece-4-V1-java": true, + "test-piece-4-V2-java": true, + "test-root-package-V1-java": true, + "test-root-package-V2-java": true, + "test-root-package-V3-java": true, + "test-root-package-V4-java": true, + "testServiceIface-java": true, + "wm-flicker-common-app-helpers": true, + "wm-flicker-common-assertions": true, + "wm-shell-flicker-utils": true, + "wycheproof-keystore": true, + } + + // Union of aosp and internal allowlists + PlatformApiAllowlist = map[string]bool{} +) +func init() { + for k, v := range aospPlatformApiAllowlist { + PlatformApiAllowlist[k] = v + } +} + +func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { + if disableSourceApexVariant(ctx) { + // Prebuilts are active, do not create the installation rules for the source javalib. + // Even though the source javalib is not used, we need to hide it to prevent duplicate installation rules. + // TODO (b/331665856): Implement a principled solution for this. + j.HideFromMake() + } j.provideHiddenAPIPropertyInfo(ctx) j.sdkVersion = j.SdkVersion(ctx) j.minSdkVersion = j.MinSdkVersion(ctx) j.maxSdkVersion = j.MaxSdkVersion(ctx) - apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + // Check min_sdk_version of the transitive dependencies if this module is created from + // java_sdk_library. + if j.overridableProperties.Min_sdk_version != nil && j.SdkLibraryName() != nil { + j.CheckDepsMinSdkVersion(ctx) + } + + // SdkLibrary.GenerateAndroidBuildActions(ctx) sets the stubsLinkType to Unknown. + // If the stubsLinkType has already been set to Unknown, the stubsLinkType should + // not be overridden. + if j.stubsLinkType != Unknown { + if proptools.Bool(j.properties.Is_stubs_module) { + j.stubsLinkType = Stubs + } else { + j.stubsLinkType = Implementation + } + } + + j.stem = proptools.StringDefault(j.overridableProperties.Stem, ctx.ModuleName()) + + proguardSpecInfo := j.collectProguardSpecInfo(ctx) + android.SetProvider(ctx, ProguardSpecInfoProvider, proguardSpecInfo) + exportedProguardFlagsFiles := proguardSpecInfo.ProguardFlagsFiles.ToList() + j.extraProguardFlagsFiles = append(j.extraProguardFlagsFiles, exportedProguardFlagsFiles...) + + combinedExportedProguardFlagFile := android.PathForModuleOut(ctx, "export_proguard_flags") + writeCombinedProguardFlagsFile(ctx, combinedExportedProguardFlagFile, exportedProguardFlagsFiles) + j.combinedExportedProguardFlagsFile = combinedExportedProguardFlagFile + + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) if !apexInfo.IsForPlatform() { j.hideApexVariantFromMake = true } j.checkSdkVersions(ctx) + j.checkHeadersOnly(ctx) if ctx.Device() { + libName := j.Name() + if j.SdkLibraryName() != nil && strings.HasSuffix(libName, ".impl") { + libName = proptools.String(j.SdkLibraryName()) + } j.dexpreopter.installPath = j.dexpreopter.getInstallPath( - ctx, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")) + ctx, libName, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")) j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary setUncompressDex(ctx, &j.dexpreopter, &j.dexer) j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex j.classLoaderContexts = j.usesLibrary.classLoaderContextForUsesLibDeps(ctx) + if j.usesLibrary.shouldDisableDexpreopt { + j.dexpreopter.disableDexpreopt() + } } - j.compile(ctx, nil) + j.compile(ctx, nil, nil, nil) + + // If this module is an impl library created from java_sdk_library, + // install the files under the java_sdk_library module outdir instead of this module outdir. + if j.SdkLibraryName() != nil && strings.HasSuffix(j.Name(), ".impl") { + j.setInstallRules(ctx, proptools.String(j.SdkLibraryName())) + } else { + j.setInstallRules(ctx, ctx.ModuleName()) + } + + android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{ + TestOnly: Bool(j.sourceProperties.Test_only), + TopLevelTarget: j.sourceProperties.Top_level_test_target, + }) - // Collect the module directory for IDE info in java/jdeps.go. - j.modulePaths = append(j.modulePaths, ctx.ModuleDir()) + setOutputFiles(ctx, j.Module) +} + +func (j *Library) setInstallRules(ctx android.ModuleContext, installModuleName string) { + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) - exclusivelyForApex := !apexInfo.IsForPlatform() - if (Bool(j.properties.Installable) || ctx.Host()) && !exclusivelyForApex { - var extraInstallDeps android.Paths + if (Bool(j.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() { + var extraInstallDeps android.InstallPaths if j.InstallMixin != nil { extraInstallDeps = j.InstallMixin(ctx, j.outputFile) } @@ -709,26 +1001,27 @@ func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { if !ctx.Host() { archDir = ctx.DeviceConfig().DeviceArch() } - installDir = android.PathForModuleInstall(ctx, ctx.ModuleName(), archDir) + installDir = android.PathForModuleInstall(ctx, installModuleName, archDir) } else { installDir = android.PathForModuleInstall(ctx, "framework") } j.installFile = ctx.InstallFile(installDir, j.Stem()+".jar", j.outputFile, extraInstallDeps...) } - - j.exportedProguardFlagFiles = append(j.exportedProguardFlagFiles, - android.PathsForModuleSrc(ctx, j.dexProperties.Optimize.Proguard_flags_files)...) - ctx.VisitDirectDeps(func(m android.Module) { - if lib, ok := m.(LibraryDependency); ok && ctx.OtherModuleDependencyTag(m) == staticLibTag { - j.exportedProguardFlagFiles = append(j.exportedProguardFlagFiles, lib.ExportedProguardFlagFiles()...) - } - }) - j.exportedProguardFlagFiles = android.FirstUniquePaths(j.exportedProguardFlagFiles) } func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) { - j.deps(ctx) j.usesLibrary.deps(ctx, false) + j.deps(ctx) + + if j.SdkLibraryName() != nil && strings.HasSuffix(j.Name(), ".impl") { + if dexpreopt.IsDex2oatNeeded(ctx) { + dexpreopt.RegisterToolDeps(ctx) + } + prebuiltSdkLibExists := ctx.OtherModuleExists(android.PrebuiltNameFromSource(proptools.String(j.SdkLibraryName()))) + if prebuiltSdkLibExists && ctx.OtherModuleExists("all_apex_contributions") { + ctx.AddDependency(ctx.Module(), android.AcDepTag, "all_apex_contributions") + } + } } const ( @@ -812,8 +1105,11 @@ func (p *librarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberCo // If the min_sdk_version was set then add the canonical representation of the API level to the // snapshot. - if j.deviceProperties.Min_sdk_version != nil { - canonical := android.ReplaceFinalizedCodenames(ctx.SdkModuleContext().Config(), j.minSdkVersion.String()) + if j.overridableProperties.Min_sdk_version != nil { + canonical, err := android.ReplaceFinalizedCodenames(ctx.SdkModuleContext().Config(), j.minSdkVersion.String()) + if err != nil { + ctx.ModuleErrorf("%s", err) + } p.MinSdkVersion = proptools.StringPtr(canonical) } @@ -886,11 +1182,11 @@ func LibraryFactory() android.Module { module := &Library{} module.addHostAndDeviceProperties() + module.AddProperties(&module.sourceProperties) module.initModuleAndImport(module) android.InitApexModule(module) - android.InitBazelModule(module) InitJavaModule(module, android.HostAndDeviceSupported) return module } @@ -912,7 +1208,6 @@ func LibraryHostFactory() android.Module { module.Module.properties.Installable = proptools.BoolPtr(true) android.InitApexModule(module) - android.InitBazelModule(module) InitJavaModule(module, android.HostSupported) return module } @@ -1063,6 +1358,10 @@ func (j *JavaTestImport) InstallInTestcases() bool { return true } +func (j *TestHost) IsNativeCoverageNeeded(ctx cc.IsNativeCoverageNeededContext) bool { + return ctx.DeviceConfig().NativeCoverageEnabled() +} + func (j *TestHost) addDataDeviceBinsDeps(ctx android.BottomUpMutatorContext) { if len(j.testHostProperties.Data_device_bins_first) > 0 { deviceVariations := ctx.Config().AndroidFirstDeviceTarget.Variations() @@ -1199,10 +1498,23 @@ func (j *TestHost) GenerateAndroidBuildActions(ctx android.ModuleContext) { } j.Test.generateAndroidBuildActionsWithConfig(ctx, configs) + android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{}) + android.SetProvider(ctx, tradefed.BaseTestProviderKey, tradefed.BaseTestProviderData{ + InstalledFiles: j.data, + OutputFile: j.outputFile, + TestConfig: j.testConfig, + RequiredModuleNames: j.RequiredModuleNames(ctx), + TestSuites: j.testProperties.Test_suites, + IsHost: true, + LocalSdkVersion: j.sdkVersion.String(), + IsUnitTest: Bool(j.testProperties.Test_options.Unit_test), + }) } func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) { + checkMinSdkVersionMts(ctx, j.MinSdkVersion(ctx)) j.generateAndroidBuildActionsWithConfig(ctx, nil) + android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{}) } func (j *Test) generateAndroidBuildActionsWithConfig(ctx android.ModuleContext, configs []tradefed.Config) { @@ -1238,7 +1550,7 @@ func (j *Test) generateAndroidBuildActionsWithConfig(ctx android.ModuleContext, }) ctx.VisitDirectDepsWithTag(jniLibTag, func(dep android.Module) { - sharedLibInfo := ctx.OtherModuleProvider(dep, cc.SharedLibraryInfoProvider).(cc.SharedLibraryInfo) + sharedLibInfo, _ := android.OtherModuleProvider(ctx, dep, cc.SharedLibraryInfoProvider) if sharedLibInfo.SharedLibrary != nil { // Copy to an intermediate output directory to append "lib[64]" to the path, // so that it's compatible with the default rpath values. @@ -1355,6 +1667,8 @@ func TestFactory() android.Module { module.Module.properties.Installable = proptools.BoolPtr(true) module.Module.dexpreopter.isTest = true module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true) + module.Module.sourceProperties.Test_only = proptools.BoolPtr(true) + module.Module.sourceProperties.Top_level_test_target = true InitJavaModule(module, android.HostAndDeviceSupported) return module @@ -1370,6 +1684,7 @@ func TestHelperLibraryFactory() android.Module { module.Module.properties.Installable = proptools.BoolPtr(true) module.Module.dexpreopter.isTest = true module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true) + module.Module.sourceProperties.Test_only = proptools.BoolPtr(true) InitJavaModule(module, android.HostAndDeviceSupported) return module @@ -1425,6 +1740,8 @@ func InitTestHost(th *TestHost, installable *bool, testSuites []string, autoGenC th.properties.Installable = installable th.testProperties.Auto_gen_config = autoGenConfig th.testProperties.Test_suites = testSuites + th.sourceProperties.Test_only = proptools.BoolPtr(true) + th.sourceProperties.Top_level_test_target = true } // @@ -1459,6 +1776,8 @@ func (j *Binary) HostToolPath() android.OptionalPath { } func (j *Binary) GenerateAndroidBuildActions(ctx android.ModuleContext) { + j.stem = proptools.StringDefault(j.overridableProperties.Stem, ctx.ModuleName()) + if ctx.Arch().ArchType == android.Common { // Compile the jar if j.binaryProperties.Main_class != nil { @@ -1519,6 +1838,8 @@ func (j *Binary) GenerateAndroidBuildActions(ctx android.ModuleContext) { // libraries. This is verified by TestBinary. j.binaryFile = ctx.InstallExecutable(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName()+ext, j.wrapperFile) + + setOutputFiles(ctx, j.Library.Module) } } @@ -1548,13 +1869,12 @@ func BinaryFactory() android.Module { module := &Binary{} module.addHostAndDeviceProperties() - module.AddProperties(&module.binaryProperties) + module.AddProperties(&module.binaryProperties, &module.sourceProperties) module.Module.properties.Installable = proptools.BoolPtr(true) android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommonFirst) android.InitDefaultableModule(module) - android.InitBazelModule(module) return module } @@ -1573,13 +1893,13 @@ func BinaryHostFactory() android.Module { android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommonFirst) android.InitDefaultableModule(module) - android.InitBazelModule(module) return module } type JavaApiContribution struct { android.ModuleBase android.DefaultableModuleBase + embeddableInModuleAndImport properties struct { // name of the API surface @@ -1595,14 +1915,16 @@ func ApiContributionFactory() android.Module { android.InitAndroidModule(module) android.InitDefaultableModule(module) module.AddProperties(&module.properties) + module.initModuleAndImport(module) return module } type JavaApiImportInfo struct { - ApiFile android.Path + ApiFile android.Path + ApiSurface string } -var JavaApiImportProvider = blueprint.NewProvider(JavaApiImportInfo{}) +var JavaApiImportProvider = blueprint.NewProvider[JavaApiImportInfo]() func (ap *JavaApiContribution) GenerateAndroidBuildActions(ctx android.ModuleContext) { var apiFile android.Path = nil @@ -1610,24 +1932,19 @@ func (ap *JavaApiContribution) GenerateAndroidBuildActions(ctx android.ModuleCon apiFile = android.PathForModuleSrc(ctx, String(apiFileString)) } - ctx.SetProvider(JavaApiImportProvider, JavaApiImportInfo{ - ApiFile: apiFile, + android.SetProvider(ctx, JavaApiImportProvider, JavaApiImportInfo{ + ApiFile: apiFile, + ApiSurface: proptools.String(ap.properties.Api_surface), }) } -type JavaApiLibraryDepsInfo struct { - JavaInfo - StubsSrcJar android.Path -} - -var JavaApiLibraryDepsProvider = blueprint.NewProvider(JavaApiLibraryDepsInfo{}) - type ApiLibrary struct { android.ModuleBase android.DefaultableModuleBase hiddenAPI dexer + embeddableInModuleAndImport properties JavaApiLibraryProperties @@ -1637,6 +1954,12 @@ type ApiLibrary struct { extractedSrcJar android.WritablePath // .dex of stubs, used for hiddenapi processing dexJarFile OptionalDexJarPath + + validationPaths android.Paths + + stubsType StubsType + + aconfigProtoFiles android.Paths } type JavaApiLibraryProperties struct { @@ -1647,11 +1970,6 @@ type JavaApiLibraryProperties struct { // This is a list of Soong modules Api_contributions []string - // list of api.txt files relative to this directory that contribute to the - // API surface. - // This is a list of relative paths - Api_files []string - // List of flags to be passed to the javac compiler to generate jar file Javacflags []string @@ -1663,16 +1981,47 @@ type JavaApiLibraryProperties struct { // merge zipped after metalava invocation Static_libs []string - // Java Api library to provide the full API surface text files and jar file. - // If this property is set, the provided full API surface text files and - // jar file are passed to metalava invocation. - Dep_api_srcs *string + // Java Api library to provide the full API surface stub jar file. + // If this property is set, the stub jar of this module is created by + // extracting the compiled class files provided by the + // full_api_surface_stub module. + Full_api_surface_stub *string + + // Version of previously released API file for compatibility check. + Previous_api *string `android:"path"` + + // java_system_modules module providing the jar to be added to the + // bootclasspath when compiling the stubs. + // The jar will also be passed to metalava as a classpath to + // generate compilable stubs. + System_modules *string + + // If true, the module runs validation on the API signature files provided + // by the modules passed via api_contributions by checking if the files are + // in sync with the source Java files. However, the environment variable + // DISABLE_STUB_VALIDATION has precedence over this property. + Enable_validation *bool + + // Type of stubs the module should generate. Must be one of "everything", "runtime" or + // "exportable". Defaults to "everything". + // - "everything" stubs include all non-flagged apis and flagged apis, regardless of the state + // of the flag. + // - "runtime" stubs include all non-flagged apis and flagged apis that are ENABLED or + // READ_WRITE, and all other flagged apis are stripped. + // - "exportable" stubs include all non-flagged apis and flagged apis that are ENABLED and + // READ_ONLY, and all other flagged apis are stripped. + Stubs_type *string + + // List of aconfig_declarations module names that the stubs generated in this module + // depend on. + Aconfig_declarations []string } func ApiLibraryFactory() android.Module { module := &ApiLibrary{} android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) module.AddProperties(&module.properties) + module.initModuleAndImport(module) android.InitDefaultableModule(module) return module } @@ -1686,7 +2035,8 @@ func (al *ApiLibrary) StubsJar() android.Path { } func metalavaStubCmd(ctx android.ModuleContext, rule *android.RuleBuilder, - srcs android.Paths, homeDir android.WritablePath) *android.RuleBuilderCommand { + srcs android.Paths, homeDir android.WritablePath, + classpath android.Paths) *android.RuleBuilderCommand { rule.Command().Text("rm -rf").Flag(homeDir.String()) rule.Command().Text("mkdir -p").Flag(homeDir.String()) @@ -1710,13 +2060,10 @@ func metalavaStubCmd(ctx android.ModuleContext, rule *android.RuleBuilder, cmd.BuiltTool("metalava").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "metalava.jar")). Flag(config.JavacVmFlags). Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED"). - FlagWithArg("-encoding ", "UTF-8"). FlagWithInputList("--source-files ", srcs, " ") - cmd.Flag("--no-banner"). - Flag("--color"). + cmd.Flag("--color"). Flag("--quiet"). - Flag("--format=v2"). Flag("--include-annotations"). // The flag makes nullability issues as warnings rather than errors by replacing // @Nullable/@NonNull in the listed packages APIs with @RecentlyNullable/@RecentlyNonNull, @@ -1728,6 +2075,18 @@ func metalavaStubCmd(ctx android.ModuleContext, rule *android.RuleBuilder, FlagWithArg("--hide ", "InvalidNullabilityOverride"). FlagWithArg("--hide ", "ChangedDefault") + if len(classpath) == 0 { + // The main purpose of the `--api-class-resolution api` option is to force metalava to ignore + // classes on the classpath when an API file contains missing classes. However, as this command + // does not specify `--classpath` this is not needed for that. However, this is also used as a + // signal to the special metalava code for generating stubs from text files that it needs to add + // some additional items into the API (e.g. default constructors). + cmd.FlagWithArg("--api-class-resolution ", "api") + } else { + cmd.FlagWithArg("--api-class-resolution ", "api:classpath") + cmd.FlagWithInputList("--classpath ", classpath, ":") + } + return cmd } @@ -1745,50 +2104,120 @@ func (al *ApiLibrary) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBui } } -// This method extracts the stub java files from the srcjar file provided from dep_api_srcs module -// and replaces the java stubs generated by invoking metalava in this module. +func (al *ApiLibrary) addValidation(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, validationPaths android.Paths) { + for _, validationPath := range validationPaths { + cmd.Validation(validationPath) + } +} + +// This method extracts the stub class files from the stub jar file provided +// from full_api_surface_stub module instead of compiling the srcjar generated from invoking metalava. // This method is used because metalava can generate compilable from-text stubs only when -// the codebase encompasses all classes listed in the input API text file, but a class can extend +// the codebase encompasses all classes listed in the input API text file, and a class can extend // a class that is not within the same API domain. -func (al *ApiLibrary) extractApiSrcs(ctx android.ModuleContext, rule *android.RuleBuilder, stubsDir android.OptionalPath, depApiSrcsSrcJar android.Path) { - generatedStubsList := android.PathForModuleOut(ctx, "metalava", "sources.txt") +func (al *ApiLibrary) extractApiSrcs(ctx android.ModuleContext, rule *android.RuleBuilder, stubsDir android.OptionalPath, fullApiSurfaceStubJar android.Path) { + classFilesList := android.PathForModuleOut(ctx, "metalava", "classes.txt") unzippedSrcJarDir := android.PathForModuleOut(ctx, "metalava", "unzipDir") rule.Command(). BuiltTool("list_files"). Text(stubsDir.String()). - FlagWithOutput("--out ", generatedStubsList). + FlagWithOutput("--out ", classFilesList). FlagWithArg("--extensions ", ".java"). - FlagWithArg("--root ", unzippedSrcJarDir.String()) + FlagWithArg("--root ", unzippedSrcJarDir.String()). + Flag("--classes") rule.Command(). Text("unzip"). Flag("-q"). - Input(depApiSrcsSrcJar). + Input(fullApiSurfaceStubJar). FlagWithArg("-d ", unzippedSrcJarDir.String()) rule.Command(). BuiltTool("soong_zip"). - Flag("-srcjar"). + Flag("-jar"). Flag("-write_if_changed"). + Flag("-ignore_missing_files"). + Flag("-quiet"). FlagWithArg("-C ", unzippedSrcJarDir.String()). - FlagWithInput("-l ", generatedStubsList). - FlagWithOutput("-o ", al.stubsSrcJar) + FlagWithInput("-l ", classFilesList). + FlagWithOutput("-o ", al.stubsJarWithoutStaticLibs) } func (al *ApiLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { apiContributions := al.properties.Api_contributions + addValidations := !ctx.Config().IsEnvTrue("DISABLE_STUB_VALIDATION") && + !ctx.Config().IsEnvTrue("WITHOUT_CHECK_API") && + proptools.BoolDefault(al.properties.Enable_validation, true) for _, apiContributionName := range apiContributions { ctx.AddDependency(ctx.Module(), javaApiContributionTag, apiContributionName) + + // Add the java_api_contribution module generating droidstubs module + // as dependency when validation adding conditions are met and + // the java_api_contribution module name has ".api.contribution" suffix. + // All droidstubs-generated modules possess the suffix in the name, + // but there is no such guarantee for tests. + if addValidations { + if strings.HasSuffix(apiContributionName, ".api.contribution") { + ctx.AddDependency(ctx.Module(), metalavaCurrentApiTimestampTag, strings.TrimSuffix(apiContributionName, ".api.contribution")) + } else { + ctx.ModuleErrorf("Validation is enabled for module %s but a "+ + "current timestamp provider is not found for the api "+ + "contribution %s", + ctx.ModuleName(), + apiContributionName, + ) + } + } } ctx.AddVariationDependencies(nil, libTag, al.properties.Libs...) ctx.AddVariationDependencies(nil, staticLibTag, al.properties.Static_libs...) - if al.properties.Dep_api_srcs != nil { - ctx.AddVariationDependencies(nil, depApiSrcsTag, String(al.properties.Dep_api_srcs)) + if al.properties.Full_api_surface_stub != nil { + ctx.AddVariationDependencies(nil, depApiSrcsTag, String(al.properties.Full_api_surface_stub)) + } + if al.properties.System_modules != nil { + ctx.AddVariationDependencies(nil, systemModulesTag, String(al.properties.System_modules)) + } + for _, aconfigDeclarationsName := range al.properties.Aconfig_declarations { + ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfigDeclarationsName) + } +} + +// Map where key is the api scope name and value is the int value +// representing the order of the api scope, narrowest to the widest +var scopeOrderMap = AllApiScopes.MapToIndex( + func(s *apiScope) string { return s.name }) + +func (al *ApiLibrary) sortApiFilesByApiScope(ctx android.ModuleContext, srcFilesInfo []JavaApiImportInfo) []JavaApiImportInfo { + for _, srcFileInfo := range srcFilesInfo { + if srcFileInfo.ApiSurface == "" { + ctx.ModuleErrorf("Api surface not defined for the associated api file %s", srcFileInfo.ApiFile) + } + } + sort.Slice(srcFilesInfo, func(i, j int) bool { + return scopeOrderMap[srcFilesInfo[i].ApiSurface] < scopeOrderMap[srcFilesInfo[j].ApiSurface] + }) + + return srcFilesInfo +} + +var validstubsType = []StubsType{Everything, Runtime, Exportable} + +func (al *ApiLibrary) validateProperties(ctx android.ModuleContext) { + if al.properties.Stubs_type == nil { + ctx.ModuleErrorf("java_api_library module type must specify stubs_type property.") + } else { + al.stubsType = StringToStubsType(proptools.String(al.properties.Stubs_type)) + } + + if !android.InList(al.stubsType, validstubsType) { + ctx.PropertyErrorf("stubs_type", "%s is not a valid stubs_type property value. "+ + "Must be one of %s.", proptools.String(al.properties.Stubs_type), validstubsType) } } func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { + al.validateProperties(ctx) rule := android.NewRuleBuilder(pctx, ctx) @@ -1796,81 +2225,110 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { android.PathForModuleOut(ctx, "metalava.sbox.textproto")). SandboxInputs() - var stubsDir android.OptionalPath - stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, "metalava", "stubsDir")) + stubsDir := android.OptionalPathForPath(android.PathForModuleOut(ctx, "metalava", "stubsDir")) rule.Command().Text("rm -rf").Text(stubsDir.String()) rule.Command().Text("mkdir -p").Text(stubsDir.String()) homeDir := android.PathForModuleOut(ctx, "metalava", "home") - var srcFiles android.Paths + var srcFilesInfo []JavaApiImportInfo var classPaths android.Paths var staticLibs android.Paths - var depApiSrcsStubsSrcJar android.Path + var depApiSrcsStubsJar android.Path + var systemModulesPaths android.Paths ctx.VisitDirectDeps(func(dep android.Module) { tag := ctx.OtherModuleDependencyTag(dep) switch tag { case javaApiContributionTag: - provider := ctx.OtherModuleProvider(dep, JavaApiImportProvider).(JavaApiImportInfo) - providerApiFile := provider.ApiFile - if providerApiFile == nil { + provider, _ := android.OtherModuleProvider(ctx, dep, JavaApiImportProvider) + if provider.ApiFile == nil && !ctx.Config().AllowMissingDependencies() { ctx.ModuleErrorf("Error: %s has an empty api file.", dep.Name()) } - srcFiles = append(srcFiles, android.PathForSource(ctx, providerApiFile.String())) + srcFilesInfo = append(srcFilesInfo, provider) case libTag: - provider := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo) + provider, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider) classPaths = append(classPaths, provider.HeaderJars...) case staticLibTag: - provider := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo) + provider, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider) staticLibs = append(staticLibs, provider.HeaderJars...) case depApiSrcsTag: - provider := ctx.OtherModuleProvider(dep, JavaApiLibraryDepsProvider).(JavaApiLibraryDepsInfo) - classPaths = append(classPaths, provider.HeaderJars...) - depApiSrcsStubsSrcJar = provider.StubsSrcJar + provider, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider) + depApiSrcsStubsJar = provider.HeaderJars[0] + case systemModulesTag: + module := dep.(SystemModulesProvider) + systemModulesPaths = append(systemModulesPaths, module.HeaderJars()...) + case metalavaCurrentApiTimestampTag: + if currentApiTimestampProvider, ok := dep.(currentApiTimestampProvider); ok { + al.validationPaths = append(al.validationPaths, currentApiTimestampProvider.CurrentApiTimestamp()) + } + case aconfigDeclarationTag: + if provider, ok := android.OtherModuleProvider(ctx, dep, android.AconfigDeclarationsProviderKey); ok { + al.aconfigProtoFiles = append(al.aconfigProtoFiles, provider.IntermediateCacheOutputPath) + } else if provider, ok := android.OtherModuleProvider(ctx, dep, android.CodegenInfoProvider); ok { + al.aconfigProtoFiles = append(al.aconfigProtoFiles, provider.IntermediateCacheOutputPaths...) + } else { + ctx.ModuleErrorf("Only aconfig_declarations and aconfig_declarations_group "+ + "module type is allowed for flags_packages property, but %s is neither "+ + "of these supported module types", + dep.Name(), + ) + } } }) - // Add the api_files inputs - for _, api := range al.properties.Api_files { - // Use MaybeExistentPathForSource since the api file might not exist during analysis. - // This will be provided by the orchestrator in the combined execution. - srcFiles = append(srcFiles, android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), api)) + srcFilesInfo = al.sortApiFilesByApiScope(ctx, srcFilesInfo) + var srcFiles android.Paths + for _, srcFileInfo := range srcFilesInfo { + srcFiles = append(srcFiles, android.PathForSource(ctx, srcFileInfo.ApiFile.String())) } - if srcFiles == nil { + if srcFiles == nil && !ctx.Config().AllowMissingDependencies() { ctx.ModuleErrorf("Error: %s has an empty api file.", ctx.ModuleName()) } - cmd := metalavaStubCmd(ctx, rule, srcFiles, homeDir) + cmd := metalavaStubCmd(ctx, rule, srcFiles, homeDir, systemModulesPaths) al.stubsFlags(ctx, cmd, stubsDir) + previousApi := String(al.properties.Previous_api) + if previousApi != "" { + previousApiFiles := android.PathsForModuleSrc(ctx, []string{previousApi}) + cmd.FlagForEachInput("--migrate-nullness ", previousApiFiles) + } + + al.addValidation(ctx, cmd, al.validationPaths) + + generateRevertAnnotationArgs(ctx, cmd, al.stubsType, al.aconfigProtoFiles) + al.stubsSrcJar = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-"+"stubs.srcjar") + al.stubsJarWithoutStaticLibs = android.PathForModuleOut(ctx, "metalava", "stubs.jar") + al.stubsJar = android.PathForModuleOut(ctx, ctx.ModuleName(), fmt.Sprintf("%s.jar", ctx.ModuleName())) - if depApiSrcsStubsSrcJar != nil { - al.extractApiSrcs(ctx, rule, stubsDir, depApiSrcsStubsSrcJar) - } else { - rule.Command(). - BuiltTool("soong_zip"). - Flag("-write_if_changed"). - Flag("-jar"). - FlagWithOutput("-o ", al.stubsSrcJar). - FlagWithArg("-C ", stubsDir.String()). - FlagWithArg("-D ", stubsDir.String()) + if depApiSrcsStubsJar != nil { + al.extractApiSrcs(ctx, rule, stubsDir, depApiSrcsStubsJar) } + rule.Command(). + BuiltTool("soong_zip"). + Flag("-write_if_changed"). + Flag("-jar"). + FlagWithOutput("-o ", al.stubsSrcJar). + FlagWithArg("-C ", stubsDir.String()). + FlagWithArg("-D ", stubsDir.String()) - rule.Build("metalava", "metalava merged") + rule.Build("metalava", "metalava merged text") - al.stubsJarWithoutStaticLibs = android.PathForModuleOut(ctx, ctx.ModuleName(), "stubs.jar") - al.stubsJar = android.PathForModuleOut(ctx, ctx.ModuleName(), fmt.Sprintf("%s.jar", ctx.ModuleName())) + if depApiSrcsStubsJar == nil { + var flags javaBuilderFlags + flags.javaVersion = getStubsJavaVersion() + flags.javacFlags = strings.Join(al.properties.Javacflags, " ") + flags.classpath = classpath(classPaths) + flags.bootClasspath = classpath(systemModulesPaths) - var flags javaBuilderFlags - flags.javaVersion = getStubsJavaVersion() - flags.javacFlags = strings.Join(al.properties.Javacflags, " ") - flags.classpath = classpath(classPaths) + annoSrcJar := android.PathForModuleOut(ctx, ctx.ModuleName(), "anno.srcjar") - TransformJavaToClasses(ctx, al.stubsJarWithoutStaticLibs, 0, android.Paths{}, - android.Paths{al.stubsSrcJar}, flags, android.Paths{}) + TransformJavaToClasses(ctx, al.stubsJarWithoutStaticLibs, 0, android.Paths{}, + android.Paths{al.stubsSrcJar}, annoSrcJar, flags, android.Paths{}) + } builder := android.NewRuleBuilder(pctx, ctx) builder.Command(). @@ -1888,7 +2346,7 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { classesJar: al.stubsJar, jarName: ctx.ModuleName() + ".jar", } - dexOutputFile := al.dexer.compileDex(ctx, dexParams) + dexOutputFile, _ := al.dexer.compileDex(ctx, dexParams) uncompressed := true al.initHiddenAPI(ctx, makeDexJarPathFromPath(dexOutputFile), al.stubsJar, &uncompressed) dexOutputFile = al.hiddenAPIEncodeDex(ctx, dexOutputFile) @@ -1896,22 +2354,17 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { ctx.Phony(ctx.ModuleName(), al.stubsJar) - ctx.SetProvider(JavaInfoProvider, JavaInfo{ + android.SetProvider(ctx, JavaInfoProvider, JavaInfo{ HeaderJars: android.PathsIfNonNil(al.stubsJar), ImplementationAndResourcesJars: android.PathsIfNonNil(al.stubsJar), ImplementationJars: android.PathsIfNonNil(al.stubsJar), AidlIncludeDirs: android.Paths{}, - }) - - ctx.SetProvider(JavaApiLibraryDepsProvider, JavaApiLibraryDepsInfo{ - JavaInfo: JavaInfo{ - HeaderJars: android.PathsIfNonNil(al.stubsJar), - }, - StubsSrcJar: al.stubsSrcJar, + StubsLinkType: Stubs, + // No aconfig libraries on api libraries }) } -func (al *ApiLibrary) DexJarBuildPath() OptionalDexJarPath { +func (al *ApiLibrary) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath { return al.dexJarFile } @@ -1964,6 +2417,9 @@ type ImportProperties struct { // List of shared java libs that this module has dependencies to Libs []string + // List of static java libs that this module has dependencies to + Static_libs []string + // List of files to remove from the jar file(s) Exclude_files []string @@ -1981,13 +2437,25 @@ type ImportProperties struct { // that depend on this module, as well as to aidl for this module. Export_include_dirs []string } + + // Name of the source soong module that gets shadowed by this prebuilt + // If unspecified, follows the naming convention that the source module of + // the prebuilt is Name() without "prebuilt_" prefix + Source_module_name *string + + // Non-nil if this java_import module was dynamically created by a java_sdk_library_import + // The name is the undecorated name of the java_sdk_library as it appears in the blueprint file + // (without any prebuilt_ prefix) + Created_by_java_sdk_library_name *string `blueprint:"mutated"` + + // Property signifying whether the module provides stubs jar or not. + Is_stubs_module *bool } type Import struct { android.ModuleBase android.DefaultableModuleBase android.ApexModuleBase - android.BazelModuleBase prebuilt android.Prebuilt // Functionality common to Module and Import. @@ -2001,16 +2469,20 @@ type Import struct { // output file containing classes.dex and resources dexJarFile OptionalDexJarPath + dexJarFileErr error dexJarInstallFile android.Path - combinedClasspathFile android.Path - classLoaderContexts dexpreopt.ClassLoaderContextMap - exportAidlIncludeDirs android.Paths + combinedImplementationFile android.Path + combinedHeaderFile android.Path + classLoaderContexts dexpreopt.ClassLoaderContextMap + exportAidlIncludeDirs android.Paths hideApexVariantFromMake bool sdkVersion android.SdkSpec minSdkVersion android.ApiLevel + + stubsLinkType StubsLinkType } var _ PermittedPackagesForUpdatableBootJars = (*Import)(nil) @@ -2054,12 +2526,20 @@ func (j *Import) PrebuiltSrcs() []string { return j.properties.Jars } +func (j *Import) BaseModuleName() string { + return proptools.StringDefault(j.properties.Source_module_name, j.ModuleBase.Name()) +} + func (j *Import) Name() string { return j.prebuilt.Name(j.ModuleBase.Name()) } func (j *Import) Stem() string { - return proptools.StringDefault(j.properties.Stem, j.ModuleBase.Name()) + return proptools.StringDefault(j.properties.Stem, j.BaseModuleName()) +} + +func (j *Import) CreatedByJavaSdkLibraryName() *string { + return j.properties.Created_by_java_sdk_library_name } func (a *Import) JacocoReportClassesFile() android.Path { @@ -2079,6 +2559,7 @@ func (j *Import) setStrictUpdatabilityLinting(bool) { func (j *Import) DepsMutator(ctx android.BottomUpMutatorContext) { ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...) + ctx.AddVariationDependencies(nil, staticLibTag, j.properties.Static_libs...) if ctx.Device() && Bool(j.dexProperties.Compile_dex) { sdkDeps(ctx, android.SdkContext(j), j.dexer) @@ -2086,49 +2567,46 @@ func (j *Import) DepsMutator(ctx android.BottomUpMutatorContext) { } func (j *Import) commonBuildActions(ctx android.ModuleContext) { - //TODO(b/231322772) these should come from Bazel once available j.sdkVersion = j.SdkVersion(ctx) j.minSdkVersion = j.MinSdkVersion(ctx) - if !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform() { + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) + if !apexInfo.IsForPlatform() { j.hideApexVariantFromMake = true } if ctx.Windows() { j.HideFromMake() } + + if proptools.Bool(j.properties.Is_stubs_module) { + j.stubsLinkType = Stubs + } else { + j.stubsLinkType = Implementation + } } func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.commonBuildActions(ctx) - jars := android.PathsForModuleSrc(ctx, j.properties.Jars) - - jarName := j.Stem() + ".jar" - outputFile := android.PathForModuleOut(ctx, "combined", jarName) - TransformJarsToJar(ctx, outputFile, "for prebuilts", jars, android.OptionalPath{}, - false, j.properties.Exclude_files, j.properties.Exclude_dirs) - if Bool(j.properties.Jetifier) { - inputFile := outputFile - outputFile = android.PathForModuleOut(ctx, "jetifier", jarName) - TransformJetifier(ctx, outputFile, inputFile) - } - j.combinedClasspathFile = outputFile j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap) var flags javaBuilderFlags j.collectTransitiveHeaderJars(ctx) + var staticJars android.Paths + var staticHeaderJars android.Paths ctx.VisitDirectDeps(func(module android.Module) { tag := ctx.OtherModuleDependencyTag(module) - if ctx.OtherModuleHasProvider(module, JavaInfoProvider) { - dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo) + if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok { switch tag { case libTag, sdkLibTag: flags.classpath = append(flags.classpath, dep.HeaderJars...) flags.dexClasspath = append(flags.dexClasspath, dep.HeaderJars...) case staticLibTag: flags.classpath = append(flags.classpath, dep.HeaderJars...) + staticJars = append(staticJars, dep.ImplementationAndResourcesJars...) + staticHeaderJars = append(staticHeaderJars, dep.HeaderJars...) case bootClasspathTag: flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars...) } @@ -2142,6 +2620,50 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { addCLCFromDep(ctx, module, j.classLoaderContexts) }) + jars := android.PathsForModuleSrc(ctx, j.properties.Jars) + jarName := j.Stem() + ".jar" + + // Always pass the input jars to TransformJarsToJar, even if there is only a single jar, we need the output + // file of the module to be named jarName. + outputFile := android.PathForModuleOut(ctx, "combined", jarName) + implementationJars := append(slices.Clone(jars), staticJars...) + TransformJarsToJar(ctx, outputFile, "combine prebuilt implementation jars", implementationJars, android.OptionalPath{}, + false, j.properties.Exclude_files, j.properties.Exclude_dirs) + + // If no dependencies have separate header jars then there is no need to create a separate + // header jar for this module. + reuseImplementationJarAsHeaderJar := slices.Equal(staticJars, staticHeaderJars) + + var headerOutputFile android.ModuleOutPath + if reuseImplementationJarAsHeaderJar { + headerOutputFile = outputFile + } else { + headerJars := append(slices.Clone(jars), staticHeaderJars...) + headerOutputFile = android.PathForModuleOut(ctx, "turbine-combined", jarName) + TransformJarsToJar(ctx, headerOutputFile, "combine prebuilt header jars", headerJars, android.OptionalPath{}, + false, j.properties.Exclude_files, j.properties.Exclude_dirs) + } + + if Bool(j.properties.Jetifier) { + inputFile := outputFile + outputFile = android.PathForModuleOut(ctx, "jetifier", jarName) + TransformJetifier(ctx, outputFile, inputFile) + + if !reuseImplementationJarAsHeaderJar { + headerInputFile := headerOutputFile + headerOutputFile = android.PathForModuleOut(ctx, "jetifier-headers", jarName) + TransformJetifier(ctx, headerOutputFile, headerInputFile) + } else { + headerOutputFile = outputFile + } + } + + // Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource. + // Also strip the relative path from the header output file so that the reuseImplementationJarAsHeaderJar check + // in a module that depends on this module considers them equal. + j.combinedHeaderFile = headerOutputFile.WithoutRel() + j.combinedImplementationFile = outputFile.WithoutRel() + j.maybeInstall(ctx, jarName, outputFile) j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs) @@ -2149,21 +2671,25 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { if ctx.Device() { // If this is a variant created for a prebuilt_apex then use the dex implementation jar // obtained from the associated deapexer module. - ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + ai, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) if ai.ForPrebuiltApex { // Get the path of the dex implementation jar from the `deapexer` module. - di := android.FindDeapexerProviderForModule(ctx) - if di == nil { - return // An error has been reported by FindDeapexerProviderForModule. + di, err := android.FindDeapexerProviderForModule(ctx) + if err != nil { + // An error was found, possibly due to multiple apexes in the tree that export this library + // Defer the error till a client tries to call DexJarBuildPath + j.dexJarFileErr = err + j.initHiddenAPIError(err) + return } - dexJarFileApexRootRelative := apexRootRelativePathToJavaLib(j.BaseModuleName()) + dexJarFileApexRootRelative := ApexRootRelativePathToJavaLib(j.BaseModuleName()) if dexOutputPath := di.PrebuiltExportPath(dexJarFileApexRootRelative); dexOutputPath != nil { dexJarFile := makeDexJarPathFromPath(dexOutputPath) j.dexJarFile = dexJarFile - installPath := android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(j.BaseModuleName())) + installPath := android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, ApexRootRelativePathToJavaLib(j.BaseModuleName())) j.dexJarInstallFile = installPath - j.dexpreopter.installPath = j.dexpreopter.getInstallPath(ctx, installPath) + j.dexpreopter.installPath = j.dexpreopter.getInstallPath(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), installPath) setUncompressDex(ctx, &j.dexpreopter, &j.dexer) j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex @@ -2171,8 +2697,6 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.dexpreopter.inputProfilePathOnHost = profilePath } - j.dexpreopt(ctx, dexOutputPath) - // Initialize the hiddenapi structure. j.initHiddenAPI(ctx, dexJarFile, outputFile, j.dexProperties.Uncompress_dex) } else { @@ -2193,7 +2717,7 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { // Dex compilation j.dexpreopter.installPath = j.dexpreopter.getInstallPath( - ctx, android.PathForModuleInstall(ctx, "framework", jarName)) + ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), android.PathForModuleInstall(ctx, "framework", jarName)) setUncompressDex(ctx, &j.dexpreopter, &j.dexer) j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex @@ -2206,7 +2730,7 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { jarName: jarName, } - dexOutputFile = j.dexer.compileDex(ctx, dexParams) + dexOutputFile, _ = j.dexer.compileDex(ctx, dexParams) if ctx.Failed() { return } @@ -2222,14 +2746,19 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { } } - ctx.SetProvider(JavaInfoProvider, JavaInfo{ - HeaderJars: android.PathsIfNonNil(j.combinedClasspathFile), + android.SetProvider(ctx, JavaInfoProvider, JavaInfo{ + HeaderJars: android.PathsIfNonNil(j.combinedHeaderFile), TransitiveLibsHeaderJars: j.transitiveLibsHeaderJars, TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars, - ImplementationAndResourcesJars: android.PathsIfNonNil(j.combinedClasspathFile), - ImplementationJars: android.PathsIfNonNil(j.combinedClasspathFile), + ImplementationAndResourcesJars: android.PathsIfNonNil(j.combinedImplementationFile), + ImplementationJars: android.PathsIfNonNil(j.combinedImplementationFile), AidlIncludeDirs: j.exportAidlIncludeDirs, + StubsLinkType: j.stubsLinkType, + // TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts }) + + ctx.SetOutputFiles(android.Paths{j.combinedImplementationFile}, "") + ctx.SetOutputFiles(android.Paths{j.combinedImplementationFile}, ".jar") } func (j *Import) maybeInstall(ctx android.ModuleContext, jarName string, outputFile android.Path) { @@ -2250,32 +2779,18 @@ func (j *Import) maybeInstall(ctx android.ModuleContext, jarName string, outputF ctx.InstallFile(installDir, jarName, outputFile) } -func (j *Import) OutputFiles(tag string) (android.Paths, error) { - switch tag { - case "", ".jar": - return android.Paths{j.combinedClasspathFile}, nil - default: - return nil, fmt.Errorf("unsupported module reference tag %q", tag) - } -} - -var _ android.OutputFileProducer = (*Import)(nil) - func (j *Import) HeaderJars() android.Paths { - if j.combinedClasspathFile == nil { - return nil - } - return android.Paths{j.combinedClasspathFile} + return android.PathsIfNonNil(j.combinedHeaderFile) } func (j *Import) ImplementationAndResourcesJars() android.Paths { - if j.combinedClasspathFile == nil { - return nil - } - return android.Paths{j.combinedClasspathFile} + return android.PathsIfNonNil(j.combinedImplementationFile) } -func (j *Import) DexJarBuildPath() OptionalDexJarPath { +func (j *Import) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath { + if j.dexJarFileErr != nil { + ctx.ModuleErrorf(j.dexJarFileErr.Error()) + } return j.dexJarFile } @@ -2316,7 +2831,7 @@ func (j *Import) ShouldSupportSdkVersion(ctx android.BaseModuleContext, // java_sdk_library_import with the specified base module name requires to be exported from a // prebuilt_apex/apex_set. func requiredFilesFromPrebuiltApexForImport(name string, d *dexpreopter) []string { - dexJarFileApexRootRelative := apexRootRelativePathToJavaLib(name) + dexJarFileApexRootRelative := ApexRootRelativePathToJavaLib(name) // Add the dex implementation jar to the set of exported files. files := []string{ dexJarFileApexRootRelative, @@ -2327,9 +2842,9 @@ func requiredFilesFromPrebuiltApexForImport(name string, d *dexpreopter) []strin return files } -// apexRootRelativePathToJavaLib returns the path, relative to the root of the apex's contents, for +// ApexRootRelativePathToJavaLib returns the path, relative to the root of the apex's contents, for // the java library with the specified name. -func apexRootRelativePathToJavaLib(name string) string { +func ApexRootRelativePathToJavaLib(name string) string { return filepath.Join("javalib", name+".jar") } @@ -2340,6 +2855,10 @@ func (j *Import) RequiredFilesFromPrebuiltApex(_ android.BaseModuleContext) []st return requiredFilesFromPrebuiltApexForImport(name, &j.dexpreopter) } +func (j *Import) UseProfileGuidedDexpreopt() bool { + return proptools.Bool(j.importDexpreoptProperties.Dex_preopt.Profile_guided) +} + // Add compile time check for interface implementation var _ android.IDEInfo = (*Import)(nil) var _ android.IDECustomizedModuleName = (*Import)(nil) @@ -2387,7 +2906,6 @@ func ImportFactory() android.Module { android.InitPrebuiltModule(module, &module.properties.Jars) android.InitApexModule(module) - android.InitBazelModule(module) InitJavaModule(module, android.HostAndDeviceSupported) return module } @@ -2404,7 +2922,6 @@ func ImportFactoryHost() android.Module { android.InitPrebuiltModule(module, &module.properties.Jars) android.InitApexModule(module) - android.InitBazelModule(module) InitJavaModule(module, android.HostSupported) return module } @@ -2473,14 +2990,14 @@ func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { ctx.PropertyErrorf("jars", "exactly one jar must be provided") } - apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) if !apexInfo.IsForPlatform() { j.hideApexVariantFromMake = true } j.dexpreopter.installPath = j.dexpreopter.getInstallPath( - ctx, android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")) - j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter) + ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")) + j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), &j.dexpreopter) inputJar := ctx.ExpandSource(j.properties.Jars[0], "jars") dexOutputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".jar") @@ -2519,7 +3036,7 @@ func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.dexJarFile = makeDexJarPathFromPath(dexOutputFile) - j.dexpreopt(ctx, dexOutputFile) + j.dexpreopt(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), dexOutputFile) if apexInfo.IsForPlatform() { ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"), @@ -2527,7 +3044,7 @@ func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { } } -func (j *DexImport) DexJarBuildPath() OptionalDexJarPath { +func (j *DexImport) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath { return j.dexJarFile } @@ -2599,7 +3116,7 @@ func DefaultsFactory() android.Module { module.AddProperties( &CommonProperties{}, &DeviceProperties{}, - &OverridableDeviceProperties{}, + &OverridableProperties{}, &DexProperties{}, &DexpreoptProperties{}, &android.ProtoProperties{}, @@ -2608,6 +3125,7 @@ func DefaultsFactory() android.Module { &appProperties{}, &appTestProperties{}, &overridableAppProperties{}, + &hostTestProperties{}, &testProperties{}, &ImportProperties{}, &AARImportProperties{}, @@ -2619,6 +3137,8 @@ func DefaultsFactory() android.Module { &LintProperties{}, &appTestHelperAppProperties{}, &JavaApiLibraryProperties{}, + &bootclasspathFragmentProperties{}, + &SourceOnlyBootclasspathProperties{}, ) android.InitDefaultsModule(module) @@ -2648,7 +3168,7 @@ func (ks *kytheExtractJavaSingleton) GenerateBuildActions(ctx android.SingletonC var Bool = proptools.Bool var BoolDefault = proptools.BoolDefault var String = proptools.String -var inList = android.InList +var inList = android.InList[string] // Add class loader context (CLC) of a given dependency to the current CLC. func addCLCFromDep(ctx android.ModuleContext, depModule android.Module, @@ -2693,520 +3213,80 @@ func addCLCFromDep(ctx android.ModuleContext, depModule android.Module, // <uses_library> and should not be added to CLC, but the transitive <uses-library> dependencies // from its CLC should be added to the current CLC. if sdkLib != nil { - clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, false, - dep.DexJarBuildPath().PathOrNil(), dep.DexJarInstallPath(), dep.ClassLoaderContexts()) + optional := false + if module, ok := ctx.Module().(ModuleWithUsesLibrary); ok { + if android.InList(*sdkLib, module.UsesLibrary().usesLibraryProperties.Optional_uses_libs) { + optional = true + } + } + clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, optional, + dep.DexJarBuildPath(ctx).PathOrNil(), dep.DexJarInstallPath(), dep.ClassLoaderContexts()) } else { clcMap.AddContextMap(dep.ClassLoaderContexts(), depName) } } -type javaResourcesAttributes struct { - Resources bazel.LabelListAttribute - Resource_strip_prefix *string -} - -func (m *Library) convertJavaResourcesAttributes(ctx android.TopDownMutatorContext) *javaResourcesAttributes { - var resources bazel.LabelList - var resourceStripPrefix *string +func addMissingOptionalUsesLibsFromDep(ctx android.ModuleContext, depModule android.Module, + usesLibrary *usesLibrary) { - if m.properties.Java_resources != nil { - resources.Append(android.BazelLabelForModuleSrc(ctx, m.properties.Java_resources)) + dep, ok := depModule.(ModuleWithUsesLibrary) + if !ok { + return } - //TODO(b/179889880) handle case where glob includes files outside package - resDeps := ResourceDirsToFiles( - ctx, - m.properties.Java_resource_dirs, - m.properties.Exclude_java_resource_dirs, - m.properties.Exclude_java_resources, - ) - - for i, resDep := range resDeps { - dir, files := resDep.dir, resDep.files - - resources.Append(bazel.MakeLabelList(android.RootToModuleRelativePaths(ctx, files))) - - // Bazel includes the relative path from the WORKSPACE root when placing the resource - // inside the JAR file, so we need to remove that prefix - resourceStripPrefix = proptools.StringPtr(dir.String()) - if i > 0 { - // TODO(b/226423379) allow multiple resource prefixes - ctx.ModuleErrorf("bp2build does not support more than one directory in java_resource_dirs (b/226423379)") + for _, lib := range dep.UsesLibrary().usesLibraryProperties.Missing_optional_uses_libs { + if !android.InList(lib, usesLibrary.usesLibraryProperties.Missing_optional_uses_libs) { + usesLibrary.usesLibraryProperties.Missing_optional_uses_libs = + append(usesLibrary.usesLibraryProperties.Missing_optional_uses_libs, lib) } } - - return &javaResourcesAttributes{ - Resources: bazel.MakeLabelListAttribute(resources), - Resource_strip_prefix: resourceStripPrefix, - } -} - -type javaCommonAttributes struct { - *javaResourcesAttributes - *kotlinAttributes - Srcs bazel.LabelListAttribute - Plugins bazel.LabelListAttribute - Javacopts bazel.StringListAttribute - Sdk_version bazel.StringAttribute - Java_version bazel.StringAttribute } -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 -} +type JavaApiContributionImport struct { + JavaApiContribution -type eventLogTagsAttributes struct { - Srcs bazel.LabelListAttribute + prebuilt android.Prebuilt + prebuiltProperties javaApiContributionImportProperties } -type aidlLibraryAttributes struct { - Srcs bazel.LabelListAttribute - Tags bazel.StringListAttribute -} +type javaApiContributionImportProperties struct { + // Name of the source soong module that gets shadowed by this prebuilt + // If unspecified, follows the naming convention that the source module of + // the prebuilt is Name() without "prebuilt_" prefix + Source_module_name *string -type javaAidlLibraryAttributes struct { - Deps bazel.LabelListAttribute - Tags bazel.StringListAttribute + // Non-nil if this java_import module was dynamically created by a java_sdk_library_import + // The name is the undecorated name of the java_sdk_library as it appears in the blueprint file + // (without any prebuilt_ prefix) + Created_by_java_sdk_library_name *string `blueprint:"mutated"` } -// bp2BuildJavaInfo has information needed for the conversion of java*_modules -// that is needed bor Bp2Build conversion but that requires different handling -// depending on the module type. -type bp2BuildJavaInfo struct { - // separates dependencies into dynamic dependencies and static dependencies. - DepLabels *javaDependencyLabels - hasKotlin bool -} - -// convertLibraryAttrsBp2Build returns a javaCommonAttributes struct with -// converted attributes shared across java_* modules and a bp2BuildJavaInfo struct -// which has other non-attribute information needed for bp2build conversion -// that needs different handling depending on the module types, and thus needs -// to be returned to the calling function. -func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) (*javaCommonAttributes, *bp2BuildJavaInfo) { - var srcs bazel.LabelListAttribute - var deps bazel.LabelListAttribute - var staticDeps bazel.LabelList - - 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) - } - } - } - srcs.ResolveExcludes() - - javaSrcPartition := "java" - protoSrcPartition := "proto" - logtagSrcPartition := "logtag" - aidlSrcPartition := "aidl" - kotlinPartition := "kotlin" - 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, - aidlSrcPartition: android.AidlSrcLabelPartition, - kotlinPartition: bazel.LabelPartition{Extensions: []string{".kt"}}, - }) - - javaSrcs := srcPartitions[javaSrcPartition] - kotlinSrcs := srcPartitions[kotlinPartition] - javaSrcs.Append(kotlinSrcs) - - if !srcPartitions[logtagSrcPartition].IsEmpty() { - logtagsLibName := m.Name() + "_logtags" - ctx.CreateBazelTargetModule( - bazel.BazelTargetModuleProperties{ - Rule_class: "event_log_tags", - Bzl_load_location: "//build/bazel/rules/java:event_log_tags.bzl", - }, - android.CommonAttributes{Name: logtagsLibName}, - &eventLogTagsAttributes{ - Srcs: srcPartitions[logtagSrcPartition], - }, - ) - - logtagsSrcs := bazel.MakeLabelList([]bazel.Label{{Label: ":" + logtagsLibName}}) - javaSrcs.Append(bazel.MakeLabelListAttribute(logtagsSrcs)) - } - - if !srcPartitions[aidlSrcPartition].IsEmpty() { - aidlLibs, aidlSrcs := srcPartitions[aidlSrcPartition].Partition(func(src bazel.Label) bool { - return android.IsConvertedToAidlLibrary(ctx, src.OriginalModuleName) - }) - - apexAvailableTags := android.ApexAvailableTags(ctx.Module()) - - if !aidlSrcs.IsEmpty() { - aidlLibName := m.Name() + "_aidl_library" - ctx.CreateBazelTargetModule( - bazel.BazelTargetModuleProperties{ - Rule_class: "aidl_library", - Bzl_load_location: "//build/bazel/rules/aidl:aidl_library.bzl", - }, - android.CommonAttributes{Name: aidlLibName}, - &aidlLibraryAttributes{ - Srcs: aidlSrcs, - Tags: apexAvailableTags, - }, - ) - aidlLibs.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + aidlLibName}}) - } - - javaAidlLibName := m.Name() + "_java_aidl_library" - ctx.CreateBazelTargetModule( - bazel.BazelTargetModuleProperties{ - Rule_class: "java_aidl_library", - Bzl_load_location: "//build/bazel/rules/java:java_aidl_library.bzl", - }, - android.CommonAttributes{Name: javaAidlLibName}, - &javaAidlLibraryAttributes{ - Deps: aidlLibs, - Tags: apexAvailableTags, - }, - ) - - staticDeps.Add(&bazel.Label{Label: ":" + javaAidlLibName}) - } - - 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, - javaResourcesAttributes: m.convertJavaResourcesAttributes(ctx), - Plugins: bazel.MakeLabelListAttribute( - android.BazelLabelForModuleDeps(ctx, m.properties.Plugins), - ), - Javacopts: bazel.MakeStringListAttribute(javacopts), - Java_version: bazel.StringAttribute{Value: m.properties.Java_version}, - Sdk_version: bazel.StringAttribute{Value: m.deviceProperties.Sdk_version}, - } - - for axis, configToProps := range archVariantProps { - for config, _props := range configToProps { - if archProps, ok := _props.(*CommonProperties); ok { - var libLabels []bazel.Label - for _, d := range archProps.Libs { - neverlinkLabel := android.BazelLabelForModuleDepSingle(ctx, d) - neverlinkLabel.Label = neverlinkLabel.Label + "-neverlink" - libLabels = append(libLabels, neverlinkLabel) - } - deps.SetSelectValue(axis, config, bazel.MakeLabelList(libLabels)) - } - } - } - - 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 := &javaDependencyLabels{} - depLabels.Deps = deps - - for axis, configToProps := range archVariantProps { - for config, _props := range configToProps { - if archProps, ok := _props.(*CommonProperties); ok { - archStaticLibs := android.BazelLabelForModuleDeps( - ctx, - android.LastUniqueStrings(android.CopyOf(archProps.Static_libs))) - depLabels.StaticDeps.SetSelectValue(axis, config, archStaticLibs) - } - } - } - depLabels.StaticDeps.Value.Append(staticDeps) - - hasKotlin := !kotlinSrcs.IsEmpty() - commonAttrs.kotlinAttributes = &kotlinAttributes{ - Kotlincflags: &m.properties.Kotlincflags, - } - if len(m.properties.Common_srcs) != 0 { - hasKotlin = true - commonAttrs.kotlinAttributes.Common_srcs = bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Common_srcs)) - } - - bp2BuildInfo := &bp2BuildJavaInfo{ - DepLabels: depLabels, - hasKotlin: hasKotlin, - } - - return commonAttrs, bp2BuildInfo -} - -type javaLibraryAttributes struct { - *javaCommonAttributes - Deps bazel.LabelListAttribute - Exports bazel.LabelListAttribute - Neverlink bazel.BoolAttribute -} - -type kotlinAttributes struct { - Common_srcs bazel.LabelListAttribute - Kotlincflags *[]string -} - -func ktJvmLibraryBazelTargetModuleProperties() bazel.BazelTargetModuleProperties { - return bazel.BazelTargetModuleProperties{ - Rule_class: "kt_jvm_library", - Bzl_load_location: "//build/bazel/rules/kotlin:rules.bzl", - } -} - -func javaLibraryBazelTargetModuleProperties() bazel.BazelTargetModuleProperties { - return bazel.BazelTargetModuleProperties{ - Rule_class: "java_library", - Bzl_load_location: "//build/bazel/rules/java:rules.bzl", - } -} - -func javaLibraryBp2Build(ctx android.TopDownMutatorContext, m *Library) { - commonAttrs, bp2BuildInfo := m.convertLibraryAttrsBp2Build(ctx) - depLabels := bp2BuildInfo.DepLabels - - 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() { - ctx.ModuleErrorf("Module has direct dependencies but no sources. Bazel will not allow this.") - } - var props bazel.BazelTargetModuleProperties - attrs := &javaLibraryAttributes{ - javaCommonAttributes: commonAttrs, - Deps: deps, - Exports: depLabels.StaticDeps, - } - name := m.Name() - - if !bp2BuildInfo.hasKotlin { - props = javaLibraryBazelTargetModuleProperties() - } else { - props = ktJvmLibraryBazelTargetModuleProperties() - } - - ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, attrs) - neverlinkProp := true - neverLinkAttrs := &javaLibraryAttributes{ - Exports: bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + name}), - Neverlink: bazel.BoolAttribute{Value: &neverlinkProp}, - javaCommonAttributes: &javaCommonAttributes{ - Sdk_version: bazel.StringAttribute{Value: m.deviceProperties.Sdk_version}, - Java_version: bazel.StringAttribute{Value: m.properties.Java_version}, - }, - } - ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name + "-neverlink"}, neverLinkAttrs) - -} - -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, bp2BuildInfo := m.convertLibraryAttrsBp2Build(ctx) - depLabels := bp2BuildInfo.DepLabels - - 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 - } - - // Attribute jvm_flags - var jvmFlags bazel.StringListAttribute - 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) - } - jvmFlags = bazel.MakeStringListAttribute([]string{"-Djava.library.path=" + strings.Join(jniLibPaths, ":")}) - } - - props := bazel.BazelTargetModuleProperties{ - Rule_class: "java_binary", - Bzl_load_location: "//build/bazel/rules/java:rules.bzl", - } - binAttrs := &javaBinaryHostAttributes{ - Runtime_deps: runtimeDeps, - Main_class: mainClass, - Jvm_flags: jvmFlags, - } - - if commonAttrs.Srcs.IsEmpty() { - binAttrs.javaCommonAttributes = commonAttrs - ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, binAttrs) - return - } - - libName := m.Name() + "_lib" - var libProps bazel.BazelTargetModuleProperties - if bp2BuildInfo.hasKotlin { - libProps = ktJvmLibraryBazelTargetModuleProperties() - } else { - libProps = javaLibraryBazelTargetModuleProperties() - } - libAttrs := &javaLibraryAttributes{ - Deps: deps, - javaCommonAttributes: commonAttrs, - } - - ctx.CreateBazelTargetModule(libProps, android.CommonAttributes{Name: libName}, libAttrs) - binAttrs.Runtime_deps.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + libName}}) - - // Create the BazelTargetModule. - ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, binAttrs) -} - -type bazelJavaImportAttributes struct { - Jars bazel.LabelListAttribute - Exports bazel.LabelListAttribute +func ApiContributionImportFactory() android.Module { + module := &JavaApiContributionImport{} + android.InitAndroidModule(module) + android.InitDefaultableModule(module) + android.InitPrebuiltModule(module, &[]string{""}) + module.AddProperties(&module.properties, &module.prebuiltProperties) + module.AddProperties(&module.sdkLibraryComponentProperties) + return module } -// 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", - Bzl_load_location: "//build/bazel/rules/java:rules.bzl", - } - - name := android.RemoveOptionalPrebuiltPrefix(i.Name()) - - ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, attrs) - - neverlink := true - neverlinkAttrs := &javaLibraryAttributes{ - Neverlink: bazel.BoolAttribute{Value: &neverlink}, - Exports: bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + name}), - javaCommonAttributes: &javaCommonAttributes{ - Sdk_version: bazel.StringAttribute{Value: proptools.StringPtr("none")}, - }, - } - ctx.CreateBazelTargetModule( - javaLibraryBazelTargetModuleProperties(), - android.CommonAttributes{Name: name + "-neverlink"}, - neverlinkAttrs) - +func (module *JavaApiContributionImport) Prebuilt() *android.Prebuilt { + return &module.prebuilt } -var _ android.MixedBuildBuildable = (*Import)(nil) - -func (i *Import) getBazelModuleLabel(ctx android.BaseModuleContext) string { - return android.RemoveOptionalPrebuiltPrefixFromBazelLabel(i.GetBazelLabel(ctx, i)) +func (module *JavaApiContributionImport) Name() string { + return module.prebuilt.Name(module.ModuleBase.Name()) } -func (i *Import) ProcessBazelQueryResponse(ctx android.ModuleContext) { - i.commonBuildActions(ctx) - - bazelCtx := ctx.Config().BazelContext - filePaths, err := bazelCtx.GetOutputFiles(i.getBazelModuleLabel(ctx), android.GetConfigKey(ctx)) - if err != nil { - ctx.ModuleErrorf(err.Error()) - return - } - - bazelJars := android.Paths{} - for _, bazelOutputFile := range filePaths { - bazelJars = append(bazelJars, android.PathForBazelOut(ctx, bazelOutputFile)) - } - - jarName := android.RemoveOptionalPrebuiltPrefix(i.Name()) + ".jar" - outputFile := android.PathForModuleOut(ctx, "bazelCombined", jarName) - TransformJarsToJar(ctx, outputFile, "combine prebuilt jars", bazelJars, - android.OptionalPath{}, // manifest - false, // stripDirEntries - []string{}, // filesToStrip - []string{}, // dirsToStrip - ) - i.combinedClasspathFile = outputFile - - ctx.SetProvider(JavaInfoProvider, JavaInfo{ - HeaderJars: android.PathsIfNonNil(i.combinedClasspathFile), - ImplementationAndResourcesJars: android.PathsIfNonNil(i.combinedClasspathFile), - ImplementationJars: android.PathsIfNonNil(i.combinedClasspathFile), - //TODO(b/240308299) include AIDL information from Bazel - }) - - i.maybeInstall(ctx, jarName, outputFile) +func (j *JavaApiContributionImport) BaseModuleName() string { + return proptools.StringDefault(j.prebuiltProperties.Source_module_name, j.ModuleBase.Name()) } -func (i *Import) QueueBazelCall(ctx android.BaseModuleContext) { - bazelCtx := ctx.Config().BazelContext - bazelCtx.QueueBazelRequest(i.getBazelModuleLabel(ctx), cquery.GetOutputFiles, android.GetConfigKey(ctx)) +func (j *JavaApiContributionImport) CreatedByJavaSdkLibraryName() *string { + return j.prebuiltProperties.Created_by_java_sdk_library_name } -func (i *Import) IsMixedBuildSupported(ctx android.BaseModuleContext) bool { - return true +func (ap *JavaApiContributionImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { + ap.JavaApiContribution.GenerateAndroidBuildActions(ctx) } diff --git a/java/java_test.go b/java/java_test.go index 0c002f385..33079f381 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -24,8 +24,10 @@ import ( "strings" "testing" + "github.com/google/blueprint" "github.com/google/blueprint/proptools" + "android/soong/aconfig" "android/soong/android" "android/soong/cc" "android/soong/dexpreopt" @@ -47,10 +49,8 @@ var prepareForJavaTest = android.GroupFixturePreparers( cc.PrepareForTestWithCcBuildComponents, // Include all the default java modules. PrepareForTestWithDexpreopt, - PrepareForTestWithOverlayBuildComponents, - android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { - ctx.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory) - }), + // Include aconfig modules. + aconfig.PrepareForTestWithAconfigBuildComponents, ) func TestMain(m *testing.M) { @@ -529,6 +529,15 @@ func TestHostBinaryNoJavaDebugInfoOverride(t *testing.T) { } } +// A minimal context object for use with DexJarBuildPath +type moduleErrorfTestCtx struct { +} + +func (ctx moduleErrorfTestCtx) ModuleErrorf(format string, args ...interface{}) { +} + +var _ android.ModuleErrorfContext = (*moduleErrorfTestCtx)(nil) + func TestPrebuilts(t *testing.T) { ctx, _ := testJava(t, ` java_library { @@ -579,10 +588,11 @@ func TestPrebuilts(t *testing.T) { javac := fooModule.Rule("javac") combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac") barModule := ctx.ModuleForTests("bar", "android_common") - barJar := barModule.Rule("combineJar").Output + barJar := barModule.Output("combined/bar.jar").Output bazModule := ctx.ModuleForTests("baz", "android_common") bazJar := bazModule.Rule("combineJar").Output - sdklibStubsJar := ctx.ModuleForTests("sdklib.stubs", "android_common").Rule("combineJar").Output + sdklibStubsJar := ctx.ModuleForTests("sdklib.stubs", "android_common"). + Output("combined/sdklib.stubs.jar").Output fooLibrary := fooModule.Module().(*Library) assertDeepEquals(t, "foo unique sources incorrect", @@ -596,7 +606,8 @@ func TestPrebuilts(t *testing.T) { t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], barJar.String()) } - barDexJar := barModule.Module().(*Import).DexJarBuildPath() + errCtx := moduleErrorfTestCtx{} + barDexJar := barModule.Module().(*Import).DexJarBuildPath(errCtx) if barDexJar.IsSet() { t.Errorf("bar dex jar build path expected to be set, got %s", barDexJar) } @@ -609,7 +620,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().Path() + bazDexJar := bazModule.Module().(*Import).DexJarBuildPath(errCtx).Path() expectedDexJar := "out/soong/.intermediates/baz/android_common/dex/baz.jar" android.AssertPathRelativeToTopEquals(t, "baz dex jar build path", expectedDexJar, bazDexJar) @@ -619,8 +630,6 @@ func TestPrebuilts(t *testing.T) { android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "java_library", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0]) entries = android.AndroidMkEntriesForTest(t, ctx, barModule.Module())[0] android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "java_import", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0]) - entries = android.AndroidMkEntriesForTest(t, ctx, ctx.ModuleForTests("sdklib", "android_common").Module())[0] - android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "java_sdk_library_import", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0]) } func assertDeepEquals(t *testing.T, message string, expected interface{}, actual interface{}) { @@ -1027,7 +1036,7 @@ func TestExcludeFileGroupInSrcs(t *testing.T) { } } -func TestJavaLibrary(t *testing.T) { +func TestJavaLibraryOutputFiles(t *testing.T) { testJavaWithFS(t, "", map[string][]byte{ "libcore/Android.bp": []byte(` java_library { @@ -1044,7 +1053,7 @@ func TestJavaLibrary(t *testing.T) { }) } -func TestJavaImport(t *testing.T) { +func TestJavaImportOutputFiles(t *testing.T) { testJavaWithFS(t, "", map[string][]byte{ "libcore/Android.bp": []byte(` java_import { @@ -1060,6 +1069,85 @@ func TestJavaImport(t *testing.T) { }) } +func TestJavaImport(t *testing.T) { + bp := ` + java_library { + name: "source_library", + srcs: ["source.java"], + } + + java_import { + name: "import_with_no_deps", + jars: ["no_deps.jar"], + } + + java_import { + name: "import_with_source_deps", + jars: ["source_deps.jar"], + static_libs: ["source_library"], + } + + java_import { + name: "import_with_import_deps", + jars: ["import_deps.jar"], + static_libs: ["import_with_no_deps"], + } + ` + ctx := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + ).RunTestWithBp(t, bp) + + source := ctx.ModuleForTests("source_library", "android_common") + sourceJar := source.Output("javac/source_library.jar") + sourceHeaderJar := source.Output("turbine-combined/source_library.jar") + sourceJavaInfo, _ := android.SingletonModuleProvider(ctx, source.Module(), JavaInfoProvider) + + // The source library produces separate implementation and header jars + android.AssertPathsRelativeToTopEquals(t, "source library implementation jar", + []string{sourceJar.Output.String()}, sourceJavaInfo.ImplementationAndResourcesJars) + android.AssertPathsRelativeToTopEquals(t, "source library header jar", + []string{sourceHeaderJar.Output.String()}, sourceJavaInfo.HeaderJars) + + importWithNoDeps := ctx.ModuleForTests("import_with_no_deps", "android_common") + importWithNoDepsJar := importWithNoDeps.Output("combined/import_with_no_deps.jar") + importWithNoDepsJavaInfo, _ := android.SingletonModuleProvider(ctx, importWithNoDeps.Module(), JavaInfoProvider) + + // An import with no deps produces a single jar used as both the header and implementation jar. + android.AssertPathsRelativeToTopEquals(t, "import with no deps implementation jar", + []string{importWithNoDepsJar.Output.String()}, importWithNoDepsJavaInfo.ImplementationAndResourcesJars) + android.AssertPathsRelativeToTopEquals(t, "import with no deps header jar", + []string{importWithNoDepsJar.Output.String()}, importWithNoDepsJavaInfo.HeaderJars) + android.AssertPathsRelativeToTopEquals(t, "import with no deps combined inputs", + []string{"no_deps.jar"}, importWithNoDepsJar.Inputs) + + importWithSourceDeps := ctx.ModuleForTests("import_with_source_deps", "android_common") + importWithSourceDepsJar := importWithSourceDeps.Output("combined/import_with_source_deps.jar") + importWithSourceDepsHeaderJar := importWithSourceDeps.Output("turbine-combined/import_with_source_deps.jar") + importWithSourceDepsJavaInfo, _ := android.SingletonModuleProvider(ctx, importWithSourceDeps.Module(), JavaInfoProvider) + + // An import with source deps produces separate header and implementation jars. + android.AssertPathsRelativeToTopEquals(t, "import with source deps implementation jar", + []string{importWithSourceDepsJar.Output.String()}, importWithSourceDepsJavaInfo.ImplementationAndResourcesJars) + android.AssertPathsRelativeToTopEquals(t, "import with source deps header jar", + []string{importWithSourceDepsHeaderJar.Output.String()}, importWithSourceDepsJavaInfo.HeaderJars) + android.AssertPathsRelativeToTopEquals(t, "import with source deps combined implementation jar inputs", + []string{"source_deps.jar", sourceJar.Output.String()}, importWithSourceDepsJar.Inputs) + android.AssertPathsRelativeToTopEquals(t, "import with source deps combined header jar inputs", + []string{"source_deps.jar", sourceHeaderJar.Output.String()}, importWithSourceDepsHeaderJar.Inputs) + + importWithImportDeps := ctx.ModuleForTests("import_with_import_deps", "android_common") + importWithImportDepsJar := importWithImportDeps.Output("combined/import_with_import_deps.jar") + importWithImportDepsJavaInfo, _ := android.SingletonModuleProvider(ctx, importWithImportDeps.Module(), JavaInfoProvider) + + // An import with only import deps produces a single jar used as both the header and implementation jar. + android.AssertPathsRelativeToTopEquals(t, "import with import deps implementation jar", + []string{importWithImportDepsJar.Output.String()}, importWithImportDepsJavaInfo.ImplementationAndResourcesJars) + android.AssertPathsRelativeToTopEquals(t, "import with import deps header jar", + []string{importWithImportDepsJar.Output.String()}, importWithImportDepsJavaInfo.HeaderJars) + android.AssertPathsRelativeToTopEquals(t, "import with import deps combined implementation jar inputs", + []string{"import_deps.jar", importWithNoDepsJar.Output.String()}, importWithImportDepsJar.Inputs) +} + var compilerFlagsTestCases = []struct { in string out bool @@ -1206,7 +1294,7 @@ func TestPatchModule(t *testing.T) { expected := "java.base=.:out/soong" checkPatchModuleFlag(t, ctx, "bar", expected) expected = "java.base=" + strings.Join([]string{ - ".", "out/soong", "dir", "dir2", "nested", defaultModuleToPath("ext"), defaultModuleToPath("framework")}, ":") + ".", "out/soong", defaultModuleToPath("ext"), defaultModuleToPath("framework")}, ":") checkPatchModuleFlag(t, ctx, "baz", expected) }) } @@ -1291,43 +1379,6 @@ func TestAidlExportIncludeDirsFromImports(t *testing.T) { } } -func TestAidlIncludeDirFromConvertedFileGroupWithPathPropInMixedBuilds(t *testing.T) { - // TODO(b/247782695), TODO(b/242847534) Fix mixed builds for filegroups - t.Skip("Re-enable once filegroups are corrected for mixed builds") - bp := ` - filegroup { - name: "foo_aidl", - srcs: ["aidl/foo/IFoo.aidl"], - path: "aidl/foo", - bazel_module: { label: "//:foo_aidl" }, - } - java_library { - name: "foo", - srcs: [":foo_aidl"], - } -` - - outBaseDir := "out/bazel/output" - result := android.GroupFixturePreparers( - prepareForJavaTest, - android.PrepareForTestWithFilegroup, - android.FixtureModifyConfig(func(config android.Config) { - config.BazelContext = android.MockBazelContext{ - OutputBaseDir: outBaseDir, - LabelToOutputFiles: map[string][]string{ - "//:foo_aidl": []string{"aidl/foo/IFoo.aidl"}, - }, - } - }), - ).RunTestWithBp(t, bp) - - aidlCommand := result.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command - expectedAidlFlag := "-I" + outBaseDir + "/execroot/__main__/aidl/foo" - if !strings.Contains(aidlCommand, expectedAidlFlag) { - t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag) - } -} - func TestAidlFlagsArePassedToTheAidlCompiler(t *testing.T) { ctx, _ := testJava(t, ` java_library { @@ -1744,85 +1795,6 @@ func TestDataDeviceBinsBuildsDeviceBinary(t *testing.T) { } } -func TestImportMixedBuild(t *testing.T) { - bp := ` - java_import { - name: "baz", - jars: [ - "test1.jar", - "test2.jar", - ], - bazel_module: { label: "//foo/bar:baz" }, - } - ` - - ctx := android.GroupFixturePreparers( - prepareForJavaTest, - android.FixtureModifyConfig(func(config android.Config) { - config.BazelContext = android.MockBazelContext{ - OutputBaseDir: "outputbase", - LabelToOutputFiles: map[string][]string{ - "//foo/bar:baz": []string{"test1.jar", "test2.jar"}, - }, - } - }), - ).RunTestWithBp(t, bp) - - bazMod := ctx.ModuleForTests("baz", "android_common").Module() - producer := bazMod.(android.OutputFileProducer) - expectedOutputFiles := []string{".intermediates/baz/android_common/bazelCombined/baz.jar"} - - outputFiles, err := producer.OutputFiles("") - if err != nil { - t.Errorf("Unexpected error getting java_import outputfiles %s", err) - } - actualOutputFiles := android.NormalizePathsForTesting(outputFiles) - android.AssertDeepEquals(t, "Output files are produced", expectedOutputFiles, actualOutputFiles) - - javaInfoProvider := ctx.ModuleProvider(bazMod, JavaInfoProvider) - javaInfo, ok := javaInfoProvider.(JavaInfo) - if !ok { - t.Error("could not get JavaInfo from java_import module") - } - android.AssertDeepEquals(t, "Header JARs are produced", expectedOutputFiles, android.NormalizePathsForTesting(javaInfo.HeaderJars)) - android.AssertDeepEquals(t, "Implementation/Resources JARs are produced", expectedOutputFiles, android.NormalizePathsForTesting(javaInfo.ImplementationAndResourcesJars)) - android.AssertDeepEquals(t, "Implementation JARs are produced", expectedOutputFiles, android.NormalizePathsForTesting(javaInfo.ImplementationJars)) -} - -func TestGenAidlIncludeFlagsForMixedBuilds(t *testing.T) { - bazelOutputBaseDir := filepath.Join("out", "bazel") - result := android.GroupFixturePreparers( - PrepareForIntegrationTestWithJava, - android.FixtureModifyConfig(func(config android.Config) { - config.BazelContext = android.MockBazelContext{ - OutputBaseDir: bazelOutputBaseDir, - } - }), - ).RunTest(t) - - ctx := &android.TestPathContext{TestResult: result} - - srcDirectory := filepath.Join("frameworks", "base") - srcDirectoryAlreadyIncluded := filepath.Join("frameworks", "base", "core", "java") - bazelSrcDirectory := android.PathForBazelOut(ctx, srcDirectory) - bazelSrcDirectoryAlreadyIncluded := android.PathForBazelOut(ctx, srcDirectoryAlreadyIncluded) - srcs := android.Paths{ - android.PathForTestingWithRel(bazelSrcDirectory.String(), "bazelAidl.aidl"), - android.PathForTestingWithRel(bazelSrcDirectory.String(), "bazelAidl2.aidl"), - android.PathForTestingWithRel(bazelSrcDirectoryAlreadyIncluded.String(), "bazelAidlExclude.aidl"), - android.PathForTestingWithRel(bazelSrcDirectoryAlreadyIncluded.String(), "bazelAidl2Exclude.aidl"), - } - dirsAlreadyIncluded := android.Paths{ - android.PathForTesting(srcDirectoryAlreadyIncluded), - } - - expectedFlags := " -Iout/bazel/execroot/__main__/frameworks/base" - flags := genAidlIncludeFlags(ctx, srcs, dirsAlreadyIncluded) - if flags != expectedFlags { - t.Errorf("expected flags to be %q; was %q", expectedFlags, flags) - } -} - func TestDeviceBinaryWrapperGeneration(t *testing.T) { // Scenario 1: java_binary has main_class property in its bp ctx, _ := testJava(t, ` @@ -1847,15 +1819,24 @@ func TestDeviceBinaryWrapperGeneration(t *testing.T) { } func TestJavaApiContributionEmptyApiFile(t *testing.T) { - testJavaError(t, + android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureMergeEnv( + map[string]string{ + "DISABLE_STUB_VALIDATION": "true", + }, + ), + ).ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern( "Error: foo has an empty api file.", - `java_api_contribution { + )).RunTestWithBp(t, ` + java_api_contribution { name: "foo", } java_api_library { name: "bar", api_surface: "public", api_contributions: ["foo"], + stubs_type: "everything", } `) } @@ -1864,32 +1845,44 @@ func TestJavaApiLibraryAndProviderLink(t *testing.T) { provider_bp_a := ` java_api_contribution { name: "foo1", - api_file: "foo1.txt", + api_file: "current.txt", + api_surface: "public", } ` provider_bp_b := `java_api_contribution { name: "foo2", - api_file: "foo2.txt", + api_file: "current.txt", + api_surface: "public", } ` - ctx, _ := testJavaWithFS(t, ` + ctx := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureMergeMockFs( + map[string][]byte{ + "a/Android.bp": []byte(provider_bp_a), + "b/Android.bp": []byte(provider_bp_b), + }, + ), + android.FixtureMergeEnv( + map[string]string{ + "DISABLE_STUB_VALIDATION": "true", + }, + ), + ).RunTestWithBp(t, ` java_api_library { name: "bar1", api_surface: "public", api_contributions: ["foo1"], + stubs_type: "everything", } java_api_library { name: "bar2", api_surface: "system", api_contributions: ["foo1", "foo2"], - api_files: ["api1/current.txt", "api2/current.txt"] + stubs_type: "everything", } - `, - map[string][]byte{ - "a/Android.bp": []byte(provider_bp_a), - "b/Android.bp": []byte(provider_bp_b), - }) + `) testcases := []struct { moduleName string @@ -1897,17 +1890,17 @@ func TestJavaApiLibraryAndProviderLink(t *testing.T) { }{ { moduleName: "bar1", - sourceTextFileDirs: []string{"a/foo1.txt"}, + sourceTextFileDirs: []string{"a/current.txt"}, }, { moduleName: "bar2", - sourceTextFileDirs: []string{"a/foo1.txt", "b/foo2.txt", "api1/current.txt", "api2/current.txt"}, + sourceTextFileDirs: []string{"a/current.txt", "b/current.txt"}, }, } for _, c := range testcases { m := ctx.ModuleForTests(c.moduleName, "android_common") manifest := m.Output("metalava.sbox.textproto") - sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest) + sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, manifest) manifestCommand := sboxProto.Commands[0].GetCommand() sourceFilesFlag := "--source-files " + strings.Join(c.sourceTextFileDirs, " ") android.AssertStringDoesContain(t, "source text files not present", manifestCommand, sourceFilesFlag) @@ -1918,28 +1911,47 @@ func TestJavaApiLibraryAndDefaultsLink(t *testing.T) { provider_bp_a := ` java_api_contribution { name: "foo1", - api_file: "foo1.txt", + api_file: "current.txt", + api_surface: "public", } ` provider_bp_b := ` java_api_contribution { name: "foo2", - api_file: "foo2.txt", + api_file: "current.txt", + api_surface: "public", } ` provider_bp_c := ` java_api_contribution { name: "foo3", - api_file: "foo3.txt", + api_file: "system-current.txt", + api_surface: "system", } ` provider_bp_d := ` java_api_contribution { name: "foo4", - api_file: "foo4.txt", + api_file: "system-current.txt", + api_surface: "system", } ` - ctx, _ := testJavaWithFS(t, ` + ctx := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureMergeMockFs( + map[string][]byte{ + "a/Android.bp": []byte(provider_bp_a), + "b/Android.bp": []byte(provider_bp_b), + "c/Android.bp": []byte(provider_bp_c), + "d/Android.bp": []byte(provider_bp_d), + }, + ), + android.FixtureMergeEnv( + map[string]string{ + "DISABLE_STUB_VALIDATION": "true", + }, + ), + ).RunTestWithBp(t, ` java_defaults { name: "baz1", api_surface: "public", @@ -1956,12 +1968,14 @@ func TestJavaApiLibraryAndDefaultsLink(t *testing.T) { name: "bar1", api_surface: "public", api_contributions: ["foo1"], + stubs_type: "everything", } java_api_library { name: "bar2", api_surface: "public", defaults:["baz1"], + stubs_type: "everything", } java_api_library { @@ -1969,15 +1983,9 @@ func TestJavaApiLibraryAndDefaultsLink(t *testing.T) { api_surface: "system", defaults:["baz1", "baz2"], api_contributions: ["foo4"], - api_files: ["api1/current.txt", "api2/current.txt"] + stubs_type: "everything", } - `, - map[string][]byte{ - "a/Android.bp": []byte(provider_bp_a), - "b/Android.bp": []byte(provider_bp_b), - "c/Android.bp": []byte(provider_bp_c), - "d/Android.bp": []byte(provider_bp_d), - }) + `) testcases := []struct { moduleName string @@ -1985,21 +1993,22 @@ func TestJavaApiLibraryAndDefaultsLink(t *testing.T) { }{ { moduleName: "bar1", - sourceTextFileDirs: []string{"a/foo1.txt"}, + sourceTextFileDirs: []string{"a/current.txt"}, }, { moduleName: "bar2", - sourceTextFileDirs: []string{"a/foo1.txt", "b/foo2.txt"}, + sourceTextFileDirs: []string{"a/current.txt", "b/current.txt"}, }, { - moduleName: "bar3", - sourceTextFileDirs: []string{"c/foo3.txt", "a/foo1.txt", "b/foo2.txt", "d/foo4.txt", "api1/current.txt", "api2/current.txt"}, + moduleName: "bar3", + // API text files need to be sorted from the narrower api scope to the wider api scope + sourceTextFileDirs: []string{"a/current.txt", "b/current.txt", "c/system-current.txt", "d/system-current.txt"}, }, } for _, c := range testcases { m := ctx.ModuleForTests(c.moduleName, "android_common") manifest := m.Output("metalava.sbox.textproto") - sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest) + sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, manifest) manifestCommand := sboxProto.Commands[0].GetCommand() sourceFilesFlag := "--source-files " + strings.Join(c.sourceTextFileDirs, " ") android.AssertStringDoesContain(t, "source text files not present", manifestCommand, sourceFilesFlag) @@ -2010,32 +2019,45 @@ func TestJavaApiLibraryJarGeneration(t *testing.T) { provider_bp_a := ` java_api_contribution { name: "foo1", - api_file: "foo1.txt", + api_file: "current.txt", + api_surface: "public", } ` provider_bp_b := ` java_api_contribution { name: "foo2", - api_file: "foo2.txt", + api_file: "current.txt", + api_surface: "public", } ` - ctx, _ := testJavaWithFS(t, ` + ctx := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureMergeMockFs( + map[string][]byte{ + "a/Android.bp": []byte(provider_bp_a), + "b/Android.bp": []byte(provider_bp_b), + }, + ), + android.FixtureMergeEnv( + map[string]string{ + "DISABLE_STUB_VALIDATION": "true", + }, + ), + ).RunTestWithBp(t, ` java_api_library { name: "bar1", api_surface: "public", api_contributions: ["foo1"], + stubs_type: "everything", } java_api_library { name: "bar2", api_surface: "system", api_contributions: ["foo1", "foo2"], + stubs_type: "everything", } - `, - map[string][]byte{ - "a/Android.bp": []byte(provider_bp_a), - "b/Android.bp": []byte(provider_bp_b), - }) + `) testcases := []struct { moduleName string @@ -2063,13 +2085,15 @@ func TestJavaApiLibraryLibsLink(t *testing.T) { provider_bp_a := ` java_api_contribution { name: "foo1", - api_file: "foo1.txt", + api_file: "current.txt", + api_surface: "public", } ` provider_bp_b := ` java_api_contribution { name: "foo2", - api_file: "foo2.txt", + api_file: "current.txt", + api_surface: "public", } ` lib_bp_a := ` @@ -2085,12 +2109,30 @@ func TestJavaApiLibraryLibsLink(t *testing.T) { } ` - ctx, _ := testJavaWithFS(t, ` + ctx := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureMergeMockFs( + map[string][]byte{ + "a/Android.bp": []byte(provider_bp_a), + "b/Android.bp": []byte(provider_bp_b), + "c/Android.bp": []byte(lib_bp_a), + "c/Lib.java": {}, + "d/Android.bp": []byte(lib_bp_b), + "d/Lib.java": {}, + }, + ), + android.FixtureMergeEnv( + map[string]string{ + "DISABLE_STUB_VALIDATION": "true", + }, + ), + ).RunTestWithBp(t, ` java_api_library { name: "bar1", api_surface: "public", api_contributions: ["foo1"], libs: ["lib1"], + stubs_type: "everything", } java_api_library { @@ -2098,16 +2140,9 @@ func TestJavaApiLibraryLibsLink(t *testing.T) { api_surface: "system", api_contributions: ["foo1", "foo2"], libs: ["lib1", "lib2", "bar1"], + stubs_type: "everything", } - `, - map[string][]byte{ - "a/Android.bp": []byte(provider_bp_a), - "b/Android.bp": []byte(provider_bp_b), - "c/Android.bp": []byte(lib_bp_a), - "c/Lib.java": {}, - "d/Android.bp": []byte(lib_bp_b), - "d/Lib.java": {}, - }) + `) testcases := []struct { moduleName string @@ -2138,13 +2173,15 @@ func TestJavaApiLibraryStaticLibsLink(t *testing.T) { provider_bp_a := ` java_api_contribution { name: "foo1", - api_file: "foo1.txt", + api_file: "current.txt", + api_surface: "public", } ` provider_bp_b := ` java_api_contribution { name: "foo2", - api_file: "foo2.txt", + api_file: "current.txt", + api_surface: "public", } ` lib_bp_a := ` @@ -2160,12 +2197,30 @@ func TestJavaApiLibraryStaticLibsLink(t *testing.T) { } ` - ctx, _ := testJavaWithFS(t, ` + ctx := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureMergeMockFs( + map[string][]byte{ + "a/Android.bp": []byte(provider_bp_a), + "b/Android.bp": []byte(provider_bp_b), + "c/Android.bp": []byte(lib_bp_a), + "c/Lib.java": {}, + "d/Android.bp": []byte(lib_bp_b), + "d/Lib.java": {}, + }, + ), + android.FixtureMergeEnv( + map[string]string{ + "DISABLE_STUB_VALIDATION": "true", + }, + ), + ).RunTestWithBp(t, ` java_api_library { name: "bar1", api_surface: "public", api_contributions: ["foo1"], static_libs: ["lib1"], + stubs_type: "everything", } java_api_library { @@ -2173,16 +2228,9 @@ func TestJavaApiLibraryStaticLibsLink(t *testing.T) { api_surface: "system", api_contributions: ["foo1", "foo2"], static_libs: ["lib1", "lib2", "bar1"], + stubs_type: "everything", } - `, - map[string][]byte{ - "a/Android.bp": []byte(provider_bp_a), - "b/Android.bp": []byte(provider_bp_b), - "c/Android.bp": []byte(lib_bp_a), - "c/Lib.java": {}, - "d/Android.bp": []byte(lib_bp_b), - "d/Lib.java": {}, - }) + `) testcases := []struct { moduleName string @@ -2208,17 +2256,19 @@ func TestJavaApiLibraryStaticLibsLink(t *testing.T) { } } -func TestJavaApiLibraryDepApiSrcs(t *testing.T) { +func TestJavaApiLibraryFullApiSurfaceStub(t *testing.T) { provider_bp_a := ` java_api_contribution { name: "foo1", - api_file: "foo1.txt", + api_file: "current.txt", + api_surface: "public", } ` provider_bp_b := ` java_api_contribution { name: "foo2", - api_file: "foo2.txt", + api_file: "current.txt", + api_surface: "public", } ` lib_bp_a := ` @@ -2226,30 +2276,62 @@ func TestJavaApiLibraryDepApiSrcs(t *testing.T) { name: "lib1", api_surface: "public", api_contributions: ["foo1", "foo2"], + stubs_type: "everything", } ` - ctx, _ := testJavaWithFS(t, ` + ctx := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureMergeMockFs( + map[string][]byte{ + "a/Android.bp": []byte(provider_bp_a), + "b/Android.bp": []byte(provider_bp_b), + "c/Android.bp": []byte(lib_bp_a), + }, + ), + android.FixtureMergeEnv( + map[string]string{ + "DISABLE_STUB_VALIDATION": "true", + }, + ), + ).RunTestWithBp(t, ` java_api_library { name: "bar1", api_surface: "public", api_contributions: ["foo1"], - dep_api_srcs: "lib1", + full_api_surface_stub: "lib1", + stubs_type: "everything", } - `, - map[string][]byte{ - "a/Android.bp": []byte(provider_bp_a), - "b/Android.bp": []byte(provider_bp_b), - "c/Android.bp": []byte(lib_bp_a), - }) + `) m := ctx.ModuleForTests("bar1", "android_common") manifest := m.Output("metalava.sbox.textproto") - sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest) + sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, manifest) manifestCommand := sboxProto.Commands[0].GetCommand() + android.AssertStringDoesContain(t, "Command expected to contain full_api_surface_stub output jar", manifestCommand, "lib1.jar") +} - android.AssertStringDoesContain(t, "Command expected to contain module srcjar file", manifestCommand, "bar1-stubs.srcjar") - android.AssertStringDoesContain(t, "Command expected to contain output files list text file flag", manifestCommand, "--out __SBOX_SANDBOX_DIR__/out/sources.txt") +func TestTransitiveSrcFiles(t *testing.T) { + ctx, _ := testJava(t, ` + java_library { + name: "a", + srcs: ["a.java"], + } + java_library { + name: "b", + srcs: ["b.java"], + } + java_library { + name: "c", + srcs: ["c.java"], + libs: ["a"], + static_libs: ["b"], + } + `) + c := ctx.ModuleForTests("c", "android_common").Module() + javaInfo, _ := android.SingletonModuleProvider(ctx, c, JavaInfoProvider) + transitiveSrcFiles := android.Paths(javaInfo.TransitiveSrcFiles.ToList()) + android.AssertArrayString(t, "unexpected jar deps", []string{"b.java", "c.java"}, transitiveSrcFiles.Strings()) } func TestTradefedOptions(t *testing.T) { @@ -2299,3 +2381,664 @@ java_test_host { t.Errorf("Expected args[\"extraTestRunnerConfigs\"] to equal %q, was %q", expected, args["extraTestRunnerConfigs"]) } } + +func TestJavaExcludeStaticLib(t *testing.T) { + ctx, _ := testJava(t, ` + java_library { + name: "bar", + } + java_library { + name: "foo", + } + java_library { + name: "baz", + static_libs: [ + "foo", + "bar", + ], + exclude_static_libs: [ + "bar", + ], + } + `) + + // "bar" not included as dependency of "baz" + CheckModuleDependencies(t, ctx, "baz", "android_common", []string{ + `core-lambda-stubs`, + `ext`, + `foo`, + `framework`, + `stable-core-platform-api-stubs-system-modules`, + `stable.core.platform.api.stubs`, + }) +} + +func TestJavaLibraryWithResourcesStem(t *testing.T) { + ctx, _ := testJavaWithFS(t, ` + java_library { + name: "foo", + java_resource_dirs: ["test-jar"], + stem: "test", + } + `, + map[string][]byte{ + "test-jar/test/resource.txt": nil, + }) + + m := ctx.ModuleForTests("foo", "android_common") + outputs := fmt.Sprint(m.AllOutputs()) + if !strings.Contains(outputs, "test.jar") { + t.Errorf("Module output does not contain expected jar %s", "test.jar") + } +} + +func TestHeadersOnly(t *testing.T) { + ctx, _ := testJava(t, ` + java_library { + name: "foo", + srcs: ["a.java"], + headers_only: true, + } + `) + + turbine := ctx.ModuleForTests("foo", "android_common").Rule("turbine") + if len(turbine.Inputs) != 1 || turbine.Inputs[0].String() != "a.java" { + t.Errorf(`foo inputs %v != ["a.java"]`, turbine.Inputs) + } + + javac := ctx.ModuleForTests("foo", "android_common").MaybeRule("javac") + android.AssertDeepEquals(t, "javac rule", nil, javac.Rule) +} + +func TestJavaApiContributionImport(t *testing.T) { + ctx := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureMergeEnv( + map[string]string{ + "DISABLE_STUB_VALIDATION": "true", + }, + ), + ).RunTestWithBp(t, ` + java_api_library { + name: "foo", + api_contributions: ["bar"], + stubs_type: "everything", + } + java_api_contribution_import { + name: "bar", + api_file: "current.txt", + api_surface: "public", + } + `) + m := ctx.ModuleForTests("foo", "android_common") + manifest := m.Output("metalava.sbox.textproto") + sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, manifest) + manifestCommand := sboxProto.Commands[0].GetCommand() + sourceFilesFlag := "--source-files current.txt" + android.AssertStringDoesContain(t, "source text files not present", manifestCommand, sourceFilesFlag) +} + +func TestJavaApiLibraryApiFilesSorting(t *testing.T) { + ctx, _ := testJava(t, ` + java_api_library { + name: "foo", + api_contributions: [ + "system-server-api-stubs-docs-non-updatable.api.contribution", + "test-api-stubs-docs-non-updatable.api.contribution", + "system-api-stubs-docs-non-updatable.api.contribution", + "module-lib-api-stubs-docs-non-updatable.api.contribution", + "api-stubs-docs-non-updatable.api.contribution", + ], + stubs_type: "everything", + } + `) + m := ctx.ModuleForTests("foo", "android_common") + manifest := m.Output("metalava.sbox.textproto") + sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, manifest) + manifestCommand := sboxProto.Commands[0].GetCommand() + + // Api files are sorted from the narrowest api scope to the widest api scope. + // test api and module lib api surface do not have subset/superset relationship, + // but they will never be passed as inputs at the same time. + sourceFilesFlag := "--source-files default/java/api/current.txt " + + "default/java/api/system-current.txt default/java/api/test-current.txt " + + "default/java/api/module-lib-current.txt default/java/api/system-server-current.txt" + android.AssertStringDoesContain(t, "source text files not in api scope order", manifestCommand, sourceFilesFlag) +} + +func TestSdkLibraryProvidesSystemModulesToApiLibrary(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis("foo"), + android.FixtureModifyConfig(func(config android.Config) { + config.SetApiLibraries([]string{"foo"}) + }), + android.FixtureMergeMockFs( + map[string][]byte{ + "A.java": nil, + }, + ), + ).RunTestWithBp(t, ` + java_library { + name: "bar", + srcs: ["a.java"], + } + java_system_modules { + name: "baz", + libs: ["bar"], + } + java_sdk_library { + name: "foo", + srcs: ["A.java"], + system_modules: "baz", + } + `) + m := result.ModuleForTests(apiScopePublic.apiLibraryModuleName("foo"), "android_common") + manifest := m.Output("metalava.sbox.textproto") + sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest) + manifestCommand := sboxProto.Commands[0].GetCommand() + classPathFlag := "--classpath __SBOX_SANDBOX_DIR__/out/soong/.intermediates/bar/android_common/turbine-combined/bar.jar" + android.AssertStringDoesContain(t, "command expected to contain classpath flag", manifestCommand, classPathFlag) +} + +func TestApiLibraryDroidstubsDependency(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis("foo"), + android.FixtureModifyConfig(func(config android.Config) { + config.SetApiLibraries([]string{"foo"}) + }), + android.FixtureMergeMockFs( + map[string][]byte{ + "A.java": nil, + }, + ), + ).RunTestWithBp(t, ` + java_api_library { + name: "foo", + api_contributions: [ + "api-stubs-docs-non-updatable.api.contribution", + ], + enable_validation: true, + stubs_type: "everything", + } + java_api_library { + name: "bar", + api_contributions: [ + "api-stubs-docs-non-updatable.api.contribution", + ], + enable_validation: false, + stubs_type: "everything", + } + `) + + currentApiTimestampPath := "api-stubs-docs-non-updatable/android_common/everything/check_current_api.timestamp" + foo := result.ModuleForTests("foo", "android_common").Module().(*ApiLibrary) + fooValidationPathsString := strings.Join(foo.validationPaths.Strings(), " ") + bar := result.ModuleForTests("bar", "android_common").Module().(*ApiLibrary) + barValidationPathsString := strings.Join(bar.validationPaths.Strings(), " ") + android.AssertStringDoesContain(t, + "Module expected to have validation", + fooValidationPathsString, + currentApiTimestampPath, + ) + android.AssertStringDoesNotContain(t, + "Module expected to not have validation", + barValidationPathsString, + currentApiTimestampPath, + ) +} + +func TestDisableFromTextStubForCoverageBuild(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + PrepareForTestWithJacocoInstrumentation, + FixtureWithLastReleaseApis("foo"), + android.FixtureModifyConfig(func(config android.Config) { + config.SetApiLibraries([]string{"foo"}) + config.SetBuildFromTextStub(true) + }), + android.FixtureModifyEnv(func(env map[string]string) { + env["EMMA_INSTRUMENT"] = "true" + }), + ).RunTestWithBp(t, ` + java_sdk_library { + name: "foo", + srcs: ["A.java"], + } + `) + android.AssertBoolEquals(t, "stub module expected to depend on from-source stub", + true, CheckModuleHasDependency(t, result.TestContext, + apiScopePublic.stubsLibraryModuleName("foo"), "android_common", + apiScopePublic.sourceStubLibraryModuleName("foo"))) + + android.AssertBoolEquals(t, "stub module expected to not depend on from-text stub", + false, CheckModuleHasDependency(t, result.TestContext, + apiScopePublic.stubsLibraryModuleName("foo"), "android_common", + apiScopePublic.apiLibraryModuleName("foo"))) +} + +func TestMultiplePrebuilts(t *testing.T) { + bp := ` + // an rdep + java_library { + name: "foo", + libs: ["bar"], + } + + // multiple variations of dep + // source + java_library { + name: "bar", + srcs: ["bar.java"], + } + // prebuilt "v1" + java_import { + name: "bar", + jars: ["bar.jar"], + } + // prebuilt "v2" + java_import { + name: "bar.v2", + source_module_name: "bar", + jars: ["bar.v1.jar"], + } + + // selectors + apex_contributions { + name: "myapex_contributions", + contents: ["%v"], + } + ` + hasDep := func(ctx *android.TestResult, m android.Module, wantDep android.Module) bool { + t.Helper() + var found bool + ctx.VisitDirectDeps(m, func(dep blueprint.Module) { + if dep == wantDep { + found = true + } + }) + return found + } + + hasFileWithStem := func(m android.TestingModule, stem string) bool { + t.Helper() + for _, o := range m.AllOutputs() { + _, file := filepath.Split(o) + if file == stem+".jar" { + return true + } + } + return false + } + + testCases := []struct { + desc string + selectedDependencyName string + expectedDependencyName string + }{ + { + desc: "Source library is selected using apex_contributions", + selectedDependencyName: "bar", + expectedDependencyName: "bar", + }, + { + desc: "Prebuilt library v1 is selected using apex_contributions", + selectedDependencyName: "prebuilt_bar", + expectedDependencyName: "prebuilt_bar", + }, + { + desc: "Prebuilt library v2 is selected using apex_contributions", + selectedDependencyName: "prebuilt_bar.v2", + expectedDependencyName: "prebuilt_bar.v2", + }, + } + + for _, tc := range testCases { + ctx := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildFlags = map[string]string{ + "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions", + } + }), + ).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName)) + + // check that rdep gets the correct variation of dep + foo := ctx.ModuleForTests("foo", "android_common") + expectedDependency := ctx.ModuleForTests(tc.expectedDependencyName, "android_common") + android.AssertBoolEquals(t, fmt.Sprintf("expected dependency from %s to %s\n", foo.Module().Name(), tc.expectedDependencyName), true, hasDep(ctx, foo.Module(), expectedDependency.Module())) + + // check that output file of dep is always bar.jar + // The filename should be agnostic to source/prebuilt/prebuilt_version + android.AssertBoolEquals(t, fmt.Sprintf("could not find bar.jar in outputs of %s. All Outputs %v\n", tc.expectedDependencyName, expectedDependency.AllOutputs()), true, hasFileWithStem(expectedDependency, "bar")) + + // check LOCAL_MODULE of the selected module name + // the prebuilt should have the same LOCAL_MODULE when exported to make + entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, expectedDependency.Module())[0] + android.AssertStringEquals(t, "unexpected LOCAL_MODULE", "bar", entries.EntryMap["LOCAL_MODULE"][0]) + } +} + +func TestMultiplePlatformCompatConfigPrebuilts(t *testing.T) { + bp := ` + // multiple variations of platform_compat_config + // source + platform_compat_config { + name: "myconfig", + } + // prebuilt "v1" + prebuilt_platform_compat_config { + name: "myconfig", + metadata: "myconfig.xml", + } + // prebuilt "v2" + prebuilt_platform_compat_config { + name: "myconfig.v2", + source_module_name: "myconfig", // without source_module_name, the singleton will merge two .xml files + metadata: "myconfig.v2.xml", + } + + // selectors + apex_contributions { + name: "myapex_contributions", + contents: ["%v"], + } + ` + testCases := []struct { + desc string + selectedDependencyName string + expectedPlatformCompatConfigXml string + }{ + { + desc: "Source platform_compat_config is selected using apex_contributions", + selectedDependencyName: "myconfig", + expectedPlatformCompatConfigXml: "out/soong/.intermediates/myconfig/android_common/myconfig_meta.xml", + }, + { + desc: "Prebuilt platform_compat_config v1 is selected using apex_contributions", + selectedDependencyName: "prebuilt_myconfig", + expectedPlatformCompatConfigXml: "myconfig.xml", + }, + { + desc: "Prebuilt platform_compat_config v2 is selected using apex_contributions", + selectedDependencyName: "prebuilt_myconfig.v2", + expectedPlatformCompatConfigXml: "myconfig.v2.xml", + }, + } + + for _, tc := range testCases { + ctx := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithPlatformCompatConfig, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildFlags = map[string]string{ + "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions", + } + }), + ).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName)) + + mergedGlobalConfig := ctx.SingletonForTests("platform_compat_config_singleton").Output("compat_config/merged_compat_config.xml") + android.AssertIntEquals(t, "The merged compat config file should only have a single dependency", 1, len(mergedGlobalConfig.Implicits)) + android.AssertStringEquals(t, "The merged compat config file is missing the appropriate platform compat config", mergedGlobalConfig.Implicits[0].String(), tc.expectedPlatformCompatConfigXml) + } +} + +func TestApiLibraryAconfigDeclarations(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + }), + android.FixtureMergeMockFs(map[string][]byte{ + "a/A.java": nil, + "a/current.txt": nil, + "a/removed.txt": nil, + }), + ).RunTestWithBp(t, ` + aconfig_declarations { + name: "bar", + package: "com.example.package", + container: "com.android.foo", + srcs: [ + "bar.aconfig", + ], + } + java_api_contribution { + name: "baz", + api_file: "a/current.txt", + api_surface: "public", + } + java_api_library { + name: "foo", + api_surface: "public", + api_contributions: [ + "baz", + ], + aconfig_declarations: [ + "bar", + ], + stubs_type: "exportable", + enable_validation: false, + } + `) + + // Check if java_api_library depends on aconfig_declarations + android.AssertBoolEquals(t, "foo expected to depend on bar", + CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar"), true) + + m := result.ModuleForTests("foo", "android_common") + android.AssertStringDoesContain(t, "foo generates revert annotations file", + strings.Join(m.AllOutputs(), ""), "revert-annotations-exportable.txt") + + // revert-annotations.txt passed to exportable stubs generation metalava command + manifest := m.Output("metalava.sbox.textproto") + cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command) + android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "revert-annotations-exportable.txt") +} + +func TestTestOnly(t *testing.T) { + t.Parallel() + ctx := android.GroupFixturePreparers( + prepareForJavaTest, + ).RunTestWithBp(t, ` + // These should be test-only + java_library { + name: "lib1-test-only", + srcs: ["a.java"], + test_only: true, + } + java_test { + name: "java-test", + } + java_test_host { + name: "java-test-host", + } + java_test_helper_library { + name: "helper-library", + } + java_binary { + name: "java-data-binary", + srcs: ["foo.java"], + main_class: "foo.bar.jb", + test_only: true, + } + + // These are NOT + java_library { + name: "lib2-app", + srcs: ["b.java"], + } + java_import { + name: "bar", + jars: ["bar.jar"], + } + java_binary { + name: "java-binary", + srcs: ["foo.java"], + main_class: "foo.bar.jb", + } + `) + + expectedTestOnlyModules := []string{ + "lib1-test-only", + "java-test", + "java-test-host", + "helper-library", + "java-data-binary", + } + expectedTopLevelTests := []string{ + "java-test", + "java-test-host", + } + assertTestOnlyAndTopLevel(t, ctx, expectedTestOnlyModules, expectedTopLevelTests) +} + +// Don't allow setting test-only on things that are always tests or never tests. +func TestInvalidTestOnlyTargets(t *testing.T) { + testCases := []string{ + ` java_test { name: "java-test", test_only: true, srcs: ["foo.java"], } `, + ` java_test_host { name: "java-test-host", test_only: true, srcs: ["foo.java"], } `, + ` java_test_import { name: "java-test-import", test_only: true, } `, + ` java_api_library { name: "java-api-library", test_only: true, } `, + ` java_test_helper_library { name: "test-help-lib", test_only: true, } `, + ` java_defaults { name: "java-defaults", test_only: true, } `, + } + + for i, bp := range testCases { + android.GroupFixturePreparers(prepareForJavaTest). + ExtendWithErrorHandler( + expectOneError("unrecognized property \"test_only\"", + fmt.Sprintf("testcase: %d", i))). + RunTestWithBp(t, bp) + } +} + +// Expect exactly one that matches 'expected'. +// Append 'msg' to the Errorf that printed. +func expectOneError(expected string, msg string) android.FixtureErrorHandler { + return android.FixtureCustomErrorHandler(func(t *testing.T, result *android.TestResult) { + t.Helper() + if len(result.Errs) != 1 { + t.Errorf("Expected exactly one error, but found: %d when setting test_only on: %s", len(result.Errs), msg) + return + } + actualErrMsg := result.Errs[0].Error() + if !strings.Contains(actualErrMsg, expected) { + t.Errorf("Different error than expected. Received: [%v] on %s expected: %s", actualErrMsg, msg, expected) + } + }) +} + +func TestJavaLibHostWithStem(t *testing.T) { + ctx, _ := testJava(t, ` + java_library_host { + name: "foo", + srcs: ["a.java"], + stem: "foo-new", + } + `) + + buildOS := ctx.Config().BuildOS.String() + foo := ctx.ModuleForTests("foo", buildOS+"_common") + + outputs := fmt.Sprint(foo.AllOutputs()) + if !strings.Contains(outputs, "foo-new.jar") { + t.Errorf("Module output does not contain expected jar %s", "foo-new.jar") + } +} + +func TestJavaLibWithStem(t *testing.T) { + ctx, _ := testJava(t, ` + java_library { + name: "foo", + srcs: ["a.java"], + stem: "foo-new", + } + `) + + foo := ctx.ModuleForTests("foo", "android_common") + + outputs := fmt.Sprint(foo.AllOutputs()) + if !strings.Contains(outputs, "foo-new.jar") { + t.Errorf("Module output does not contain expected jar %s", "foo-new.jar") + } +} + +func TestJavaLibraryOutputFilesRel(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + ).RunTestWithBp(t, ` + java_library { + name: "foo", + srcs: ["a.java"], + } + + java_import { + name: "bar", + jars: ["bar.aar"], + + } + + java_import { + name: "baz", + jars: ["baz.aar"], + static_libs: ["bar"], + } + `) + + foo := result.ModuleForTests("foo", "android_common") + bar := result.ModuleForTests("bar", "android_common") + baz := result.ModuleForTests("baz", "android_common") + + fooOutputPaths := foo.OutputFiles(t, "") + barOutputPaths := bar.OutputFiles(t, "") + bazOutputPaths := baz.OutputFiles(t, "") + + android.AssertPathsRelativeToTopEquals(t, "foo output path", + []string{"out/soong/.intermediates/foo/android_common/javac/foo.jar"}, fooOutputPaths) + android.AssertPathsRelativeToTopEquals(t, "bar output path", + []string{"out/soong/.intermediates/bar/android_common/combined/bar.jar"}, barOutputPaths) + android.AssertPathsRelativeToTopEquals(t, "baz output path", + []string{"out/soong/.intermediates/baz/android_common/combined/baz.jar"}, bazOutputPaths) + + android.AssertStringEquals(t, "foo relative output path", + "foo.jar", fooOutputPaths[0].Rel()) + android.AssertStringEquals(t, "bar relative output path", + "bar.jar", barOutputPaths[0].Rel()) + android.AssertStringEquals(t, "baz relative output path", + "baz.jar", bazOutputPaths[0].Rel()) +} + +func assertTestOnlyAndTopLevel(t *testing.T, ctx *android.TestResult, expectedTestOnly []string, expectedTopLevel []string) { + t.Helper() + actualTrueModules := []string{} + actualTopLevelTests := []string{} + addActuals := func(m blueprint.Module, key blueprint.ProviderKey[android.TestModuleInformation]) { + if provider, ok := android.OtherModuleProvider(ctx.TestContext.OtherModuleProviderAdaptor(), m, key); ok { + if provider.TestOnly { + actualTrueModules = append(actualTrueModules, m.Name()) + } + if provider.TopLevelTarget { + actualTopLevelTests = append(actualTopLevelTests, m.Name()) + } + } + } + + ctx.VisitAllModules(func(m blueprint.Module) { + addActuals(m, android.TestOnlyProviderKey) + + }) + + notEqual, left, right := android.ListSetDifference(expectedTestOnly, actualTrueModules) + if notEqual { + t.Errorf("test-only: Expected but not found: %v, Found but not expected: %v", left, right) + } + + notEqual, left, right = android.ListSetDifference(expectedTopLevel, actualTopLevelTests) + if notEqual { + t.Errorf("top-level: Expected but not found: %v, Found but not expected: %v", left, right) + } +} diff --git a/java/jdeps.go b/java/jdeps.go index a52b86708..340026318 100644 --- a/java/jdeps.go +++ b/java/jdeps.go @@ -26,7 +26,7 @@ import ( // called. Dependency info file is generated in $OUT/module_bp_java_depend.json. func init() { - android.RegisterSingletonType("jdeps_generator", jDepsGeneratorSingleton) + android.RegisterParallelSingletonType("jdeps_generator", jDepsGeneratorSingleton) } func jDepsGeneratorSingleton() android.Singleton { @@ -48,7 +48,7 @@ func (j *jdepsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonCont moduleInfos := make(map[string]android.IdeInfo) ctx.VisitAllModules(func(module android.Module) { - if !module.Enabled() { + if !module.Enabled(ctx) { return } @@ -75,7 +75,7 @@ func (j *jdepsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonCont dpInfo.Jarjar_rules = android.FirstUniqueStrings(dpInfo.Jarjar_rules) dpInfo.Jars = android.FirstUniqueStrings(dpInfo.Jars) dpInfo.SrcJars = android.FirstUniqueStrings(dpInfo.SrcJars) - dpInfo.Paths = android.FirstUniqueStrings(dpInfo.Paths) + dpInfo.Paths = []string{ctx.ModuleDir(module)} dpInfo.Static_libs = android.FirstUniqueStrings(dpInfo.Static_libs) dpInfo.Libs = android.FirstUniqueStrings(dpInfo.Libs) moduleInfos[name] = dpInfo @@ -89,8 +89,7 @@ func (j *jdepsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonCont dpInfo.Classes = append(dpInfo.Classes, data.Class) } - if ctx.ModuleHasProvider(module, JavaInfoProvider) { - dep := ctx.ModuleProvider(module, JavaInfoProvider).(JavaInfo) + if dep, ok := android.SingletonModuleProvider(ctx, module, JavaInfoProvider); ok { dpInfo.Installed_paths = append(dpInfo.Installed_paths, dep.ImplementationJars.Strings()...) } dpInfo.Classes = android.FirstUniqueStrings(dpInfo.Classes) diff --git a/java/kotlin.go b/java/kotlin.go index 9bff5ea01..aa2db0ecd 100644 --- a/java/kotlin.go +++ b/java/kotlin.go @@ -30,7 +30,7 @@ var kotlinc = pctx.AndroidRemoteStaticRule("kotlinc", android.RemoteRuleSupports blueprint.RuleParams{ 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.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" -f "*.kt" $srcJars && ` + `${config.GenKotlinBuildFileCmd} --classpath "$classpath" --name "$name"` + ` --out_dir "$classesDir" --srcs "$out.rsp" --srcs "$srcJarDir/list"` + ` $commonSrcFilesArg --out "$kotlinBuildFile" && ` + @@ -42,7 +42,7 @@ var kotlinc = pctx.AndroidRemoteStaticRule("kotlinc", android.RemoteRuleSupports ` -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"`, + `rm -rf "$srcJarDir" "$classesDir" "$headerClassesDir"`, CommandDeps: []string{ "${config.KotlincCmd}", "${config.KotlinCompilerJar}", @@ -145,7 +145,7 @@ var kaptStubs = pctx.AndroidRemoteStaticRule("kaptStubs", android.RemoteRuleSupp `$kaptProcessorPath ` + `$kaptProcessor ` + `-Xbuild-file=$kotlinBuildFile && ` + - `${config.SoongZipCmd} -jar -o $out -C $kaptDir/stubs -D $kaptDir/stubs && ` + + `${config.SoongZipCmd} -jar -write_if_changed -o $out -C $kaptDir/stubs -D $kaptDir/stubs && ` + `rm -rf "$srcJarDir"`, CommandDeps: []string{ "${config.KotlincCmd}", @@ -157,6 +157,7 @@ var kaptStubs = pctx.AndroidRemoteStaticRule("kaptStubs", android.RemoteRuleSupp }, Rspfile: "$out.rsp", RspfileContent: `$in`, + Restat: true, }, "kotlincFlags", "encodedJavacFlags", "kaptProcessorPath", "kaptProcessor", "classpath", "srcJars", "commonSrcFilesArg", "srcJarDir", "kaptDir", "kotlinJvmTarget", diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go index 6cb549eaf..4be7d0470 100644 --- a/java/legacy_core_platform_api_usage.go +++ b/java/legacy_core_platform_api_usage.go @@ -28,6 +28,7 @@ var legacyCorePlatformApiModules = []string{ "FloralClocks", "framework-jobscheduler", "framework-minus-apex", + "framework-minus-apex-headers", "framework-minus-apex-intdefs", "FrameworksCoreTests", "HelloOslo", diff --git a/java/lint.go b/java/lint.go index 40ef48416..2eea07d31 100644 --- a/java/lint.go +++ b/java/lint.go @@ -17,7 +17,6 @@ package java import ( "fmt" "sort" - "strconv" "strings" "github.com/google/blueprint/proptools" @@ -56,7 +55,8 @@ type LintProperties struct { // Modules that provide extra lint checks Extra_check_modules []string - // Name of the file that lint uses as the baseline. Defaults to "lint-baseline.xml". + // The lint baseline file to use. If specified, lint warnings listed in this file will be + // suppressed during lint checks. Baseline_filename *string // If true, baselining updatability lint checks (e.g. NewApi) is prohibited. Defaults to false. @@ -66,6 +66,10 @@ type LintProperties struct { // This will be true by default for test module types, false otherwise. // If soong gets support for testonly, this flag should be replaced with that. Test *bool + + // Whether to ignore the exit code of Android lint. This is the --exit_code + // option. Defaults to false. + Suppress_exit_code *bool } } @@ -80,15 +84,16 @@ type linter struct { classes android.Path extraLintCheckJars android.Paths library bool - minSdkVersion int - targetSdkVersion int - compileSdkVersion int + minSdkVersion android.ApiLevel + targetSdkVersion android.ApiLevel + compileSdkVersion android.ApiLevel compileSdkKind android.SdkKind javaLanguageLevel string kotlinLanguageLevel string outputs lintOutputs properties LintProperties extraMainlineLintErrors []string + compile_data android.Paths reports android.Paths @@ -117,18 +122,18 @@ type LintDepSetsIntf interface { } type LintDepSets struct { - HTML, Text, XML *android.DepSet + HTML, Text, XML *android.DepSet[android.Path] } type LintDepSetsBuilder struct { - HTML, Text, XML *android.DepSetBuilder + HTML, Text, XML *android.DepSetBuilder[android.Path] } func NewLintDepSetBuilder() LintDepSetsBuilder { return LintDepSetsBuilder{ - HTML: android.NewDepSetBuilder(android.POSTORDER), - Text: android.NewDepSetBuilder(android.POSTORDER), - XML: android.NewDepSetBuilder(android.POSTORDER), + HTML: android.NewDepSetBuilder[android.Path](android.POSTORDER), + Text: android.NewDepSetBuilder[android.Path](android.POSTORDER), + XML: android.NewDepSetBuilder[android.Path](android.POSTORDER), } } @@ -314,25 +319,19 @@ func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.Ru cmd.FlagWithInput("@", android.PathForSource(ctx, "build/soong/java/lint_defaults.txt")) - if l.compileSdkKind == android.SdkPublic { - cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors) - } else { - // TODO(b/268261262): Remove this branch. We're demoting NewApi to a warning due to pre-existing issues that need to be fixed. - cmd.FlagForEachArg("--warning_check ", l.extraMainlineLintErrors) - } + cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors) cmd.FlagForEachArg("--disable_check ", l.properties.Lint.Disabled_checks) cmd.FlagForEachArg("--warning_check ", l.properties.Lint.Warning_checks) cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks) cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks) - // TODO(b/193460475): Re-enable strict updatability linting - //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()) - // cmd.FlagForEachArg("--disallowed_issues ", updatabilityChecks) - // } - //} + if l.GetStrictUpdatabilityLinting() { + // Verify the module does not baseline issues that endanger safe updatability. + if l.properties.Lint.Baseline_filename != nil { + cmd.FlagWithInput("--baseline ", android.PathForModuleSrc(ctx, *l.properties.Lint.Baseline_filename)) + cmd.FlagForEachArg("--disallowed_issues ", updatabilityChecks) + } + } return lintPaths{ projectXML: projectXMLPath, @@ -352,33 +351,26 @@ func (l *linter) generateManifest(ctx android.ModuleContext, rule *android.RuleB Text(`echo "<?xml version='1.0' encoding='utf-8'?>" &&`). 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='%d' android:targetSdkVersion='%d'/>" &&`, - l.minSdkVersion, l.targetSdkVersion). + Textf(`echo " <uses-sdk android:minSdkVersion='%s' android:targetSdkVersion='%s'/>" &&`, + l.minSdkVersion.String(), l.targetSdkVersion.String()). Text(`echo "</manifest>"`). Text(") >").Output(manifestPath) return manifestPath } -func (l *linter) getBaselineFilepath(ctx android.ModuleContext) android.OptionalPath { - var lintBaseline android.OptionalPath - if lintFilename := proptools.StringDefault(l.properties.Lint.Baseline_filename, "lint-baseline.xml"); lintFilename != "" { - if String(l.properties.Lint.Baseline_filename) != "" { - // if manually specified, we require the file to exist - lintBaseline = android.OptionalPathForPath(android.PathForModuleSrc(ctx, lintFilename)) - } else { - lintBaseline = android.ExistentPathForSource(ctx, ctx.ModuleDir(), lintFilename) - } - } - return lintBaseline -} - func (l *linter) lint(ctx android.ModuleContext) { if !l.enabled() { return } - if l.minSdkVersion != l.compileSdkVersion { + for _, flag := range l.properties.Lint.Flags { + if strings.Contains(flag, "--disable") || strings.Contains(flag, "--enable") || strings.Contains(flag, "--check") { + ctx.PropertyErrorf("lint.flags", "Don't use --disable, --enable, or --check in the flags field, instead use the dedicated disabled_checks, warning_checks, error_checks, or fatal_checks fields") + } + } + + if l.minSdkVersion.CompareTo(l.compileSdkVersion) == -1 { l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, updatabilityChecks...) // Skip lint warning checks for NewApi warnings for libcore where they come from source // files that reference the API they are adding (b/208656169). @@ -408,8 +400,7 @@ func (l *linter) lint(ctx android.ModuleContext) { extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag) for _, extraLintCheckModule := range extraLintCheckModules { - if ctx.OtherModuleHasProvider(extraLintCheckModule, JavaInfoProvider) { - dep := ctx.OtherModuleProvider(extraLintCheckModule, JavaInfoProvider).(JavaInfo) + if dep, ok := android.OtherModuleProvider(ctx, extraLintCheckModule, JavaInfoProvider); ok { l.extraLintCheckJars = append(l.extraLintCheckJars, dep.ImplementationAndResourcesJars...) } else { ctx.PropertyErrorf("lint.extra_check_modules", @@ -444,7 +435,7 @@ func (l *linter) lint(ctx android.ModuleContext) { srcsList := android.PathForModuleOut(ctx, "lint", "lint-srcs.list") srcsListRsp := android.PathForModuleOut(ctx, "lint-srcs.list.rsp") - rule.Command().Text("cp").FlagWithRspFileInputList("", srcsListRsp, l.srcs).Output(srcsList) + rule.Command().Text("cp").FlagWithRspFileInputList("", srcsListRsp, l.srcs).Output(srcsList).Implicits(l.compile_data) lintPaths := l.writeLintProjectXML(ctx, rule, srcsList) @@ -487,12 +478,13 @@ func (l *linter) lint(ctx android.ModuleContext) { cmd.BuiltTool("lint").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "lint.jar")). Flag("--quiet"). + Flag("--include-aosp-issues"). FlagWithInput("--project ", lintPaths.projectXML). FlagWithInput("--config ", lintPaths.configXML). FlagWithOutput("--html ", html). FlagWithOutput("--text ", text). FlagWithOutput("--xml ", xml). - FlagWithArg("--compile-sdk-version ", strconv.Itoa(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())). @@ -504,7 +496,8 @@ func (l *linter) lint(ctx android.ModuleContext) { rule.Temporary(lintPaths.projectXML) rule.Temporary(lintPaths.configXML) - if exitCode := ctx.Config().Getenv("ANDROID_LINT_SUPPRESS_EXIT_CODE"); exitCode == "" { + suppressExitCode := BoolDefault(l.properties.Lint.Suppress_exit_code, false) + if exitCode := ctx.Config().Getenv("ANDROID_LINT_SUPPRESS_EXIT_CODE"); exitCode == "" && !suppressExitCode { cmd.Flag("--exitcode") } @@ -512,9 +505,8 @@ func (l *linter) lint(ctx android.ModuleContext) { cmd.FlagWithArg("--check ", checkOnly) } - lintBaseline := l.getBaselineFilepath(ctx) - if lintBaseline.Valid() { - cmd.FlagWithInput("--baseline ", lintBaseline.Path()) + if l.properties.Lint.Baseline_filename != nil { + cmd.FlagWithInput("--baseline ", android.PathForModuleSrc(ctx, *l.properties.Lint.Baseline_filename)) } cmd.FlagWithOutput("--write-reference-baseline ", referenceBaseline) @@ -550,12 +542,16 @@ func (l *linter) lint(ctx android.ModuleContext) { if l.buildModuleReportZip { l.reports = BuildModuleLintReportZips(ctx, l.LintDepSets()) } + + // Create a per-module phony target to run the lint check. + phonyName := ctx.ModuleName() + "-lint" + ctx.Phony(phonyName, xml) } func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets) android.Paths { - htmlList := depSets.HTML.ToSortedList() - textList := depSets.Text.ToSortedList() - xmlList := depSets.XML.ToSortedList() + htmlList := android.SortedUniquePaths(depSets.HTML.ToList()) + textList := android.SortedUniquePaths(depSets.Text.ToList()) + xmlList := android.SortedUniquePaths(depSets.XML.ToList()) if len(htmlList) == 0 && len(textList) == 0 && len(xmlList) == 0 { return nil @@ -610,7 +606,7 @@ func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) { apiVersionsDb := findModuleOrErr(ctx, files.apiVersionsModule) if apiVersionsDb == nil { if !ctx.Config().AllowMissingDependencies() { - ctx.Errorf("lint: missing module api_versions_public") + ctx.Errorf("lint: missing module %s", files.apiVersionsModule) } return } @@ -618,7 +614,7 @@ func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) { sdkAnnotations := findModuleOrErr(ctx, files.annotationsModule) if sdkAnnotations == nil { if !ctx.Config().AllowMissingDependencies() { - ctx.Errorf("lint: missing module sdk-annotations.zip") + ctx.Errorf("lint: missing module %s", files.annotationsModule) } return } @@ -654,7 +650,7 @@ func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) { } if apex, ok := m.(android.ApexModule); ok && apex.NotAvailableForPlatform() { - apexInfo := ctx.ModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo) + apexInfo, _ := android.SingletonModuleProvider(ctx, m, android.ApexInfoProvider) if apexInfo.IsForPlatform() { // There are stray platform variants of modules in apexes that are not available for // the platform, and they sometimes can't be built. Don't depend on them. @@ -705,7 +701,7 @@ func (l *lintSingleton) MakeVars(ctx android.MakeVarsContext) { var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil) func init() { - android.RegisterSingletonType("lint", + android.RegisterParallelSingletonType("lint", func() android.Singleton { return &lintSingleton{} }) registerLintBuildComponents(android.InitRegistrationContext) diff --git a/java/lint_defaults.txt b/java/lint_defaults.txt index 1bb49962c..b8ce95cc8 100644 --- a/java/lint_defaults.txt +++ b/java/lint_defaults.txt @@ -122,3 +122,17 @@ --warning_check RemoteViewLayout --warning_check SupportAnnotationUsage --warning_check UniqueConstants +--warning_check UseSdkSuppress +# TODO(b/303434307) The intent is for this to be set to error severity +# once existing violations are cleaned up +--warning_check FlaggedApi + +--warning_check ExactAlarm +--warning_check ExpiredTargetSdkVersion +--warning_check ForegroundServicePermission +--warning_check ObsoleteSdkInt +--warning_check ScheduleExactAlarm +--warning_check StartActivityAndCollapseDeprecated +--warning_check UnspecifiedRegisterReceiverFlag +--warning_check WearMaterialTheme +--warning_check WearStandaloneAppFlag diff --git a/java/lint_test.go b/java/lint_test.go index ec901aa70..b51753f71 100644 --- a/java/lint_test.go +++ b/java/lint_test.go @@ -21,7 +21,7 @@ import ( "android/soong/android" ) -func TestJavaLint(t *testing.T) { +func TestJavaLintDoesntUseBaselineImplicitly(t *testing.T) { ctx, _ := testJavaWithFS(t, ` java_library { name: "foo", @@ -39,31 +39,9 @@ func TestJavaLint(t *testing.T) { foo := ctx.ModuleForTests("foo", "android_common") - sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto")) - if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml") { - t.Error("did not pass --baseline flag") - } -} - -func TestJavaLintWithoutBaseline(t *testing.T) { - ctx, _ := testJavaWithFS(t, ` - java_library { - name: "foo", - srcs: [ - "a.java", - "b.java", - "c.java", - ], - min_sdk_version: "29", - sdk_version: "system_current", - } - `, map[string][]byte{}) - - foo := ctx.ModuleForTests("foo", "android_common") - - sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto")) - if strings.Contains(*sboxProto.Commands[0].Command, "--baseline") { - t.Error("passed --baseline flag for non existent file") + sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, foo.Output("lint.sbox.textproto")) + if strings.Contains(*sboxProto.Commands[0].Command, "--baseline lint-baseline.xml") { + t.Error("Passed --baseline flag when baseline_filename was not set") } } @@ -108,14 +86,13 @@ func TestJavaLintUsesCorrectBpConfig(t *testing.T) { foo := ctx.ModuleForTests("foo", "android_common") - sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto")) + sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, foo.Output("lint.sbox.textproto")) if !strings.Contains(*sboxProto.Commands[0].Command, "--baseline mybaseline.xml") { t.Error("did not use the correct file for baseline") } - if !strings.Contains(*sboxProto.Commands[0].Command, "--warning_check NewApi") { - // TODO(b/268261262): Change this to check for --error_check - t.Error("should check NewApi warnings") + if !strings.Contains(*sboxProto.Commands[0].Command, "--error_check NewApi") { + t.Error("should check NewApi errors") } if !strings.Contains(*sboxProto.Commands[0].Command, "--error_check SomeCheck") { @@ -175,52 +152,55 @@ func TestJavaLintBypassUpdatableChecks(t *testing.T) { } } -// TODO(b/193460475): Re-enable this test -//func TestJavaLintStrictUpdatabilityLinting(t *testing.T) { -// bp := ` -// java_library { -// name: "foo", -// srcs: [ -// "a.java", -// ], -// static_libs: ["bar"], -// min_sdk_version: "29", -// sdk_version: "current", -// lint: { -// strict_updatability_linting: true, -// }, -// } -// -// java_library { -// name: "bar", -// srcs: [ -// "a.java", -// ], -// min_sdk_version: "29", -// sdk_version: "current", -// } -// ` -// fs := android.MockFS{ -// "lint-baseline.xml": nil, -// } -// -// result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules, fs.AddToFixture()). -// RunTestWithBp(t, bp) -// -// foo := result.ModuleForTests("foo", "android_common") -// sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto")) -// if !strings.Contains(*sboxProto.Commands[0].Command, -// "--baseline lint-baseline.xml --disallowed_issues NewApi") { -// t.Error("did not restrict baselining NewApi") -// } -// -// bar := result.ModuleForTests("bar", "android_common") -// sboxProto = android.RuleBuilderSboxProtoForTests(t, bar.Output("lint.sbox.textproto")) -// if !strings.Contains(*sboxProto.Commands[0].Command, -// "--baseline lint-baseline.xml --disallowed_issues NewApi") { -// t.Error("did not restrict baselining NewApi") -// } -//} +func TestJavaLintStrictUpdatabilityLinting(t *testing.T) { + bp := ` + java_library { + name: "foo", + srcs: [ + "a.java", + ], + static_libs: ["bar"], + min_sdk_version: "29", + sdk_version: "current", + lint: { + strict_updatability_linting: true, + baseline_filename: "lint-baseline.xml", + }, + } + + java_library { + name: "bar", + srcs: [ + "a.java", + ], + min_sdk_version: "29", + sdk_version: "current", + lint: { + baseline_filename: "lint-baseline.xml", + } + } + ` + fs := android.MockFS{ + "lint-baseline.xml": nil, + } + + result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules, fs.AddToFixture()). + RunTestWithBp(t, bp) + + foo := result.ModuleForTests("foo", "android_common") + sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, foo.Output("lint.sbox.textproto")) + if !strings.Contains(*sboxProto.Commands[0].Command, + "--baseline lint-baseline.xml --disallowed_issues NewApi") { + t.Error("did not restrict baselining NewApi") + } + + bar := result.ModuleForTests("bar", "android_common") + sboxProto = android.RuleBuilderSboxProtoForTests(t, result.TestContext, bar.Output("lint.sbox.textproto")) + if !strings.Contains(*sboxProto.Commands[0].Command, + "--baseline lint-baseline.xml --disallowed_issues NewApi") { + t.Error("did not restrict baselining NewApi") + } +} func TestJavaLintDatabaseSelectionFull(t *testing.T) { testCases := []struct { @@ -276,9 +256,28 @@ func TestJavaLintDatabaseSelectionFull(t *testing.T) { RunTestWithBp(t, thisBp) foo := result.ModuleForTests("foo", "android_common") - sboxProto := android.RuleBuilderSboxProtoForTests(t, foo.Output("lint.sbox.textproto")) + sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, foo.Output("lint.sbox.textproto")) if !strings.Contains(*sboxProto.Commands[0].Command, "/"+testCase.expected_file) { t.Error("did not use full api database for case", testCase) } } } + +func TestCantControlCheckSeverityWithFlags(t *testing.T) { + bp := ` + java_library { + name: "foo", + srcs: [ + "a.java", + ], + min_sdk_version: "29", + sdk_version: "current", + lint: { + flags: ["--disabled", "NewApi"], + }, + } + ` + PrepareForTestWithJavaDefaultModules. + ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern("Don't use --disable, --enable, or --check in the flags field, instead use the dedicated disabled_checks, warning_checks, error_checks, or fatal_checks fields")). + RunTestWithBp(t, bp) +} diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index 07fb92cfe..38553a61b 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -15,8 +15,6 @@ package java import ( - "fmt" - "android/soong/android" "android/soong/dexpreopt" ) @@ -26,7 +24,7 @@ func init() { } func registerPlatformBootclasspathBuildComponents(ctx android.RegistrationContext) { - ctx.RegisterSingletonModuleType("platform_bootclasspath", platformBootclasspathFactory) + ctx.RegisterParallelSingletonModuleType("platform_bootclasspath", platformBootclasspathFactory) } // The tags used for the dependencies between the platform bootclasspath and any configured boot @@ -73,8 +71,6 @@ func platformBootclasspathFactory() android.SingletonModule { return m } -var _ android.OutputFileProducer = (*platformBootclasspathModule)(nil) - func (b *platformBootclasspathModule) AndroidMkEntries() (entries []android.AndroidMkEntries) { entries = append(entries, android.AndroidMkEntries{ Class: "FAKE", @@ -86,21 +82,10 @@ func (b *platformBootclasspathModule) AndroidMkEntries() (entries []android.Andr return } -// Make the hidden API files available from the platform-bootclasspath module. -func (b *platformBootclasspathModule) OutputFiles(tag string) (android.Paths, error) { - switch tag { - case "hiddenapi-flags.csv": - return android.Paths{b.hiddenAPIFlagsCSV}, nil - case "hiddenapi-index.csv": - return android.Paths{b.hiddenAPIIndexCSV}, nil - case "hiddenapi-metadata.csv": - return android.Paths{b.hiddenAPIMetadataCSV}, nil - } - - return nil, fmt.Errorf("unknown tag %s", tag) -} - func (b *platformBootclasspathModule) DepsMutator(ctx android.BottomUpMutatorContext) { + // Create a dependency on all_apex_contributions to determine the selected mainline module + ctx.AddDependency(ctx.Module(), apexContributionsMetadataDepTag, "all_apex_contributions") + b.hiddenAPIDepsMutator(ctx) if !dexpreopt.IsDex2oatNeeded(ctx) { @@ -113,7 +98,7 @@ func (b *platformBootclasspathModule) DepsMutator(ctx android.BottomUpMutatorCon } func (b *platformBootclasspathModule) hiddenAPIDepsMutator(ctx android.BottomUpMutatorContext) { - if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") { + if ctx.Config().DisableHiddenApiChecks() { return } @@ -123,16 +108,24 @@ func (b *platformBootclasspathModule) hiddenAPIDepsMutator(ctx android.BottomUpM } func (b *platformBootclasspathModule) BootclasspathDepsMutator(ctx android.BottomUpMutatorContext) { - // Add dependencies on all the modules configured in the "art" boot image. - artImageConfig := genBootImageConfigs(ctx)[artBootImageName] - addDependenciesOntoBootImageModules(ctx, artImageConfig.modules, platformBootclasspathArtBootJarDepTag) + // Add dependencies on all the ART jars. + global := dexpreopt.GetGlobalConfig(ctx) + addDependenciesOntoSelectedBootImageApexes(ctx, "com.android.art") + // TODO: b/308174306 - Remove the mechanism of depending on the java_sdk_library(_import) directly + addDependenciesOntoBootImageModules(ctx, global.ArtApexJars, platformBootclasspathArtBootJarDepTag) - // 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. + // Add dependencies on all the non-updatable jars, which are on the platform or in non-updatable + // APEXes. addDependenciesOntoBootImageModules(ctx, b.platformJars(ctx), platformBootclasspathBootJarDepTag) - // Add dependencies on all the apex jars. + // Add dependencies on all the updatable jars, except the ART jars. apexJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars + apexes := []string{} + for i := 0; i < apexJars.Len(); i++ { + apexes = append(apexes, apexJars.Apex(i)) + } + addDependenciesOntoSelectedBootImageApexes(ctx, android.FirstUniqueStrings(apexes)...) + // TODO: b/308174306 - Remove the mechanism of depending on the java_sdk_library(_import) directly addDependenciesOntoBootImageModules(ctx, apexJars, platformBootclasspathApexBootJarDepTag) // Add dependencies on all the fragments. @@ -173,6 +166,18 @@ func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.Mo allModules = append(allModules, apexModules...) b.configuredModules = allModules + var transitiveSrcFiles android.Paths + for _, module := range allModules { + depInfo, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider) + if depInfo.TransitiveSrcFiles != nil { + transitiveSrcFiles = append(transitiveSrcFiles, depInfo.TransitiveSrcFiles.ToList()...) + } + } + jarArgs := resourcePathsToJarArgs(transitiveSrcFiles) + jarArgs = append(jarArgs, "-srcjar") // Move srcfiles to the right package + srcjar := android.PathForModuleOut(ctx, ctx.ModuleName()+"-transitive.srcjar").OutputPath + TransformResourcesToJar(ctx, srcjar, jarArgs, transitiveSrcFiles) + // Gather all the fragments dependencies. b.fragments = gatherApexModulePairDepsWithTag(ctx, bootclasspathFragmentDepTag) @@ -186,8 +191,10 @@ func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.Mo bootDexJarByModule := b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments) buildRuleForBootJarsPackageCheck(ctx, bootDexJarByModule) - b.generateBootImageBuildActions(ctx) - b.copyApexBootJarsForAppsDexpreopt(ctx, apexModules) + ctx.SetOutputFiles(android.Paths{b.hiddenAPIFlagsCSV}, "hiddenapi-flags.csv") + ctx.SetOutputFiles(android.Paths{b.hiddenAPIIndexCSV}, "hiddenapi-index.csv") + ctx.SetOutputFiles(android.Paths{b.hiddenAPIMetadataCSV}, "hiddenapi-metadata.csv") + ctx.SetOutputFiles(android.Paths{srcjar}, ".srcjar") } // Generate classpaths.proto config @@ -196,6 +203,7 @@ func (b *platformBootclasspathModule) generateClasspathProtoBuildActions(ctx and // ART and platform boot jars must have a corresponding entry in DEX2OATBOOTCLASSPATH classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, BOOTCLASSPATH, DEX2OATBOOTCLASSPATH) b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars) + b.classpathFragmentBase().installClasspathProto(ctx) } func (b *platformBootclasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList { @@ -205,7 +213,7 @@ func (b *platformBootclasspathModule) configuredJars(ctx android.ModuleContext) // 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) + info, _ := android.OtherModuleProvider(ctx, fragment, ClasspathFragmentProtoContentInfoProvider) if info.ClasspathFragmentProtoGenerated { remainingJars = remainingJars.RemoveList(info.ClasspathFragmentProtoContents) } @@ -218,7 +226,8 @@ func (b *platformBootclasspathModule) configuredJars(ctx android.ModuleContext) } func (b *platformBootclasspathModule) platformJars(ctx android.PathContext) android.ConfiguredJarList { - return defaultBootImageConfig(ctx).modules.RemoveList(artBootImageConfig(ctx).modules) + global := dexpreopt.GetGlobalConfig(ctx) + return global.BootJars.RemoveList(global.ArtApexJars) } // checkPlatformModules ensures that the non-updatable modules supplied are not part of an @@ -226,7 +235,7 @@ func (b *platformBootclasspathModule) platformJars(ctx android.PathContext) andr 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) + apexInfo, _ := android.OtherModuleProvider(ctx, m, android.ApexInfoProvider) fromUpdatableApex := apexInfo.Updatable if fromUpdatableApex { // error: this jar is part of an updatable apex @@ -240,7 +249,7 @@ func (b *platformBootclasspathModule) checkPlatformModules(ctx android.ModuleCon // 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) + apexInfo, _ := android.OtherModuleProvider(ctx, m, android.ApexInfoProvider) fromUpdatableApex := apexInfo.Updatable if fromUpdatableApex { // ok: this jar is part of an updatable apex @@ -267,6 +276,15 @@ func (b *platformBootclasspathModule) checkApexModules(ctx android.ModuleContext // generateHiddenAPIBuildActions generates all the hidden API related build rules. func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) bootDexJarByModule { + createEmptyHiddenApiFiles := func() { + paths := android.OutputPaths{b.hiddenAPIFlagsCSV, b.hiddenAPIIndexCSV, b.hiddenAPIMetadataCSV} + for _, path := range paths { + ctx.Build(pctx, android.BuildParams{ + Rule: android.Touch, + Output: path, + }) + } + } // Save the paths to the monolithic files for retrieval via OutputFiles(). b.hiddenAPIFlagsCSV = hiddenAPISingletonPaths(ctx).flags @@ -275,17 +293,11 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android. bootDexJarByModule := extractBootDexJarsFromModules(ctx, modules) - // Don't run any hiddenapi rules if UNSAFE_DISABLE_HIDDENAPI_FLAGS=true. This is a performance + // Don't run any hiddenapi rules if hidden api checks are disabled. This is a performance // optimization that can be used to reduce the incremental build time but as its name suggests it // can be unsafe to use, e.g. when the changes affect anything that goes on the bootclasspath. - if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") { - paths := android.OutputPaths{b.hiddenAPIFlagsCSV, b.hiddenAPIIndexCSV, b.hiddenAPIMetadataCSV} - for _, path := range paths { - ctx.Build(pctx, android.BuildParams{ - Rule: android.Touch, - Output: path, - }) - } + if ctx.Config().DisableHiddenApiChecks() { + createEmptyHiddenApiFiles() return bootDexJarByModule } @@ -298,6 +310,13 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android. // the fragments will have already provided the flags that are needed. classesJars := monolithicInfo.ClassesJars + if len(classesJars) == 0 { + // This product does not include any monolithic jars. Monolithic hiddenapi flag generation is not required. + // However, generate an empty file so that the dist tags in f/b/boot/Android.bp can be resolved, and `m dist` works. + createEmptyHiddenApiFiles() + return bootDexJarByModule + } + // Create the input to pass to buildRuleToGenerateHiddenAPIStubFlagsFile input := newHiddenAPIFlagInput() @@ -374,7 +393,7 @@ func (b *platformBootclasspathModule) createAndProvideMonolithicHiddenAPIInfo(ct monolithicInfo := newMonolithicHiddenAPIInfo(ctx, temporaryInput.FlagFilesByCategory, classpathElements) // Store the information for testing. - ctx.SetProvider(MonolithicHiddenAPIInfoProvider, monolithicInfo) + android.SetProvider(ctx, MonolithicHiddenAPIInfoProvider, monolithicInfo) return monolithicInfo } @@ -398,79 +417,3 @@ func (b *platformBootclasspathModule) generateHiddenApiMakeVars(ctx android.Make // INTERNAL_PLATFORM_HIDDENAPI_FLAGS is used by Make rules in art/ and cts/. ctx.Strict("INTERNAL_PLATFORM_HIDDENAPI_FLAGS", b.hiddenAPIFlagsCSV.String()) } - -// generateBootImageBuildActions generates ninja rules related to the boot image creation. -func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android.ModuleContext) { - // 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) - - global := dexpreopt.GetGlobalConfig(ctx) - if !shouldBuildBootImages(ctx.Config(), global) { - return - } - - frameworkBootImageConfig := defaultBootImageConfig(ctx) - bootFrameworkProfileRule(ctx, frameworkBootImageConfig) - b.generateBootImage(ctx, frameworkBootImageName) - b.generateBootImage(ctx, mainlineBootImageName) - dumpOatRules(ctx, frameworkBootImageConfig) -} - -func (b *platformBootclasspathModule) generateBootImage(ctx android.ModuleContext, imageName string) { - imageConfig := genBootImageConfigs(ctx)[imageName] - - modules := b.getModulesForImage(ctx, imageConfig) - - // Copy module dex jars to their predefined locations. - bootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, modules) - copyBootJarsToPredefinedLocations(ctx, bootDexJarsByModule, imageConfig.dexPathsByModule) - - // Build a profile for the image config and then use that to build the boot image. - profile := bootImageProfileRule(ctx, imageConfig) - - // If dexpreopt of boot image jars should be skipped, generate only a profile. - global := dexpreopt.GetGlobalConfig(ctx) - if global.DisablePreoptBootImages { - return - } - - // Build boot image files for the android variants. - androidBootImageFiles := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile) - - // Zip the android variant boot image files up. - buildBootImageZipInPredefinedLocation(ctx, imageConfig, androidBootImageFiles.byArch) - - // Build boot image files for the host variants. There are use directly by ART host side tests. - buildBootImageVariantsForBuildOs(ctx, imageConfig, profile) -} - -// Copy apex module dex jars to their predefined locations. They will be used for dexpreopt for apps. -func (b *platformBootclasspathModule) copyApexBootJarsForAppsDexpreopt(ctx android.ModuleContext, apexModules []android.Module) { - config := GetApexBootConfig(ctx) - apexBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, apexModules) - copyBootJarsToPredefinedLocations(ctx, apexBootDexJarsByModule, config.dexPathsByModule) -} - -func (b *platformBootclasspathModule) getModulesForImage(ctx android.ModuleContext, imageConfig *bootImageConfig) []android.Module { - modules := make([]android.Module, 0, imageConfig.modules.Len()) - for i := 0; i < imageConfig.modules.Len(); i++ { - found := false - for _, module := range b.configuredModules { - name := android.RemoveOptionalPrebuiltPrefix(module.Name()) - if name == imageConfig.modules.Jar(i) { - modules = append(modules, module) - found = true - break - } - } - if !found && !ctx.Config().AllowMissingDependencies() { - ctx.ModuleErrorf( - "Boot image '%s' module '%s' not added as a dependency of platform_bootclasspath", - imageConfig.name, - imageConfig.modules.Jar(i)) - return []android.Module{} - } - } - return modules -} diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go index ff2da4bb2..0d2acaea0 100644 --- a/java/platform_bootclasspath_test.go +++ b/java/platform_bootclasspath_test.go @@ -81,6 +81,15 @@ func TestPlatformBootclasspath(t *testing.T) { RunTest(t) }) + fooSourceSrc := "source/a.java" + barSrc := "a.java" + + checkSrcJarInputs := func(t *testing.T, result *android.TestResult, name string, expected []string) { + t.Helper() + srcjar := result.ModuleForTests(name, "android_common").Output(name + "-transitive.srcjar") + android.AssertStringDoesContain(t, "srcjar arg", srcjar.Args["jarArgs"], "-srcjar") + android.AssertArrayString(t, "srcjar inputs", expected, srcjar.Implicits.Strings()) + } t.Run("source", func(t *testing.T) { result := android.GroupFixturePreparers( preparer, @@ -91,6 +100,10 @@ func TestPlatformBootclasspath(t *testing.T) { "platform:foo", "platform:bar", }) + checkSrcJarInputs(t, result, "platform-bootclasspath", []string{ + fooSourceSrc, + barSrc, + }) }) t.Run("prebuilt", func(t *testing.T) { @@ -103,6 +116,10 @@ func TestPlatformBootclasspath(t *testing.T) { "platform:prebuilt_foo", "platform:bar", }) + checkSrcJarInputs(t, result, "platform-bootclasspath", []string{ + // TODO(b/151360309): This should also have the srcs for prebuilt_foo + barSrc, + }) }) t.Run("source+prebuilt - source preferred", func(t *testing.T) { @@ -116,6 +133,10 @@ func TestPlatformBootclasspath(t *testing.T) { "platform:foo", "platform:bar", }) + checkSrcJarInputs(t, result, "platform-bootclasspath", []string{ + fooSourceSrc, + barSrc, + }) }) t.Run("source+prebuilt - prebuilt preferred", func(t *testing.T) { @@ -129,6 +150,10 @@ func TestPlatformBootclasspath(t *testing.T) { "platform:prebuilt_foo", "platform:bar", }) + checkSrcJarInputs(t, result, "platform-bootclasspath", []string{ + // TODO(b/151360309): This should also have the srcs for prebuilt_foo + barSrc, + }) }) t.Run("dex import", func(t *testing.T) { @@ -146,6 +171,10 @@ func TestPlatformBootclasspath(t *testing.T) { "platform:prebuilt_foo", "platform:bar", }) + checkSrcJarInputs(t, result, "platform-bootclasspath", []string{ + // TODO(b/151360309): This should also have the srcs for prebuilt_foo + barSrc, + }) }) } @@ -324,7 +353,7 @@ func TestPlatformBootclasspath_HiddenAPIMonolithicFiles(t *testing.T) { // All the intermediate rules use the same inputs. expectedIntermediateInputs := ` - out/soong/.intermediates/bar/android_common/javac/bar.jar + out/soong/.intermediates/bar.impl/android_common/javac/bar.jar out/soong/.intermediates/foo-hiddenapi-annotations/android_common/javac/foo-hiddenapi-annotations.jar out/soong/.intermediates/foo/android_common/javac/foo.jar ` diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go index d41729150..67ed84e1d 100644 --- a/java/platform_compat_config.go +++ b/java/platform_compat_config.go @@ -15,11 +15,12 @@ package java import ( - "fmt" "path/filepath" "android/soong/android" + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" ) func init() { @@ -36,7 +37,7 @@ var CompatConfigSdkMemberType = &compatConfigMemberType{ } func registerPlatformCompatConfigBuildComponents(ctx android.RegistrationContext) { - ctx.RegisterSingletonType("platform_compat_config_singleton", platformCompatConfigSingletonFactory) + ctx.RegisterParallelSingletonType("platform_compat_config_singleton", platformCompatConfigSingletonFactory) ctx.RegisterModuleType("platform_compat_config", PlatformCompatConfigFactory) ctx.RegisterModuleType("prebuilt_platform_compat_config", prebuiltCompatConfigFactory) ctx.RegisterModuleType("global_compat_config", globalCompatConfigFactory) @@ -59,6 +60,8 @@ type platformCompatConfig struct { installDirPath android.InstallPath configFile android.OutputPath metadataFile android.OutputPath + + installConfigFile android.InstallPath } func (p *platformCompatConfig) compatConfigMetadata() android.Path { @@ -104,21 +107,15 @@ func (p *platformCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleCon FlagWithOutput("--merged-config ", p.metadataFile) p.installDirPath = android.PathForModuleInstall(ctx, "etc", "compatconfig") + p.installConfigFile = android.PathForModuleInstall(ctx, "etc", "compatconfig", p.configFile.Base()) rule.Build(configFileName, "Extract compat/compat_config.xml and install it") - + ctx.InstallFile(p.installDirPath, p.configFile.Base(), p.configFile) } func (p *platformCompatConfig) AndroidMkEntries() []android.AndroidMkEntries { return []android.AndroidMkEntries{android.AndroidMkEntries{ Class: "ETC", OutputFile: android.OptionalPathForPath(p.configFile), - Include: "$(BUILD_PREBUILT)", - ExtraEntries: []android.AndroidMkExtraEntriesFunc{ - func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.String()) - entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.configFile.Base()) - }, - }, }} } @@ -184,6 +181,11 @@ type prebuiltCompatConfigModule struct { type prebuiltCompatConfigProperties struct { Metadata *string `android:"path"` + + // Name of the source soong module that gets shadowed by this prebuilt + // If unspecified, follows the naming convention that the source module of + // the prebuilt is Name() without "prebuilt_" prefix + Source_module_name *string } func (module *prebuiltCompatConfigModule) Prebuilt() *android.Prebuilt { @@ -198,6 +200,10 @@ func (module *prebuiltCompatConfigModule) compatConfigMetadata() android.Path { return module.metadataFile } +func (module *prebuiltCompatConfigModule) BaseModuleName() string { + return proptools.StringDefault(module.properties.Source_module_name, module.ModuleBase.Name()) +} + var _ platformCompatConfigMetadataProvider = (*prebuiltCompatConfigModule)(nil) func (module *prebuiltCompatConfigModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -223,7 +229,7 @@ func (p *platformCompatConfigSingleton) GenerateBuildActions(ctx android.Singlet var compatConfigMetadata android.Paths ctx.VisitAllModules(func(module android.Module) { - if !module.Enabled() { + if !module.Enabled(ctx) { return } if c, ok := module.(platformCompatConfigMetadataProvider); ok { @@ -255,7 +261,7 @@ func (p *platformCompatConfigSingleton) GenerateBuildActions(ctx android.Singlet func (p *platformCompatConfigSingleton) MakeVars(ctx android.MakeVarsContext) { if p.metadata != nil { - ctx.Strict("INTERNAL_PLATFORM_MERGED_COMPAT_CONFIG", p.metadata.String()) + ctx.DistForGoal("droidcore", p.metadata) } } @@ -273,32 +279,23 @@ type globalCompatConfig struct { android.ModuleBase properties globalCompatConfigProperties - - outputFilePath android.OutputPath } func (c *globalCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) { filename := String(c.properties.Filename) inputPath := platformCompatConfigPath(ctx) - c.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath + outputFilePath := android.PathForModuleOut(ctx, filename).OutputPath // This ensures that outputFilePath has the correct name for others to // use, as the source file may have a different name. ctx.Build(pctx, android.BuildParams{ Rule: android.Cp, - Output: c.outputFilePath, + Output: outputFilePath, Input: inputPath, }) -} -func (h *globalCompatConfig) OutputFiles(tag string) (android.Paths, error) { - switch tag { - case "": - return android.Paths{h.outputFilePath}, nil - default: - return nil, fmt.Errorf("unsupported module reference tag %q", tag) - } + ctx.SetOutputFiles(android.Paths{outputFilePath}, "") } // global_compat_config provides access to the merged compat config xml file generated by the build. diff --git a/java/plugin.go b/java/plugin.go index 731dfda00..9c4774a10 100644 --- a/java/plugin.go +++ b/java/plugin.go @@ -16,7 +16,6 @@ package java import ( "android/soong/android" - "android/soong/bazel" ) func init() { @@ -35,8 +34,6 @@ func PluginFactory() android.Module { InitJavaModule(module, android.HostSupported) - android.InitBazelModule(module) - return module } @@ -56,35 +53,3 @@ 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, bp2BuildInfo := p.convertLibraryAttrsBp2Build(ctx) - depLabels := bp2BuildInfo.DepLabels - - 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/prebuilt_apis.go b/java/prebuilt_apis.go index 206d99527..00613eee3 100644 --- a/java/prebuilt_apis.go +++ b/java/prebuilt_apis.go @@ -55,6 +55,11 @@ type prebuiltApisProperties struct { // If set to true, compile dex for java_import modules. Defaults to false. Imports_compile_dex *bool + + // If set to true, allow incremental platform API of the form MM.m where MM is the major release + // version corresponding to the API level/SDK_INT and m is an incremental release version + // (e.g. API changes associated with QPR). Defaults to false. + Allow_incremental_platform_api *bool } type prebuiltApis struct { @@ -69,6 +74,8 @@ func (module *prebuiltApis) GenerateAndroidBuildActions(ctx android.ModuleContex // parsePrebuiltPath parses the relevant variables out of a variety of paths, e.g. // <version>/<scope>/<module>.jar // <version>/<scope>/api/<module>.txt +// *Note when using incremental platform API, <version> may be of the form MM.m where MM is the +// API level and m is an incremental release, otherwise <version> is a single integer corresponding to the API level only. // extensions/<version>/<scope>/<module>.jar // extensions/<version>/<scope>/api/<module>.txt func parsePrebuiltPath(ctx android.LoadHookContext, p string) (module string, version string, scope string) { @@ -90,8 +97,25 @@ func parsePrebuiltPath(ctx android.LoadHookContext, p string) (module string, ve } // 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) { +func parseFinalizedPrebuiltPath(ctx android.LoadHookContext, p string, allowIncremental bool) (module string, version int, release int, scope string) { module, v, scope := parsePrebuiltPath(ctx, p) + if allowIncremental { + parts := strings.Split(v, ".") + if len(parts) != 2 { + ctx.ModuleErrorf("Found unexpected version '%v' for incremental prebuilts - expect MM.m format for incremental API with both major (MM) an minor (m) revision.", v) + return + } + sdk, sdk_err := strconv.Atoi(parts[0]) + qpr, qpr_err := strconv.Atoi(parts[1]) + if sdk_err != nil || qpr_err != nil { + ctx.ModuleErrorf("Unable to read version number for incremental prebuilt api '%v'", v) + return + } + version = sdk + release = qpr + return + } + release = 0 version, err := strconv.Atoi(v) if err != nil { ctx.ModuleErrorf("Found finalized API files in non-numeric dir '%v'", v) @@ -103,7 +127,6 @@ func parseFinalizedPrebuiltPath(ctx android.LoadHookContext, p string) (module s 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, version, path, sdkVersion string, compileDex bool) { props := struct { Name *string @@ -111,13 +134,13 @@ func createImport(mctx android.LoadHookContext, module, scope, version, path, sd Sdk_version *string Installable *bool Compile_dex *bool - }{} - 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) - props.Compile_dex = proptools.BoolPtr(compileDex) - + }{ + Name: proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, version)), + Jars: []string{path}, + Sdk_version: proptools.StringPtr(sdkVersion), + Installable: proptools.BoolPtr(false), + Compile_dex: proptools.BoolPtr(compileDex), + } mctx.CreateModule(ImportFactory, &props) } @@ -135,6 +158,34 @@ func createApiModule(mctx android.LoadHookContext, name string, path string) { mctx.CreateModule(genrule.GenRuleFactory, &genruleProps) } +func createCombinedApiFilegroupModule(mctx android.LoadHookContext, name string, srcs []string) { + filegroupProps := struct { + Name *string + Srcs []string + }{} + filegroupProps.Name = proptools.StringPtr(name) + + var transformedSrcs []string + for _, src := range srcs { + transformedSrcs = append(transformedSrcs, ":"+src) + } + filegroupProps.Srcs = transformedSrcs + mctx.CreateModule(android.FileGroupFactory, &filegroupProps) +} + +func createLatestApiModuleExtensionVersionFile(mctx android.LoadHookContext, name string, version string) { + genruleProps := struct { + Name *string + Srcs []string + Out []string + Cmd *string + }{} + genruleProps.Name = proptools.StringPtr(name) + genruleProps.Out = []string{name} + genruleProps.Cmd = proptools.StringPtr("echo " + version + " > $(out)") + mctx.CreateModule(genrule.GenRuleFactory, &genruleProps) +} + func createEmptyFile(mctx android.LoadHookContext, name string) { props := struct { Name *string @@ -216,6 +267,10 @@ func PrebuiltApiModuleName(module, scope, version string) string { return module + ".api." + scope + "." + version } +func PrebuiltApiCombinedModuleName(module, scope, version string) string { + return module + ".api.combined." + scope + "." + version +} + func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { // <apiver>/<scope>/api/<module>.txt apiLevelFiles := globApiDirs(mctx, p, "api/*.txt") @@ -224,37 +279,44 @@ func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { } // Create modules for all (<module>, <scope, <version>) triplets, + allowIncremental := proptools.BoolDefault(p.properties.Allow_incremental_platform_api, false) for _, f := range apiLevelFiles { - module, version, scope := parseFinalizedPrebuiltPath(mctx, f) - createApiModule(mctx, PrebuiltApiModuleName(module, scope, strconv.Itoa(version)), f) + module, version, release, scope := parseFinalizedPrebuiltPath(mctx, f, allowIncremental) + if allowIncremental { + incrementalVersion := strconv.Itoa(version) + "." + strconv.Itoa(release) + createApiModule(mctx, PrebuiltApiModuleName(module, scope, incrementalVersion), f) + } else { + createApiModule(mctx, PrebuiltApiModuleName(module, scope, strconv.Itoa(version)), f) + } } // Figure out the latest version of each module/scope type latestApiInfo struct { module, scope, path string - version int + version, release int + isExtensionApiFile bool } - getLatest := func(files []string) map[string]latestApiInfo { + getLatest := func(files []string, isExtensionApiFile bool) map[string]latestApiInfo { m := make(map[string]latestApiInfo) for _, f := range files { - module, version, scope := parseFinalizedPrebuiltPath(mctx, f) + module, version, release, scope := parseFinalizedPrebuiltPath(mctx, f, allowIncremental) if strings.HasSuffix(module, "incompatibilities") { continue } key := module + "." + scope info, exists := m[key] - if !exists || version > info.version { - m[key] = latestApiInfo{module, scope, f, version} + if !exists || version > info.version || (version == info.version && release > info.release) { + m[key] = latestApiInfo{module, scope, f, version, release, isExtensionApiFile} } } return m } - latest := getLatest(apiLevelFiles) + latest := getLatest(apiLevelFiles, false) if p.properties.Extensions_dir != nil { extensionApiFiles := globExtensionDirs(mctx, p, "api/*.txt") - for k, v := range getLatest(extensionApiFiles) { + for k, v := range getLatest(extensionApiFiles, true) { 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) } @@ -264,9 +326,17 @@ func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { } // Sort the keys in order to make build.ninja stable - for _, k := range android.SortedKeys(latest) { + sortedLatestKeys := android.SortedKeys(latest) + + for _, k := range sortedLatestKeys { info := latest[k] name := PrebuiltApiModuleName(info.module, info.scope, "latest") + latestExtensionVersionModuleName := PrebuiltApiModuleName(info.module, info.scope, "latest.extension_version") + if info.isExtensionApiFile { + createLatestApiModuleExtensionVersionFile(mctx, latestExtensionVersionModuleName, strconv.Itoa(info.version)) + } else { + createLatestApiModuleExtensionVersionFile(mctx, latestExtensionVersionModuleName, "-1") + } createApiModule(mctx, name, info.path) } @@ -284,11 +354,38 @@ func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) { } } // Create empty incompatibilities files for remaining modules - for _, k := range android.SortedKeys(latest) { + // If the incompatibility module has been created, create a corresponding combined module + for _, k := range sortedLatestKeys { if _, ok := incompatibilities[k]; !ok { createEmptyFile(mctx, PrebuiltApiModuleName(latest[k].module+"-incompatibilities", latest[k].scope, "latest")) } } + + // Create combined latest api and removed api files modules. + // The combined modules list all api files of the api scope and its subset api scopes. + for _, k := range sortedLatestKeys { + info := latest[k] + name := PrebuiltApiCombinedModuleName(info.module, info.scope, "latest") + + // Iterate until the currentApiScope does not extend any other api scopes + // i.e. is not a superset of any other api scopes + // the relationship between the api scopes is defined in java/sdk_library.go + var srcs []string + currentApiScope := scopeByName[info.scope] + for currentApiScope != nil { + if _, ok := latest[fmt.Sprintf("%s.%s", info.module, currentApiScope.name)]; ok { + srcs = append(srcs, PrebuiltApiModuleName(info.module, currentApiScope.name, "latest")) + } + currentApiScope = currentApiScope.extends + } + + // srcs is currently listed in the order from the widest api scope to the narrowest api scopes + // e.g. module lib -> system -> public + // In order to pass the files in metalava from the narrowest api scope to the widest api scope, + // the list has to be reversed. + android.ReverseSliceInPlace(srcs) + createCombinedApiFilegroupModule(mctx, name, srcs) + } } func createPrebuiltApiModules(mctx android.LoadHookContext) { diff --git a/java/prebuilt_apis_test.go b/java/prebuilt_apis_test.go index 2b8435325..b6fb2c6bf 100644 --- a/java/prebuilt_apis_test.go +++ b/java/prebuilt_apis_test.go @@ -99,3 +99,26 @@ func TestPrebuiltApis_WithExtensions(t *testing.T) { android.AssertStringEquals(t, "Expected latest bar = extension level 2", "prebuilts/sdk/extensions/2/public/api/bar.txt", bar_input) android.AssertStringEquals(t, "Expected latest baz = api level 32", "prebuilts/sdk/32/public/api/baz.txt", baz_input) } + +func TestPrebuiltApis_WithIncrementalApi(t *testing.T) { + runTestWithIncrementalApi := func() (foo_input, bar_input, baz_input string) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + FixtureWithPrebuiltIncrementalApis(map[string][]string{ + "33.0": {"foo"}, + "33.1": {"foo", "bar", "baz"}, + "33.2": {"foo", "bar"}, + "current": {"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() + baz_input = result.ModuleForTests("baz.api.public.latest", "").Rule("generator").Implicits[0].String() + return + } + // 33.1 is the latest for baz, 33.2 is the latest for both foo & bar + foo_input, bar_input, baz_input := runTestWithIncrementalApi() + android.AssertStringEquals(t, "Expected latest foo = api level 33.2", "prebuilts/sdk/33.2/public/api/foo.txt", foo_input) + android.AssertStringEquals(t, "Expected latest bar = api level 33.2", "prebuilts/sdk/33.2/public/api/bar.txt", bar_input) + android.AssertStringEquals(t, "Expected latest baz = api level 33.1", "prebuilts/sdk/33.1/public/api/baz.txt", baz_input) +} diff --git a/java/proto.go b/java/proto.go index c732d9842..e27ef2c41 100644 --- a/java/proto.go +++ b/java/proto.go @@ -19,9 +19,6 @@ import ( "strconv" "android/soong/android" - "android/soong/bazel" - - "github.com/google/blueprint/proptools" ) const ( @@ -141,57 +138,3 @@ func protoFlags(ctx android.ModuleContext, j *CommonProperties, p *android.Proto return flags } - -type protoAttributes struct { - Deps bazel.LabelListAttribute - Sdk_version bazel.StringAttribute - Java_version bazel.StringAttribute -} - -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"} - protoAttrs := &protoAttributes{ - Deps: bazel.MakeSingleLabelListAttribute(protoLabel), - Java_version: bazel.StringAttribute{Value: m.properties.Java_version}, - Sdk_version: bazel.StringAttribute{Value: m.deviceProperties.Sdk_version}, - } - - 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/ravenwood.go b/java/ravenwood.go new file mode 100644 index 000000000..908619d5f --- /dev/null +++ b/java/ravenwood.go @@ -0,0 +1,278 @@ +// Copyright 2023 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package java + +import ( + "android/soong/android" + "android/soong/tradefed" + + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" +) + +func init() { + RegisterRavenwoodBuildComponents(android.InitRegistrationContext) +} + +func RegisterRavenwoodBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("android_ravenwood_test", ravenwoodTestFactory) + ctx.RegisterModuleType("android_ravenwood_libgroup", ravenwoodLibgroupFactory) +} + +var ravenwoodLibContentTag = dependencyTag{name: "ravenwoodlibcontent"} +var ravenwoodUtilsTag = dependencyTag{name: "ravenwoodutils"} +var ravenwoodRuntimeTag = dependencyTag{name: "ravenwoodruntime"} + +const ravenwoodUtilsName = "ravenwood-utils" +const ravenwoodRuntimeName = "ravenwood-runtime" + +type ravenwoodLibgroupJniDepProviderInfo struct { + // All the jni_libs module names with transient dependencies. + names map[string]bool +} + +var ravenwoodLibgroupJniDepProvider = blueprint.NewProvider[ravenwoodLibgroupJniDepProviderInfo]() + +func getLibPath(archType android.ArchType) string { + if archType.Multilib == "lib64" { + return "lib64" + } + return "lib" +} + +type ravenwoodTestProperties struct { + Jni_libs []string +} + +type ravenwoodTest struct { + Library + + ravenwoodTestProperties ravenwoodTestProperties + + testProperties testProperties + testConfig android.Path + + forceOSType android.OsType + forceArchType android.ArchType +} + +func ravenwoodTestFactory() android.Module { + module := &ravenwoodTest{} + + module.addHostAndDeviceProperties() + module.AddProperties(&module.testProperties, &module.ravenwoodTestProperties) + + module.Module.dexpreopter.isTest = true + module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true) + + module.testProperties.Test_suites = []string{ + "general-tests", + "ravenwood-tests", + } + module.testProperties.Test_options.Unit_test = proptools.BoolPtr(false) + + InitJavaModule(module, android.DeviceSupported) + android.InitDefaultableModule(module) + + return module +} + +func (r *ravenwoodTest) InstallInTestcases() bool { return true } +func (r *ravenwoodTest) InstallForceOS() (*android.OsType, *android.ArchType) { + return &r.forceOSType, &r.forceArchType +} +func (r *ravenwoodTest) TestSuites() []string { + return r.testProperties.Test_suites +} + +func (r *ravenwoodTest) DepsMutator(ctx android.BottomUpMutatorContext) { + r.Library.DepsMutator(ctx) + + // Generically depend on the runtime so that it's installed together with us + ctx.AddVariationDependencies(nil, ravenwoodRuntimeTag, ravenwoodRuntimeName) + + // Directly depend on any utils so that we link against them + utils := ctx.AddVariationDependencies(nil, ravenwoodUtilsTag, ravenwoodUtilsName)[0] + if utils != nil { + for _, lib := range utils.(*ravenwoodLibgroup).ravenwoodLibgroupProperties.Libs { + ctx.AddVariationDependencies(nil, libTag, lib) + } + } + + // Add jni libs + for _, lib := range r.ravenwoodTestProperties.Jni_libs { + ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib) + } +} + +func (r *ravenwoodTest) GenerateAndroidBuildActions(ctx android.ModuleContext) { + r.forceOSType = ctx.Config().BuildOS + r.forceArchType = ctx.Config().BuildArch + + r.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{ + TestConfigProp: r.testProperties.Test_config, + TestConfigTemplateProp: r.testProperties.Test_config_template, + TestSuites: r.testProperties.Test_suites, + AutoGenConfig: r.testProperties.Auto_gen_config, + DeviceTemplate: "${RavenwoodTestConfigTemplate}", + HostTemplate: "${RavenwoodTestConfigTemplate}", + }) + + r.Library.GenerateAndroidBuildActions(ctx) + + // Start by depending on all files installed by dependencies + var installDeps android.InstallPaths + + // All JNI libraries included in the runtime + var runtimeJniModuleNames map[string]bool + + if utils := ctx.GetDirectDepsWithTag(ravenwoodUtilsTag)[0]; utils != nil { + for _, installFile := range utils.FilesToInstall() { + installDeps = append(installDeps, installFile) + } + jniDeps, ok := android.OtherModuleProvider(ctx, utils, ravenwoodLibgroupJniDepProvider) + if ok { + runtimeJniModuleNames = jniDeps.names + } + } + + if runtime := ctx.GetDirectDepsWithTag(ravenwoodRuntimeTag)[0]; runtime != nil { + for _, installFile := range runtime.FilesToInstall() { + installDeps = append(installDeps, installFile) + } + jniDeps, ok := android.OtherModuleProvider(ctx, runtime, ravenwoodLibgroupJniDepProvider) + if ok { + runtimeJniModuleNames = jniDeps.names + } + } + + // Also remember what JNI libs are in the runtime. + + // Also depend on our config + installPath := android.PathForModuleInstall(ctx, r.BaseModuleName()) + installConfig := ctx.InstallFile(installPath, ctx.ModuleName()+".config", r.testConfig) + installDeps = append(installDeps, installConfig) + + // Depend on the JNI libraries, but don't install the ones that the runtime already + // contains. + soInstallPath := installPath.Join(ctx, getLibPath(r.forceArchType)) + for _, jniLib := range collectTransitiveJniDeps(ctx) { + if _, ok := runtimeJniModuleNames[jniLib.name]; ok { + continue // Runtime already includes it. + } + installJni := ctx.InstallFile(soInstallPath, jniLib.path.Base(), jniLib.path) + installDeps = append(installDeps, installJni) + } + + // Install our JAR with all dependencies + ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.outputFile, installDeps...) +} + +func (r *ravenwoodTest) 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.AddStrings("LOCAL_COMPATIBILITY_SUITE", + "general-tests", "ravenwood-tests") + if r.testConfig != nil { + entries.SetPath("LOCAL_FULL_TEST_CONFIG", r.testConfig) + } + }) + return entriesList +} + +type ravenwoodLibgroupProperties struct { + Libs []string + + Jni_libs []string +} + +type ravenwoodLibgroup struct { + android.ModuleBase + + ravenwoodLibgroupProperties ravenwoodLibgroupProperties + + forceOSType android.OsType + forceArchType android.ArchType +} + +func ravenwoodLibgroupFactory() android.Module { + module := &ravenwoodLibgroup{} + module.AddProperties(&module.ravenwoodLibgroupProperties) + + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) + return module +} + +func (r *ravenwoodLibgroup) InstallInTestcases() bool { return true } +func (r *ravenwoodLibgroup) InstallForceOS() (*android.OsType, *android.ArchType) { + return &r.forceOSType, &r.forceArchType +} +func (r *ravenwoodLibgroup) TestSuites() []string { + return []string{ + "general-tests", + "ravenwood-tests", + } +} + +func (r *ravenwoodLibgroup) DepsMutator(ctx android.BottomUpMutatorContext) { + // Always depends on our underlying libs + for _, lib := range r.ravenwoodLibgroupProperties.Libs { + ctx.AddVariationDependencies(nil, ravenwoodLibContentTag, lib) + } + for _, lib := range r.ravenwoodLibgroupProperties.Jni_libs { + ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib) + } +} + +func (r *ravenwoodLibgroup) GenerateAndroidBuildActions(ctx android.ModuleContext) { + r.forceOSType = ctx.Config().BuildOS + r.forceArchType = ctx.Config().BuildArch + + // Collect the JNI dependencies, including the transitive deps. + jniDepNames := make(map[string]bool) + jniLibs := collectTransitiveJniDeps(ctx) + + for _, jni := range jniLibs { + jniDepNames[jni.name] = true + } + android.SetProvider(ctx, ravenwoodLibgroupJniDepProvider, ravenwoodLibgroupJniDepProviderInfo{ + names: jniDepNames, + }) + + // Install our runtime into expected location for packaging + installPath := android.PathForModuleInstall(ctx, r.BaseModuleName()) + for _, lib := range r.ravenwoodLibgroupProperties.Libs { + libModule := ctx.GetDirectDepWithTag(lib, ravenwoodLibContentTag) + libJar := android.OutputFileForModule(ctx, libModule, "") + ctx.InstallFile(installPath, lib+".jar", libJar) + } + soInstallPath := android.PathForModuleInstall(ctx, r.BaseModuleName()).Join(ctx, getLibPath(r.forceArchType)) + + for _, jniLib := range jniLibs { + ctx.InstallFile(soInstallPath, jniLib.path.Base(), jniLib.path) + } + + // Normal build should perform install steps + ctx.Phony(r.BaseModuleName(), android.PathForPhony(ctx, r.BaseModuleName()+"-install")) +} + +// collectTransitiveJniDeps returns all JNI dependencies, including transitive +// ones, including NDK / stub libs. (Because Ravenwood has no "preinstalled" libraries) +func collectTransitiveJniDeps(ctx android.ModuleContext) []jniLib { + libs, _ := collectJniDeps(ctx, true, false, nil) + return libs +} diff --git a/java/ravenwood_test.go b/java/ravenwood_test.go new file mode 100644 index 000000000..59612645c --- /dev/null +++ b/java/ravenwood_test.go @@ -0,0 +1,186 @@ +// 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 ( + "runtime" + "testing" + + "android/soong/android" +) + +var prepareRavenwoodRuntime = android.GroupFixturePreparers( + android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { + RegisterRavenwoodBuildComponents(ctx) + }), + android.FixtureAddTextFile("ravenwood/Android.bp", ` + cc_library_shared { + name: "ravenwood-runtime-jni1", + host_supported: true, + srcs: ["jni.cpp"], + } + cc_library_shared { + name: "ravenwood-runtime-jni2", + host_supported: true, + srcs: ["jni.cpp"], + stem: "libred", + shared_libs: [ + "ravenwood-runtime-jni3", + ], + } + cc_library_shared { + name: "ravenwood-runtime-jni3", + host_supported: true, + srcs: ["jni.cpp"], + } + java_library_static { + name: "framework-minus-apex.ravenwood", + srcs: ["Framework.java"], + } + java_library_static { + name: "framework-services.ravenwood", + srcs: ["Services.java"], + } + java_library_static { + name: "framework-rules.ravenwood", + srcs: ["Rules.java"], + } + android_ravenwood_libgroup { + name: "ravenwood-runtime", + libs: [ + "framework-minus-apex.ravenwood", + "framework-services.ravenwood", + ], + jni_libs: [ + "ravenwood-runtime-jni1", + "ravenwood-runtime-jni2", + ], + } + android_ravenwood_libgroup { + name: "ravenwood-utils", + libs: [ + "framework-rules.ravenwood", + ], + } + `), +) + +var installPathPrefix = "out/soong/host/linux-x86/testcases" + +func TestRavenwoodRuntime(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip("requires linux") + } + + ctx := android.GroupFixturePreparers( + PrepareForIntegrationTestWithJava, + prepareRavenwoodRuntime, + ).RunTest(t) + + // Verify that our runtime depends on underlying libs + CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-runtime", "android_common", "framework-minus-apex.ravenwood") + CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-runtime", "android_common", "framework-services.ravenwood") + CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-runtime", "android_common", "ravenwood-runtime-jni") + CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-utils", "android_common", "framework-rules.ravenwood") + + // Verify that we've emitted artifacts in expected location + runtime := ctx.ModuleForTests("ravenwood-runtime", "android_common") + runtime.Output(installPathPrefix + "/ravenwood-runtime/framework-minus-apex.ravenwood.jar") + runtime.Output(installPathPrefix + "/ravenwood-runtime/framework-services.ravenwood.jar") + runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/ravenwood-runtime-jni1.so") + runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/libred.so") + runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/ravenwood-runtime-jni3.so") + utils := ctx.ModuleForTests("ravenwood-utils", "android_common") + utils.Output(installPathPrefix + "/ravenwood-utils/framework-rules.ravenwood.jar") +} + +func TestRavenwoodTest(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip("requires linux") + } + + ctx := android.GroupFixturePreparers( + PrepareForIntegrationTestWithJava, + prepareRavenwoodRuntime, + ).RunTestWithBp(t, ` + cc_library_shared { + name: "jni-lib1", + host_supported: true, + srcs: ["jni.cpp"], + } + cc_library_shared { + name: "jni-lib2", + host_supported: true, + srcs: ["jni.cpp"], + stem: "libblue", + shared_libs: [ + "jni-lib3", + ], + } + cc_library_shared { + name: "jni-lib3", + host_supported: true, + srcs: ["jni.cpp"], + stem: "libpink", + } + android_ravenwood_test { + name: "ravenwood-test", + srcs: ["Test.java"], + jni_libs: [ + "jni-lib1", + "jni-lib2", + "ravenwood-runtime-jni2", + ], + sdk_version: "test_current", + } + `) + + // Verify that our test depends on underlying libs + CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-test", "android_common", "ravenwood-buildtime") + CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-test", "android_common", "ravenwood-utils") + CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-test", "android_common", "jni-lib") + + module := ctx.ModuleForTests("ravenwood-test", "android_common") + classpath := module.Rule("javac").Args["classpath"] + + // Verify that we're linking against test_current + android.AssertStringDoesContain(t, "classpath", classpath, "android_test_stubs_current.jar") + // Verify that we're linking against utils + android.AssertStringDoesContain(t, "classpath", classpath, "framework-rules.ravenwood.jar") + // Verify that we're *NOT* linking against runtime + android.AssertStringDoesNotContain(t, "classpath", classpath, "framework-minus-apex.ravenwood.jar") + android.AssertStringDoesNotContain(t, "classpath", classpath, "framework-services.ravenwood.jar") + + // Verify that we've emitted test artifacts in expected location + outputJar := module.Output(installPathPrefix + "/ravenwood-test/ravenwood-test.jar") + module.Output(installPathPrefix + "/ravenwood-test/ravenwood-test.config") + module.Output(installPathPrefix + "/ravenwood-test/lib64/jni-lib1.so") + module.Output(installPathPrefix + "/ravenwood-test/lib64/libblue.so") + module.Output(installPathPrefix + "/ravenwood-test/lib64/libpink.so") + + // ravenwood-runtime*.so are included in the runtime, so it shouldn't be emitted. + for _, o := range module.AllOutputs() { + android.AssertStringDoesNotContain(t, "runtime libs shouldn't be included", o, "/ravenwood-test/lib64/ravenwood-runtime") + } + + // Verify that we're going to install underlying libs + orderOnly := outputJar.OrderOnly.Strings() + android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/framework-minus-apex.ravenwood.jar") + android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/framework-services.ravenwood.jar") + android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/lib64/ravenwood-runtime-jni1.so") + android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/lib64/libred.so") + android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/lib64/ravenwood-runtime-jni3.so") + android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-utils/framework-rules.ravenwood.jar") +} diff --git a/java/resourceshrinker.go b/java/resourceshrinker.go deleted file mode 100644 index 6d5960157..000000000 --- a/java/resourceshrinker.go +++ /dev/null @@ -1,43 +0,0 @@ -// 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 ( - "android/soong/android" - - "github.com/google/blueprint" -) - -var shrinkResources = pctx.AndroidStaticRule("shrinkResources", - blueprint.RuleParams{ - Command: `${config.ResourceShrinkerCmd} --output $out --input $in --raw_resources $raw_resources`, - CommandDeps: []string{"${config.ResourceShrinkerCmd}"}, - }, "raw_resources") - -func ShrinkResources(ctx android.ModuleContext, apk android.Path, outputFile android.WritablePath) { - protoFile := android.PathForModuleOut(ctx, apk.Base()+".proto.apk") - aapt2Convert(ctx, protoFile, apk, "proto") - strictModeFile := android.PathForSource(ctx, "prebuilts/cmdline-tools/shrinker.xml") - protoOut := android.PathForModuleOut(ctx, apk.Base()+".proto.out.apk") - ctx.Build(pctx, android.BuildParams{ - Rule: shrinkResources, - Input: protoFile, - Output: protoOut, - Args: map[string]string{ - "raw_resources": strictModeFile.String(), - }, - }) - aapt2Convert(ctx, outputFile, protoOut, "binary") -} diff --git a/java/resourceshrinker_test.go b/java/resourceshrinker_test.go deleted file mode 100644 index 3bbf11670..000000000 --- a/java/resourceshrinker_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// 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 TestShrinkResourcesArgs(t *testing.T) { - result := android.GroupFixturePreparers( - PrepareForTestWithJavaDefaultModules, - ).RunTestWithBp(t, ` - android_app { - name: "app_shrink", - platform_apis: true, - optimize: { - shrink_resources: true, - } - } - - android_app { - name: "app_no_shrink", - platform_apis: true, - optimize: { - shrink_resources: false, - } - } - `) - - appShrink := result.ModuleForTests("app_shrink", "android_common") - appShrinkResources := appShrink.Rule("shrinkResources") - android.AssertStringDoesContain(t, "expected shrinker.xml in app_shrink resource shrinker flags", - appShrinkResources.Args["raw_resources"], "shrinker.xml") - - appNoShrink := result.ModuleForTests("app_no_shrink", "android_common") - if appNoShrink.MaybeRule("shrinkResources").Rule != nil { - t.Errorf("unexpected shrinkResources rule for app_no_shrink") - } -} diff --git a/java/robolectric.go b/java/robolectric.go index 008b8b1c9..4cad5b153 100644 --- a/java/robolectric.go +++ b/java/robolectric.go @@ -22,19 +22,24 @@ import ( "android/soong/android" "android/soong/java/config" + "android/soong/testing" "android/soong/tradefed" "github.com/google/blueprint/proptools" ) func init() { - android.RegisterModuleType("android_robolectric_test", RobolectricTestFactory) - android.RegisterModuleType("android_robolectric_runtimes", robolectricRuntimesFactory) + RegisterRobolectricBuildComponents(android.InitRegistrationContext) +} + +func RegisterRobolectricBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("android_robolectric_test", RobolectricTestFactory) + ctx.RegisterModuleType("android_robolectric_runtimes", robolectricRuntimesFactory) } var robolectricDefaultLibs = []string{ "mockito-robolectric-prebuilt", - "truth-prebuilt", + "truth", // TODO(ccross): this is not needed at link time "junitxml", } @@ -45,6 +50,7 @@ const robolectricPrebuiltLibPattern = "platform-robolectric-%s-prebuilt" var ( roboCoverageLibsTag = dependencyTag{name: "roboCoverageLibs"} roboRuntimesTag = dependencyTag{name: "roboRuntimes"} + roboRuntimeOnlyTag = dependencyTag{name: "roboRuntimeOnlyTag"} ) type robolectricProperties struct { @@ -66,9 +72,14 @@ type robolectricProperties struct { // instead of the one built from source in external/robolectric-shadows. Robolectric_prebuilt_version *string - // Use /external/robolectric rather than /external/robolectric-shadows as the version of robolectri + // Use /external/robolectric rather than /external/robolectric-shadows as the version of robolectric // to use. /external/robolectric closely tracks github's master, and will fully replace /external/robolectric-shadows Upstream *bool + + // Use strict mode to limit access of Robolectric API directly. See go/roboStrictMode + Strict_mode *bool + + Jni_libs []string } type robolectricTest struct { @@ -111,7 +122,7 @@ func (r *robolectricTest) DepsMutator(ctx android.BottomUpMutatorContext) { if v := String(r.robolectricProperties.Robolectric_prebuilt_version); v != "" { ctx.AddVariationDependencies(nil, libTag, fmt.Sprintf(robolectricPrebuiltLibPattern, v)) - } else { + } else if !proptools.BoolDefault(r.robolectricProperties.Strict_mode, true) { if proptools.Bool(r.robolectricProperties.Upstream) { ctx.AddVariationDependencies(nil, libTag, robolectricCurrentLib+"_upstream") } else { @@ -119,12 +130,23 @@ func (r *robolectricTest) DepsMutator(ctx android.BottomUpMutatorContext) { } } + if proptools.BoolDefault(r.robolectricProperties.Strict_mode, true) { + ctx.AddVariationDependencies(nil, roboRuntimeOnlyTag, robolectricCurrentLib+"_upstream") + } else { + // opting out from strict mode, robolectric_non_strict_mode_permission lib should be added + ctx.AddVariationDependencies(nil, libTag, "robolectric_non_strict_mode_permission") + } + ctx.AddVariationDependencies(nil, libTag, robolectricDefaultLibs...) ctx.AddVariationDependencies(nil, roboCoverageLibsTag, r.robolectricProperties.Coverage_libs...) ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), roboRuntimesTag, "robolectric-android-all-prebuilts") + + for _, lib := range r.robolectricProperties.Jni_libs { + ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib) + } } func (r *robolectricTest) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -144,29 +166,37 @@ func (r *robolectricTest) GenerateAndroidBuildActions(ctx android.ModuleContext) roboTestConfig := android.PathForModuleGen(ctx, "robolectric"). Join(ctx, "com/android/tools/test_config.properties") + var ok bool + var instrumentedApp *AndroidApp + // TODO: this inserts paths to built files into the test, it should really be inserting the contents. instrumented := ctx.GetDirectDepsWithTag(instrumentationForTag) - if len(instrumented) != 1 { + if len(instrumented) == 1 { + instrumentedApp, ok = instrumented[0].(*AndroidApp) + if !ok { + ctx.PropertyErrorf("instrumentation_for", "dependency must be an android_app") + } + } else if !ctx.Config().AllowMissingDependencies() { panic(fmt.Errorf("expected exactly 1 instrumented dependency, got %d", len(instrumented))) } - instrumentedApp, ok := instrumented[0].(*AndroidApp) - if !ok { - ctx.PropertyErrorf("instrumentation_for", "dependency must be an android_app") - } - - r.manifest = instrumentedApp.mergedManifestFile - r.resourceApk = instrumentedApp.outputFile + if instrumentedApp != nil { + r.manifest = instrumentedApp.mergedManifestFile + r.resourceApk = instrumentedApp.outputFile - generateRoboTestConfig(ctx, roboTestConfig, instrumentedApp) - r.extraResources = android.Paths{roboTestConfig} + generateRoboTestConfig(ctx, roboTestConfig, instrumentedApp) + r.extraResources = android.Paths{roboTestConfig} + } r.Library.GenerateAndroidBuildActions(ctx) roboSrcJar := android.PathForModuleGen(ctx, "robolectric", ctx.ModuleName()+".srcjar") - r.generateRoboSrcJar(ctx, roboSrcJar, instrumentedApp) - r.roboSrcJar = roboSrcJar + + if instrumentedApp != nil { + r.generateRoboSrcJar(ctx, roboSrcJar, instrumentedApp) + r.roboSrcJar = roboSrcJar + } roboTestConfigJar := android.PathForModuleOut(ctx, "robolectric_samedir", "samedir_config.jar") generateSameDirRoboTestConfigJar(ctx, roboTestConfigJar) @@ -177,22 +207,31 @@ func (r *robolectricTest) GenerateAndroidBuildActions(ctx android.ModuleContext) // once the Make test runner is removed. roboTestConfigJar, r.outputFile, - instrumentedApp.implementationAndResourcesJar, } - handleLibDeps := func(dep android.Module) { - m := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo) - r.libs = append(r.libs, ctx.OtherModuleName(dep)) + if instrumentedApp != nil { + combinedJarJars = append(combinedJarJars, instrumentedApp.implementationAndResourcesJar) + } + + handleLibDeps := func(dep android.Module, runtimeOnly bool) { + m, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider) + if !runtimeOnly { + r.libs = append(r.libs, ctx.OtherModuleName(dep)) + } if !android.InList(ctx.OtherModuleName(dep), config.FrameworkLibraries) { combinedJarJars = append(combinedJarJars, m.ImplementationAndResourcesJars...) } } for _, dep := range ctx.GetDirectDepsWithTag(libTag) { - handleLibDeps(dep) + handleLibDeps(dep, false) } for _, dep := range ctx.GetDirectDepsWithTag(sdkLibTag) { - handleLibDeps(dep) + handleLibDeps(dep, false) + } + // handle the runtimeOnly tag for strict_mode + for _, dep := range ctx.GetDirectDepsWithTag(roboRuntimeOnlyTag) { + handleLibDeps(dep, true) } r.combinedJar = android.PathForModuleOut(ctx, "robolectric_combined", r.outputFile.Base()) @@ -213,28 +252,42 @@ func (r *robolectricTest) GenerateAndroidBuildActions(ctx android.ModuleContext) r.tests = append(r.tests, s) } - r.data = append(r.data, r.manifest, r.resourceApk) - - runtimes := ctx.GetDirectDepWithTag("robolectric-android-all-prebuilts", roboRuntimesTag) - installPath := android.PathForModuleInstall(ctx, r.BaseModuleName()) + var installDeps android.InstallPaths - installedResourceApk := ctx.InstallFile(installPath, ctx.ModuleName()+".apk", r.resourceApk) - installedManifest := ctx.InstallFile(installPath, ctx.ModuleName()+"-AndroidManifest.xml", r.manifest) - installedConfig := ctx.InstallFile(installPath, ctx.ModuleName()+".config", r.testConfig) + if r.manifest != nil { + r.data = append(r.data, r.manifest) + installedManifest := ctx.InstallFile(installPath, ctx.ModuleName()+"-AndroidManifest.xml", r.manifest) + installDeps = append(installDeps, installedManifest) + } - var installDeps android.Paths + if r.resourceApk != nil { + r.data = append(r.data, r.resourceApk) + installedResourceApk := ctx.InstallFile(installPath, ctx.ModuleName()+".apk", r.resourceApk) + installDeps = append(installDeps, installedResourceApk) + } + + runtimes := ctx.GetDirectDepWithTag("robolectric-android-all-prebuilts", roboRuntimesTag) for _, runtime := range runtimes.(*robolectricRuntimes).runtimes { installDeps = append(installDeps, runtime) } - installDeps = append(installDeps, installedResourceApk, installedManifest, installedConfig) + + installedConfig := ctx.InstallFile(installPath, ctx.ModuleName()+".config", r.testConfig) + installDeps = append(installDeps, installedConfig) for _, data := range android.PathsForModuleSrc(ctx, r.testProperties.Data) { installedData := ctx.InstallFile(installPath, data.Rel(), data) installDeps = append(installDeps, installedData) } + soInstallPath := installPath.Join(ctx, getLibPath(r.forceArchType)) + for _, jniLib := range collectTransitiveJniDeps(ctx) { + installJni := ctx.InstallFile(soInstallPath, jniLib.path.Base(), jniLib.path) + installDeps = append(installDeps, installJni) + } + r.installFile = ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.combinedJar, installDeps...) + android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{}) } func generateRoboTestConfig(ctx android.ModuleContext, outputFile android.WritablePath, @@ -281,12 +334,11 @@ func generateSameDirRoboTestConfigJar(ctx android.ModuleContext, outputFile andr func (r *robolectricTest) generateRoboSrcJar(ctx android.ModuleContext, outputFile android.WritablePath, instrumentedApp *AndroidApp) { - srcJarArgs := copyOf(instrumentedApp.srcJarArgs) + srcJarArgs := android.CopyOf(instrumentedApp.srcJarArgs) srcJarDeps := append(android.Paths(nil), instrumentedApp.srcJarDeps...) for _, m := range ctx.GetDirectDepsWithTag(roboCoverageLibsTag) { - if ctx.OtherModuleHasProvider(m, JavaInfoProvider) { - dep := ctx.OtherModuleProvider(m, JavaInfoProvider).(JavaInfo) + if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok { srcJarArgs = append(srcJarArgs, dep.SrcJarArgs...) srcJarDeps = append(srcJarDeps, dep.SrcJarDeps...) } @@ -340,7 +392,9 @@ func (r *robolectricTest) writeTestRunner(w io.Writer, module, name string, test fmt.Fprintln(w, "LOCAL_MODULE :=", name) android.AndroidMkEmitAssignList(w, "LOCAL_JAVA_LIBRARIES", []string{module}, r.libs) fmt.Fprintln(w, "LOCAL_TEST_PACKAGE :=", String(r.robolectricProperties.Instrumentation_for)) - fmt.Fprintln(w, "LOCAL_INSTRUMENT_SRCJARS :=", r.roboSrcJar.String()) + if r.roboSrcJar != nil { + fmt.Fprintln(w, "LOCAL_INSTRUMENT_SRCJARS :=", r.roboSrcJar.String()) + } android.AndroidMkEmitAssignList(w, "LOCAL_ROBOTEST_FILES", tests) if t := r.robolectricProperties.Test_options.Timeout; t != nil { fmt.Fprintln(w, "LOCAL_ROBOTEST_TIMEOUT :=", *t) diff --git a/java/robolectric_test.go b/java/robolectric_test.go new file mode 100644 index 000000000..4775bac64 --- /dev/null +++ b/java/robolectric_test.go @@ -0,0 +1,98 @@ +// Copyright 2024 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 ( + "runtime" + "testing" + + "android/soong/android" +) + +var prepareRobolectricRuntime = android.GroupFixturePreparers( + android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { + RegisterRobolectricBuildComponents(ctx) + }), + android.FixtureAddTextFile("robolectric/Android.bp", ` + java_library { + name: "Robolectric_all-target_upstream", + srcs: ["Robo.java"] + } + + java_library { + name: "mockito-robolectric-prebuilt", + srcs: ["Mockito.java"] + } + + java_library { + name: "truth", + srcs: ["Truth.java"] + } + + java_library { + name: "junitxml", + srcs: ["JUnitXml.java"] + } + + java_library_host { + name: "robolectric-host-android_all", + srcs: ["Runtime.java"] + } + + android_robolectric_runtimes { + name: "robolectric-android-all-prebuilts", + jars: ["android-all/Runtime.jar"], + lib: "robolectric-host-android_all", + } + `), +) + +func TestRobolectricJniTest(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip("requires linux") + } + + ctx := android.GroupFixturePreparers( + PrepareForIntegrationTestWithJava, + prepareRobolectricRuntime, + ).RunTestWithBp(t, ` + android_app { + name: "inst-target", + srcs: ["App.java"], + platform_apis: true, + } + + cc_library_shared { + name: "jni-lib1", + host_supported: true, + srcs: ["jni.cpp"], + } + + android_robolectric_test { + name: "robo-test", + instrumentation_for: "inst-target", + srcs: ["FooTest.java"], + jni_libs: [ + "jni-lib1" + ], + } + `) + + CheckModuleHasDependency(t, ctx.TestContext, "robo-test", "android_common", "jni-lib1") + + // Check that the .so files make it into the output. + module := ctx.ModuleForTests("robo-test", "android_common") + module.Output(installPathPrefix + "/robo-test/lib64/jni-lib1.so") +} diff --git a/java/rro.go b/java/rro.go index 53faca069..0fc6e1c6f 100644 --- a/java/rro.go +++ b/java/rro.go @@ -122,6 +122,10 @@ func (r *RuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) ctx.AddVariationDependencies(nil, staticLibTag, r.properties.Static_libs...) ctx.AddVariationDependencies(nil, libTag, r.properties.Resource_libs...) + + for _, aconfig_declaration := range r.aaptProperties.Flags_packages { + ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration) + } } func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -146,7 +150,15 @@ func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleC aaptLinkFlags = append(aaptLinkFlags, "--rename-overlay-category "+*r.overridableProperties.Category) } - r.aapt.buildActions(ctx, r, nil, nil, false, aaptLinkFlags...) + aconfigTextFilePaths := getAconfigFilePaths(ctx) + r.aapt.buildActions(ctx, + aaptBuildActionOptions{ + sdkContext: r, + enforceDefaultTargetSdkVersion: false, + extraLinkFlags: aaptLinkFlags, + aconfigTextFiles: aconfigTextFilePaths, + }, + ) // Sign the built package _, _, certificates := collectAppDeps(ctx, r, false, false) @@ -165,6 +177,10 @@ func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleC partition := rroPartition(ctx) r.installDir = android.PathForModuleInPartitionInstall(ctx, partition, "overlay", String(r.properties.Theme)) ctx.InstallFile(r.installDir, r.outputFile.Base(), r.outputFile) + + android.SetProvider(ctx, FlagsPackagesProvider, FlagsPackages{ + AconfigTextFiles: aconfigTextFilePaths, + }) } func (r *RuntimeResourceOverlay) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec { diff --git a/java/rro_test.go b/java/rro_test.go index 8067a4703..742c83982 100644 --- a/java/rro_test.go +++ b/java/rro_test.go @@ -62,7 +62,6 @@ func TestRuntimeResourceOverlay(t *testing.T) { result := android.GroupFixturePreparers( PrepareForTestWithJavaDefaultModules, - PrepareForTestWithOverlayBuildComponents, android.FixtureModifyConfig(android.SetKatiEnabledForTests), fs.AddToFixture(), ).RunTestWithBp(t, bp) @@ -330,7 +329,6 @@ func TestEnforceRRO_propagatesToDependencies(t *testing.T) { t.Run(testCase.name, func(t *testing.T) { result := android.GroupFixturePreparers( PrepareForTestWithJavaDefaultModules, - PrepareForTestWithOverlayBuildComponents, fs.AddToFixture(), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.ProductResourceOverlays = productResourceOverlays @@ -407,3 +405,51 @@ func TestRuntimeResourceOverlayPartition(t *testing.T) { android.AssertPathRelativeToTopEquals(t, "Install dir is not correct for "+testCase.name, testCase.expectedPath, mod.installDir) } } + +func TestRuntimeResourceOverlayFlagsPackages(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + ).RunTestWithBp(t, ` + runtime_resource_overlay { + name: "foo", + sdk_version: "current", + flags_packages: [ + "bar", + "baz", + ], + } + aconfig_declarations { + name: "bar", + package: "com.example.package.bar", + container: "com.android.foo", + srcs: [ + "bar.aconfig", + ], + } + aconfig_declarations { + name: "baz", + package: "com.example.package.baz", + container: "com.android.foo", + srcs: [ + "baz.aconfig", + ], + } + `) + + foo := result.ModuleForTests("foo", "android_common") + + // runtime_resource_overlay module depends on aconfig_declarations listed in flags_packages + android.AssertBoolEquals(t, "foo expected to depend on bar", true, + CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar")) + + android.AssertBoolEquals(t, "foo expected to depend on baz", true, + CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "baz")) + + aapt2LinkRule := foo.Rule("android/soong/java.aapt2Link") + linkInFlags := aapt2LinkRule.Args["inFlags"] + android.AssertStringDoesContain(t, + "aapt2 link command expected to pass feature flags arguments", + linkInFlags, + "--feature-flags @out/soong/.intermediates/bar/intermediate.txt --feature-flags @out/soong/.intermediates/baz/intermediate.txt", + ) +} diff --git a/java/sdk.go b/java/sdk.go index 8b4918add..4ef4ee251 100644 --- a/java/sdk.go +++ b/java/sdk.go @@ -17,8 +17,6 @@ package java import ( "fmt" "path/filepath" - "sort" - "strconv" "android/soong/android" "android/soong/java/config" @@ -27,23 +25,33 @@ import ( ) func init() { - android.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory) - android.RegisterSingletonType("sdk", sdkSingletonFactory) + android.RegisterParallelSingletonType("sdk", sdkSingletonFactory) android.RegisterMakeVarsProvider(pctx, sdkMakeVars) } -var sdkVersionsKey = android.NewOnceKey("sdkVersionsKey") var sdkFrameworkAidlPathKey = android.NewOnceKey("sdkFrameworkAidlPathKey") var nonUpdatableFrameworkAidlPathKey = android.NewOnceKey("nonUpdatableFrameworkAidlPathKey") -var apiFingerprintPathKey = android.NewOnceKey("apiFingerprintPathKey") -func UseApiFingerprint(ctx android.BaseModuleContext) bool { - if ctx.Config().UnbundledBuild() && - !ctx.Config().AlwaysUsePrebuiltSdks() && - ctx.Config().IsEnvTrue("UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT") { - return true +func UseApiFingerprint(ctx android.BaseModuleContext) (useApiFingerprint bool, fingerprintSdkVersion string, fingerprintDeps android.OutputPath) { + if ctx.Config().UnbundledBuild() && !ctx.Config().AlwaysUsePrebuiltSdks() { + apiFingerprintTrue := ctx.Config().IsEnvTrue("UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT") + dessertShaIsSet := ctx.Config().Getenv("UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA") != "" + + // Error when both UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT and UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA are set + if apiFingerprintTrue && dessertShaIsSet { + ctx.ModuleErrorf("UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT=true cannot be set alongside with UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA") + } + + useApiFingerprint = apiFingerprintTrue || dessertShaIsSet + if apiFingerprintTrue { + fingerprintSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", android.ApiFingerprintPath(ctx).String()) + fingerprintDeps = android.ApiFingerprintPath(ctx) + } + if dessertShaIsSet { + fingerprintSdkVersion = ctx.Config().Getenv("UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA") + } } - return false + return useApiFingerprint, fingerprintSdkVersion, fingerprintDeps } func defaultJavaLanguageVersion(ctx android.EarlyModuleContext, s android.SdkSpec) javaVersion { @@ -51,14 +59,18 @@ func defaultJavaLanguageVersion(ctx android.EarlyModuleContext, s android.SdkSpe if err != nil { ctx.PropertyErrorf("sdk_version", "%s", err) } - if sdk.FinalOrFutureInt() <= 23 { - return JAVA_VERSION_7 - } else if sdk.FinalOrFutureInt() <= 29 { + if sdk.FinalOrFutureInt() <= 29 { return JAVA_VERSION_8 } else if sdk.FinalOrFutureInt() <= 31 { return JAVA_VERSION_9 } else if sdk.FinalOrFutureInt() <= 33 { return JAVA_VERSION_11 + } else if ctx.Config().TargetsJava21() { + // Temporary experimental flag to be able to try and build with + // java version 21 options. The flag, if used, just sets Java + // 21 as the default version, leaving any components that + // target an older version intact. + return JAVA_VERSION_21 } else { return JAVA_VERSION_17 } @@ -76,7 +88,8 @@ func systemModuleKind(sdkKind android.SdkKind, apiLevel android.ApiLevel) androi // 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 { + } else if systemModuleKind == android.SdkSystem || systemModuleKind == android.SdkTest || + systemModuleKind == android.SdkTestFrameworksCore { // The core system and test APIs are currently the same as the public API so they should use // its system modules. systemModuleKind = android.SdkPublic @@ -148,10 +161,10 @@ func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) 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) - systemModules := android.JavaApiLibraryName(ctx.Config(), fmt.Sprintf("core-%s-stubs-system-modules", systemModulesKind)) + systemModules := fmt.Sprintf("core-%s-stubs-system-modules", systemModulesKind) return sdkDep{ useModule: true, - bootclasspath: []string{module, android.JavaApiLibraryName(ctx.Config(), config.DefaultLambdaStubsLibrary)}, + bootclasspath: []string{module, config.DefaultLambdaStubsLibrary}, systemModules: systemModules, java9Classpath: []string{module}, frameworkResModule: "framework-res", @@ -192,64 +205,26 @@ func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) bootclasspath: corePlatformBootclasspathLibraries(ctx), noFrameworksLibs: true, } - case android.SdkPublic, android.SdkSystem, android.SdkTest: - return toModule(sdkVersion.Kind.JavaLibraryName(ctx.Config()), sdkFrameworkAidlPath(ctx)) + case android.SdkPublic, android.SdkSystem, android.SdkTest, android.SdkTestFrameworksCore: + return toModule(sdkVersion.Kind.DefaultJavaLibraryName(), sdkFrameworkAidlPath(ctx)) case android.SdkCore: return sdkDep{ useModule: true, - bootclasspath: []string{android.SdkCore.JavaLibraryName(ctx.Config()), android.JavaApiLibraryName(ctx.Config(), config.DefaultLambdaStubsLibrary)}, - systemModules: android.JavaApiLibraryName(ctx.Config(), "core-public-stubs-system-modules"), + bootclasspath: []string{android.SdkCore.DefaultJavaLibraryName(), config.DefaultLambdaStubsLibrary}, + systemModules: "core-public-stubs-system-modules", noFrameworksLibs: true, } case android.SdkModule: // TODO(146757305): provide .apk and .aidl that have more APIs for modules - return toModule(sdkVersion.Kind.JavaLibraryName(ctx.Config()), nonUpdatableFrameworkAidlPath(ctx)) + return toModule(sdkVersion.Kind.DefaultJavaLibraryName(), nonUpdatableFrameworkAidlPath(ctx)) case android.SdkSystemServer: // TODO(146757305): provide .apk and .aidl that have more APIs for modules - return toModule(sdkVersion.Kind.JavaLibraryName(ctx.Config()), sdkFrameworkAidlPath(ctx)) + return toModule(sdkVersion.Kind.DefaultJavaLibraryName(), sdkFrameworkAidlPath(ctx)) default: panic(fmt.Errorf("invalid sdk %q", sdkVersion.Raw)) } } -func sdkPreSingletonFactory() android.Singleton { - return sdkPreSingleton{} -} - -type sdkPreSingleton struct{} - -func (sdkPreSingleton) GenerateBuildActions(ctx android.SingletonContext) { - sdkJars, err := ctx.GlobWithDeps("prebuilts/sdk/*/public/android.jar", nil) - if err != nil { - ctx.Errorf("failed to glob prebuilts/sdk/*/public/android.jar: %s", err.Error()) - } - - var sdkVersions []int - for _, sdkJar := range sdkJars { - dir := filepath.Base(filepath.Dir(filepath.Dir(sdkJar))) - v, err := strconv.Atoi(dir) - if scerr, ok := err.(*strconv.NumError); ok && scerr.Err == strconv.ErrSyntax { - continue - } else if err != nil { - ctx.Errorf("invalid sdk jar %q, %s, %v", sdkJar, err.Error()) - } - sdkVersions = append(sdkVersions, v) - } - - sort.Ints(sdkVersions) - - ctx.Config().Once(sdkVersionsKey, func() interface{} { return sdkVersions }) -} - -func LatestSdkVersionInt(ctx android.EarlyModuleContext) int { - sdkVersions := ctx.Config().Get(sdkVersionsKey).([]int) - latestSdkVersion := 0 - if len(sdkVersions) > 0 { - latestSdkVersion = sdkVersions[len(sdkVersions)-1] - } - return latestSdkVersion -} - func sdkSingletonFactory() android.Singleton { return sdkSingleton{} } @@ -269,9 +244,9 @@ func (sdkSingleton) GenerateBuildActions(ctx android.SingletonContext) { // Create framework.aidl by extracting anything that implements android.os.Parcelable from the SDK stubs modules. func createSdkFrameworkAidl(ctx android.SingletonContext) { stubsModules := []string{ - android.SdkPublic.JavaLibraryName(ctx.Config()), - android.SdkTest.JavaLibraryName(ctx.Config()), - android.SdkSystem.JavaLibraryName(ctx.Config()), + android.SdkPublic.DefaultJavaLibraryName(), + android.SdkTest.DefaultJavaLibraryName(), + android.SdkSystem.DefaultJavaLibraryName(), } combinedAidl := sdkFrameworkAidlPath(ctx) @@ -286,7 +261,7 @@ func createSdkFrameworkAidl(ctx android.SingletonContext) { // Creates a version of framework.aidl for the non-updatable part of the platform. func createNonUpdatableFrameworkAidl(ctx android.SingletonContext) { - stubsModules := []string{android.SdkModule.JavaLibraryName(ctx.Config())} + stubsModules := []string{android.SdkModule.DefaultJavaLibraryName()} combinedAidl := nonUpdatableFrameworkAidlPath(ctx) tempPath := tempPathForRestat(ctx, combinedAidl) @@ -303,8 +278,7 @@ func createFrameworkAidl(stubsModules []string, path android.WritablePath, ctx a ctx.VisitAllModules(func(module android.Module) { // Collect dex jar paths for the modules listed above. - if ctx.ModuleHasProvider(module, JavaInfoProvider) { - j := ctx.ModuleProvider(module, JavaInfoProvider).(JavaInfo) + if j, ok := android.SingletonModuleProvider(ctx, module, JavaInfoProvider); ok { name := ctx.ModuleName(module) if i := android.IndexList(name, stubsModules); i != -1 { stubsJars[i] = j.HeaderJars @@ -368,7 +342,7 @@ func nonUpdatableFrameworkAidlPath(ctx android.PathContext) android.OutputPath { // Create api_fingerprint.txt func createAPIFingerprint(ctx android.SingletonContext) { - out := ApiFingerprintPath(ctx) + out := android.ApiFingerprintPath(ctx) rule := android.NewRuleBuilder(pctx, ctx) @@ -409,17 +383,11 @@ func createAPIFingerprint(ctx android.SingletonContext) { rule.Build("api_fingerprint", "generate api_fingerprint.txt") } -func ApiFingerprintPath(ctx android.PathContext) android.OutputPath { - return ctx.Config().Once(apiFingerprintPathKey, func() interface{} { - return android.PathForOutput(ctx, "api_fingerprint.txt") - }).(android.OutputPath) -} - func sdkMakeVars(ctx android.MakeVarsContext) { if ctx.Config().AlwaysUsePrebuiltSdks() { return } ctx.Strict("FRAMEWORK_AIDL", sdkFrameworkAidlPath(ctx).String()) - ctx.Strict("API_FINGERPRINT", ApiFingerprintPath(ctx).String()) + ctx.Strict("API_FINGERPRINT", android.ApiFingerprintPath(ctx).String()) } diff --git a/java/sdk_library.go b/java/sdk_library.go index 103f1ace7..1eb7ab834 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -15,6 +15,7 @@ package java import ( + "errors" "fmt" "path" "path/filepath" @@ -28,8 +29,8 @@ import ( "github.com/google/blueprint/proptools" "android/soong/android" - "android/soong/bazel" "android/soong/dexpreopt" + "android/soong/etc" ) const ( @@ -105,15 +106,18 @@ type apiScope struct { // The name of the property in the java_sdk_library_import propertyName string - // The tag to use to depend on the stubs library module. - stubsTag scopeDependencyTag + // The tag to use to depend on the prebuilt stubs library module + prebuiltStubsTag scopeDependencyTag + + // The tag to use to depend on the everything stubs library module. + everythingStubsTag scopeDependencyTag + + // The tag to use to depend on the exportable stubs library module. + exportableStubsTag scopeDependencyTag // The tag to use to depend on the stubs source module (if separate from the API module). stubsSourceTag scopeDependencyTag - // The tag to use to depend on the API file generating module (if separate from the stubs source module). - apiFileTag scopeDependencyTag - // The tag to use to depend on the stubs source and API module. stubsSourceAndApiTag scopeDependencyTag @@ -156,6 +160,9 @@ type apiScope struct { // Whether the api scope can be treated as unstable, and should skip compat checks. unstable bool + + // Represents the SDK kind of this scope. + kind android.SdkKind } // Initialize a scope, creating and adding appropriate dependency tags @@ -165,21 +172,26 @@ func initApiScope(scope *apiScope) *apiScope { allScopeNames = append(allScopeNames, name) scope.propertyName = strings.ReplaceAll(name, "-", "_") scope.fieldName = proptools.FieldNameForProperty(scope.propertyName) - scope.stubsTag = scopeDependencyTag{ + scope.prebuiltStubsTag = scopeDependencyTag{ name: name + "-stubs", apiScope: scope, depInfoExtractor: (*scopePaths).extractStubsLibraryInfoFromDependency, } + scope.everythingStubsTag = scopeDependencyTag{ + name: name + "-stubs-everything", + apiScope: scope, + depInfoExtractor: (*scopePaths).extractEverythingStubsLibraryInfoFromDependency, + } + scope.exportableStubsTag = scopeDependencyTag{ + name: name + "-stubs-exportable", + apiScope: scope, + depInfoExtractor: (*scopePaths).extractExportableStubsLibraryInfoFromDependency, + } scope.stubsSourceTag = scopeDependencyTag{ name: name + "-stubs-source", apiScope: scope, depInfoExtractor: (*scopePaths).extractStubsSourceInfoFromDep, } - scope.apiFileTag = scopeDependencyTag{ - name: name + "-api", - apiScope: scope, - depInfoExtractor: (*scopePaths).extractApiInfoFromDep, - } scope.stubsSourceAndApiTag = scopeDependencyTag{ name: name + "-stubs-source-and-api", apiScope: scope, @@ -229,10 +241,30 @@ func (scope *apiScope) stubsLibraryModuleNameSuffix() string { return ".stubs" + scope.moduleSuffix } +func (scope *apiScope) exportableStubsLibraryModuleNameSuffix() string { + return ".stubs.exportable" + scope.moduleSuffix +} + +func (scope *apiScope) apiLibraryModuleName(baseName string) string { + return scope.stubsLibraryModuleName(baseName) + ".from-text" +} + +func (scope *apiScope) sourceStubLibraryModuleName(baseName string) string { + return scope.stubsLibraryModuleName(baseName) + ".from-source" +} + +func (scope *apiScope) exportableSourceStubsLibraryModuleName(baseName string) string { + return scope.exportableStubsLibraryModuleName(baseName) + ".from-source" +} + func (scope *apiScope) stubsLibraryModuleName(baseName string) string { return baseName + scope.stubsLibraryModuleNameSuffix() } +func (scope *apiScope) exportableStubsLibraryModuleName(baseName string) string { + return baseName + scope.exportableStubsLibraryModuleNameSuffix() +} + func (scope *apiScope) stubsSourceModuleName(baseName string) string { return baseName + ".stubs.source" + scope.moduleSuffix } @@ -273,6 +305,27 @@ func (scopes apiScopes) Strings(accessor func(*apiScope) string) []string { return list } +// Method that maps the apiScopes properties to the index of each apiScopes elements. +// apiScopes property to be used as the key can be specified with the input accessor. +// Only a string property of apiScope can be used as the key of the map. +func (scopes apiScopes) MapToIndex(accessor func(*apiScope) string) map[string]int { + ret := make(map[string]int) + for i, scope := range scopes { + ret[accessor(scope)] = i + } + return ret +} + +func (scopes apiScopes) ConvertStubsLibraryExportableToEverything(name string) string { + for _, scope := range scopes { + if strings.HasSuffix(name, scope.exportableStubsLibraryModuleNameSuffix()) { + return strings.TrimSuffix(name, scope.exportableStubsLibraryModuleNameSuffix()) + + scope.stubsLibraryModuleNameSuffix() + } + } + return name +} + var ( scopeByName = make(map[string]*apiScope) allScopeNames []string @@ -289,6 +342,7 @@ var ( return &module.sdkLibraryProperties.Public }, sdkVersion: "current", + kind: android.SdkPublic, }) apiScopeSystem = initApiScope(&apiScope{ name: "system", @@ -301,6 +355,7 @@ var ( moduleSuffix: ".system", sdkVersion: "system_current", annotation: "android.annotation.SystemApi(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS)", + kind: android.SdkSystem, }) apiScopeTest = initApiScope(&apiScope{ name: "test", @@ -314,6 +369,7 @@ var ( sdkVersion: "test_current", annotation: "android.annotation.TestApi", unstable: true, + kind: android.SdkTest, }) apiScopeModuleLib = initApiScope(&apiScope{ name: "module-lib", @@ -331,6 +387,7 @@ var ( moduleSuffix: ".module_lib", sdkVersion: "module_current", annotation: "android.annotation.SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES)", + kind: android.SdkModule, }) apiScopeSystemServer = initApiScope(&apiScope{ name: "system-server", @@ -361,14 +418,32 @@ var ( // com.android.* classes are okay in this interface" "--hide", "InternalClasses", }, + kind: android.SdkSystemServer, }) - allApiScopes = apiScopes{ + AllApiScopes = apiScopes{ apiScopePublic, apiScopeSystem, apiScopeTest, apiScopeModuleLib, apiScopeSystemServer, } + apiLibraryAdditionalProperties = map[string]struct { + FullApiSurfaceStubLib string + AdditionalApiContribution string + }{ + "legacy.i18n.module.platform.api": { + FullApiSurfaceStubLib: "legacy.core.platform.api.stubs", + AdditionalApiContribution: "i18n.module.public.api.stubs.source.api.contribution", + }, + "stable.i18n.module.platform.api": { + FullApiSurfaceStubLib: "stable.core.platform.api.stubs", + AdditionalApiContribution: "i18n.module.public.api.stubs.source.api.contribution", + }, + "conscrypt.module.platform.api": { + FullApiSurfaceStubLib: "stable.core.platform.api.stubs", + AdditionalApiContribution: "conscrypt.module.public.api.stubs.source.api.contribution", + }, + } ) var ( @@ -424,6 +499,9 @@ type ApiScopeProperties struct { // or the API file. They both have to use the same sdk_version as is used for // compiling the implementation library. Sdk_version *string + + // Extra libs used when compiling stubs for this scope. + Libs []string } type sdkLibraryProperties struct { @@ -460,9 +538,6 @@ type sdkLibraryProperties struct { // of the API. Api_packages []string - // list of package names that must be hidden from the API - Hidden_api_packages []string - // the relative path to the directory containing the api specification files. // Defaults to "api". Api_dir *string @@ -569,8 +644,23 @@ type sdkLibraryProperties struct { Api_lint struct { // Enable api linting. Enabled *bool + + // If API lint is enabled, this flag controls whether a set of legitimate lint errors + // are turned off. The default is true. + Legacy_errors_allowed *bool } + // Determines if the module contributes to any api surfaces. + // This property should be set to true only if the module is listed under + // frameworks-base-api.bootclasspath in frameworks/base/api/Android.bp. + // Otherwise, this property should be set to false. + // Defaults to false. + Contribute_to_android_api *bool + + // a list of aconfig_declarations module names that the stubs generated in this module + // depend on. + Aconfig_declarations []string + // TODO: determines whether to create HTML doc or not // Html_doc *bool } @@ -596,6 +686,11 @@ type scopePaths struct { // This is not the implementation jar, it still only contains stubs. stubsDexJarPath OptionalDexJarPath + // The exportable dex jar for the stubs. + // This is not the implementation jar, it still only contains stubs. + // Includes unflagged apis and flagged apis enabled by release configurations. + exportableStubsDexJarPath OptionalDexJarPath + // The API specification file, e.g. system_current.txt. currentApiFilePath android.OptionalPath @@ -609,95 +704,144 @@ type scopePaths struct { annotationsZip android.OptionalPath // The path to the latest API file. - latestApiPath android.OptionalPath + latestApiPaths android.Paths // The path to the latest removed API file. - latestRemovedApiPath android.OptionalPath + latestRemovedApiPaths android.Paths } func (paths *scopePaths) extractStubsLibraryInfoFromDependency(ctx android.ModuleContext, dep android.Module) error { - if ctx.OtherModuleHasProvider(dep, JavaInfoProvider) { - lib := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo) + if lib, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok { paths.stubsHeaderPath = lib.HeaderJars paths.stubsImplPath = lib.ImplementationJars libDep := dep.(UsesLibraryDependency) - paths.stubsDexJarPath = libDep.DexJarBuildPath() + paths.stubsDexJarPath = libDep.DexJarBuildPath(ctx) + paths.exportableStubsDexJarPath = libDep.DexJarBuildPath(ctx) return nil } else { return fmt.Errorf("expected module that has JavaInfoProvider, e.g. java_library") } } -func (paths *scopePaths) treatDepAsApiStubsProvider(dep android.Module, action func(provider ApiStubsProvider)) error { +func (paths *scopePaths) extractEverythingStubsLibraryInfoFromDependency(ctx android.ModuleContext, dep android.Module) error { + if lib, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok { + paths.stubsHeaderPath = lib.HeaderJars + if !ctx.Config().ReleaseHiddenApiExportableStubs() { + paths.stubsImplPath = lib.ImplementationJars + } + + libDep := dep.(UsesLibraryDependency) + paths.stubsDexJarPath = libDep.DexJarBuildPath(ctx) + return nil + } else { + return fmt.Errorf("expected module that has JavaInfoProvider, e.g. java_library") + } +} + +func (paths *scopePaths) extractExportableStubsLibraryInfoFromDependency(ctx android.ModuleContext, dep android.Module) error { + if lib, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok { + if ctx.Config().ReleaseHiddenApiExportableStubs() { + paths.stubsImplPath = lib.ImplementationJars + } + + libDep := dep.(UsesLibraryDependency) + paths.exportableStubsDexJarPath = libDep.DexJarBuildPath(ctx) + return nil + } else { + return fmt.Errorf("expected module that has JavaInfoProvider, e.g. java_library") + } +} + +func (paths *scopePaths) treatDepAsApiStubsProvider(dep android.Module, action func(provider ApiStubsProvider) error) error { if apiStubsProvider, ok := dep.(ApiStubsProvider); ok { - action(apiStubsProvider) + err := action(apiStubsProvider) + if err != nil { + return err + } return nil } else { - return fmt.Errorf("expected module that implements ApiStubsProvider, e.g. droidstubs") + return fmt.Errorf("expected module that implements ExportableApiStubsSrcProvider, e.g. droidstubs") } } -func (paths *scopePaths) treatDepAsApiStubsSrcProvider(dep android.Module, action func(provider ApiStubsSrcProvider)) error { +func (paths *scopePaths) treatDepAsApiStubsSrcProvider(dep android.Module, action func(provider ApiStubsSrcProvider) error) error { if apiStubsProvider, ok := dep.(ApiStubsSrcProvider); ok { - action(apiStubsProvider) + err := action(apiStubsProvider) + if err != nil { + return err + } return nil } else { return fmt.Errorf("expected module that implements ApiStubsSrcProvider, e.g. droidstubs") } } -func (paths *scopePaths) extractApiInfoFromApiStubsProvider(provider ApiStubsProvider) { - paths.annotationsZip = android.OptionalPathForPath(provider.AnnotationsZip()) - paths.currentApiFilePath = android.OptionalPathForPath(provider.ApiFilePath()) - paths.removedApiFilePath = android.OptionalPathForPath(provider.RemovedApiFilePath()) -} +func (paths *scopePaths) extractApiInfoFromApiStubsProvider(provider ApiStubsProvider, stubsType StubsType) error { + var annotationsZip, currentApiFilePath, removedApiFilePath android.Path + annotationsZip, annotationsZipErr := provider.AnnotationsZip(stubsType) + currentApiFilePath, currentApiFilePathErr := provider.ApiFilePath(stubsType) + removedApiFilePath, removedApiFilePathErr := provider.RemovedApiFilePath(stubsType) -func (paths *scopePaths) extractApiInfoFromDep(ctx android.ModuleContext, dep android.Module) error { - return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) { - paths.extractApiInfoFromApiStubsProvider(provider) - }) + combinedError := errors.Join(annotationsZipErr, currentApiFilePathErr, removedApiFilePathErr) + + if combinedError == nil { + paths.annotationsZip = android.OptionalPathForPath(annotationsZip) + paths.currentApiFilePath = android.OptionalPathForPath(currentApiFilePath) + paths.removedApiFilePath = android.OptionalPathForPath(removedApiFilePath) + } + return combinedError } -func (paths *scopePaths) extractStubsSourceInfoFromApiStubsProviders(provider ApiStubsSrcProvider) { - paths.stubsSrcJar = android.OptionalPathForPath(provider.StubsSrcJar()) +func (paths *scopePaths) extractStubsSourceInfoFromApiStubsProviders(provider ApiStubsSrcProvider, stubsType StubsType) error { + stubsSrcJar, err := provider.StubsSrcJar(stubsType) + if err == nil { + paths.stubsSrcJar = android.OptionalPathForPath(stubsSrcJar) + } + return err } func (paths *scopePaths) extractStubsSourceInfoFromDep(ctx android.ModuleContext, dep android.Module) error { - return paths.treatDepAsApiStubsSrcProvider(dep, func(provider ApiStubsSrcProvider) { - paths.extractStubsSourceInfoFromApiStubsProviders(provider) + stubsType := Everything + if ctx.Config().ReleaseHiddenApiExportableStubs() { + stubsType = Exportable + } + return paths.treatDepAsApiStubsSrcProvider(dep, func(provider ApiStubsSrcProvider) error { + return paths.extractStubsSourceInfoFromApiStubsProviders(provider, stubsType) }) } func (paths *scopePaths) extractStubsSourceAndApiInfoFromApiStubsProvider(ctx android.ModuleContext, dep android.Module) error { - return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) { - paths.extractApiInfoFromApiStubsProvider(provider) - paths.extractStubsSourceInfoFromApiStubsProviders(provider) + stubsType := Everything + if ctx.Config().ReleaseHiddenApiExportableStubs() { + stubsType = Exportable + } + return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) error { + extractApiInfoErr := paths.extractApiInfoFromApiStubsProvider(provider, stubsType) + extractStubsSourceInfoErr := paths.extractStubsSourceInfoFromApiStubsProviders(provider, stubsType) + return errors.Join(extractApiInfoErr, extractStubsSourceInfoErr) }) } -func extractSingleOptionalOutputPath(dep android.Module) (android.OptionalPath, error) { +func extractOutputPaths(dep android.Module) (android.Paths, error) { var paths android.Paths if sourceFileProducer, ok := dep.(android.SourceFileProducer); ok { paths = sourceFileProducer.Srcs() + return paths, nil } else { - return android.OptionalPath{}, fmt.Errorf("module %q does not produce source files", dep) - } - if len(paths) != 1 { - return android.OptionalPath{}, fmt.Errorf("expected one path from %q, got %q", dep, paths) + return nil, fmt.Errorf("module %q does not produce source files", dep) } - return android.OptionalPathForPath(paths[0]), nil } func (paths *scopePaths) extractLatestApiPath(ctx android.ModuleContext, dep android.Module) error { - outputPath, err := extractSingleOptionalOutputPath(dep) - paths.latestApiPath = outputPath + outputPaths, err := extractOutputPaths(dep) + paths.latestApiPaths = outputPaths return err } func (paths *scopePaths) extractLatestRemovedApiPath(ctx android.ModuleContext, dep android.Module) error { - outputPath, err := extractSingleOptionalOutputPath(dep) - paths.latestRemovedApiPath = outputPath + outputPaths, err := extractOutputPaths(dep) + paths.latestRemovedApiPaths = outputPaths return err } @@ -755,7 +899,30 @@ type commonToSdkLibraryAndImportProperties struct { type commonSdkLibraryAndImportModule interface { android.Module - BaseModuleName() string + // Returns the name of the root java_sdk_library that creates the child stub libraries + // This is the `name` as it appears in Android.bp, and not the name in Soong's build graph + // (with the prebuilt_ prefix) + // + // e.g. in the following java_sdk_library_import + // java_sdk_library_import { + // name: "framework-foo.v1", + // source_module_name: "framework-foo", + // } + // the values returned by + // 1. Name(): prebuilt_framework-foo.v1 # unique + // 2. BaseModuleName(): framework-foo # the source + // 3. RootLibraryName: framework-foo.v1 # the undecordated `name` from Android.bp + RootLibraryName() string +} + +func (m *SdkLibrary) RootLibraryName() string { + return m.BaseModuleName() +} + +func (m *SdkLibraryImport) RootLibraryName() string { + // m.BaseModuleName refers to the source of the import + // use moduleBase.Name to get the name of the module as it appears in the .bp file + return m.ModuleBase.Name() } // Common code between sdk library and sdk library import @@ -773,6 +940,14 @@ type commonToSdkLibraryAndImport struct { // Functionality related to this being used as a component of a java_sdk_library. EmbeddableSdkLibraryComponent + + // Path to the header jars of the implementation library + // This is non-empty only when api_only is false. + implLibraryHeaderJars android.Paths + + // The reference to the implementation library created by the source module. + // Is nil if the source module does not exist. + implLibraryModule *Library } func (c *commonToSdkLibraryAndImport) initCommon(module commonSdkLibraryAndImportModule) { @@ -794,7 +969,7 @@ func (c *commonToSdkLibraryAndImport) initCommonAfterDefaultsApplied(ctx android return false } - namePtr := proptools.StringPtr(c.module.BaseModuleName()) + namePtr := proptools.StringPtr(c.module.RootLibraryName()) c.sdkLibraryComponentProperties.SdkLibraryName = namePtr // Only track this sdk library if this can be used as a shared library. @@ -819,29 +994,60 @@ func (c *commonToSdkLibraryAndImport) generateCommonBuildActions(ctx android.Mod c.doctagPaths = android.PathsForModuleSrc(ctx, c.commonSdkLibraryProperties.Doctag_files) } +func (c *commonToSdkLibraryAndImport) getImplLibraryModule() *Library { + return c.implLibraryModule +} + // Module name of the runtime implementation library func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string { - return c.module.BaseModuleName() + ".impl" + return c.module.RootLibraryName() + ".impl" } // Module name of the XML file for the lib func (c *commonToSdkLibraryAndImport) xmlPermissionsModuleName() string { - return c.module.BaseModuleName() + sdkXmlFileSuffix + return c.module.RootLibraryName() + sdkXmlFileSuffix } // Name of the java_library module that compiles the stubs source. func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string { - baseName := c.module.BaseModuleName() + baseName := c.module.RootLibraryName() return c.namingScheme.stubsLibraryModuleName(apiScope, baseName) } +// Name of the java_library module that compiles the exportable stubs source. +func (c *commonToSdkLibraryAndImport) exportableStubsLibraryModuleName(apiScope *apiScope) string { + baseName := c.module.RootLibraryName() + return c.namingScheme.exportableStubsLibraryModuleName(apiScope, baseName) +} + // Name of the droidstubs module that generates the stubs source and may also // generate/check the API. func (c *commonToSdkLibraryAndImport) stubsSourceModuleName(apiScope *apiScope) string { - baseName := c.module.BaseModuleName() + baseName := c.module.RootLibraryName() return c.namingScheme.stubsSourceModuleName(apiScope, baseName) } +// Name of the java_api_library module that generates the from-text stubs source +// and compiles to a jar file. +func (c *commonToSdkLibraryAndImport) apiLibraryModuleName(apiScope *apiScope) string { + baseName := c.module.RootLibraryName() + return c.namingScheme.apiLibraryModuleName(apiScope, baseName) +} + +// Name of the java_library module that compiles the stubs +// generated from source Java files. +func (c *commonToSdkLibraryAndImport) sourceStubsLibraryModuleName(apiScope *apiScope) string { + baseName := c.module.RootLibraryName() + return c.namingScheme.sourceStubsLibraryModuleName(apiScope, baseName) +} + +// Name of the java_library module that compiles the exportable stubs +// generated from source Java files. +func (c *commonToSdkLibraryAndImport) exportableSourceStubsLibraryModuleName(apiScope *apiScope) string { + baseName := c.module.RootLibraryName() + return c.namingScheme.exportableSourceStubsLibraryModuleName(apiScope, baseName) +} + // The component names for different outputs of the java_sdk_library. // // They are similar to the names used for the child modules it creates @@ -877,57 +1083,26 @@ var tagSplitter = func() *regexp.Regexp { return regexp.MustCompile(fmt.Sprintf(`^\.(%s)\.(%s)$`, scopesRegexp, componentsRegexp)) }() -// For OutputFileProducer interface -// -// .<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] - component := groups[2] - - if scope, ok := scopeByName[scopeName]; ok { - paths := c.findScopePaths(scope) - if paths == nil { - return nil, fmt.Errorf("%q does not provide api scope %s", c.module.BaseModuleName(), scopeName) - } - - switch component { - case stubsSourceComponentName: - if paths.stubsSrcJar.Valid() { - return android.Paths{paths.stubsSrcJar.Path()}, nil - } - - case apiTxtComponentName: - if paths.currentApiFilePath.Valid() { - return android.Paths{paths.currentApiFilePath.Path()}, nil - } - - case removedApiTxtComponentName: - 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) - } else { - return nil, fmt.Errorf("unknown scope %s in %s", scope, tag) +func (module *commonToSdkLibraryAndImport) setOutputFiles(ctx android.ModuleContext) { + if module.doctagPaths != nil { + ctx.SetOutputFiles(module.doctagPaths, ".doctags") + } + for _, scopeName := range android.SortedKeys(scopeByName) { + paths := module.findScopePaths(scopeByName[scopeName]) + if paths == nil { + continue } - - } else { - switch tag { - case ".doctags": - if c.doctagPaths != nil { - return c.doctagPaths, nil - } else { - return nil, fmt.Errorf("no doctag_files specified on %s", c.module.BaseModuleName()) + componentToOutput := map[string]android.OptionalPath{ + stubsSourceComponentName: paths.stubsSrcJar, + apiTxtComponentName: paths.currentApiFilePath, + removedApiTxtComponentName: paths.removedApiFilePath, + annotationsComponentName: paths.annotationsZip, + } + for _, component := range android.SortedKeys(componentToOutput) { + if componentToOutput[component].Valid() { + ctx.SetOutputFiles(android.Paths{componentToOutput[component].Path()}, "."+scopeName+"."+component) } } - return nil, nil } } @@ -970,7 +1145,7 @@ func (c *commonToSdkLibraryAndImport) selectHeaderJarsForSdkVersion(ctx android. // If a specific numeric version has been requested then use prebuilt versions of the sdk. if !sdkVersion.ApiLevel.IsPreview() { - return PrebuiltJars(ctx, c.module.BaseModuleName(), sdkVersion) + return PrebuiltJars(ctx, c.module.RootLibraryName(), sdkVersion) } paths := c.selectScopePaths(ctx, sdkVersion.Kind) @@ -992,12 +1167,12 @@ func (c *commonToSdkLibraryAndImport) selectScopePaths(ctx android.BaseModuleCon paths := c.findClosestScopePath(apiScope) if paths == nil { var scopes []string - for _, s := range allApiScopes { + for _, s := range AllApiScopes { if c.findScopePaths(s) != nil { scopes = append(scopes, s.name) } } - ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.module.BaseModuleName(), scopes) + ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.module.RootLibraryName(), scopes) return nil } @@ -1033,6 +1208,16 @@ func (c *commonToSdkLibraryAndImport) SdkApiStubDexJar(ctx android.BaseModuleCon } // to satisfy SdkLibraryDependency interface +func (c *commonToSdkLibraryAndImport) SdkApiExportableStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath { + paths := c.selectScopePaths(ctx, kind) + if paths == nil { + return makeUnsetDexJarPath() + } + + return paths.exportableStubsDexJarPath +} + +// to satisfy SdkLibraryDependency interface func (c *commonToSdkLibraryAndImport) SdkRemovedTxtFile(ctx android.BaseModuleContext, kind android.SdkKind) android.OptionalPath { apiScope := sdkKindToApiScope(kind) paths := c.findScopePaths(apiScope) @@ -1049,7 +1234,7 @@ func (c *commonToSdkLibraryAndImport) sdkComponentPropertiesForChildLibrary() in SdkLibraryToImplicitlyTrack *string }{} - namePtr := proptools.StringPtr(c.module.BaseModuleName()) + namePtr := proptools.StringPtr(c.module.RootLibraryName()) componentProps.SdkLibraryName = namePtr if c.sharedLibrary() { @@ -1143,35 +1328,36 @@ type SdkLibraryDependency interface { // class changes but it does not contain and implementation or JavaDoc. SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths - // Get the implementation jars appropriate for the supplied sdk version. - // - // These are either the implementation jar for the whole sdk library or the implementation - // jars for the stubs. The latter should only be needed when generating JavaDoc as otherwise - // they are identical to the corresponding header jars. - SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths - - // SdkApiStubDexJar returns the dex jar for the stubs. It is needed by the hiddenapi processing - // tool which processes dex files. + // SdkApiStubDexJar returns the dex jar for the stubs for the prebuilt + // java_sdk_library_import module. It is needed by the hiddenapi processing tool which + // processes dex files. SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) OptionalDexJarPath + // SdkApiExportableStubDexJar returns the exportable dex jar for the stubs for + // java_sdk_library module. It is needed by the hiddenapi processing tool which processes + // dex files. + SdkApiExportableStubDexJar(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 // sharedLibrary returns true if this can be used as a shared library. sharedLibrary() bool + + getImplLibraryModule() *Library } type SdkLibrary struct { Library - android.BazelModuleBase - sdkLibraryProperties sdkLibraryProperties // Map from api scope to the scope specific property structure. scopeToProperties map[*apiScope]*ApiScopeProperties commonToSdkLibraryAndImport + + builtInstalledForApex []dexpreopterInstall } var _ SdkLibraryDependency = (*SdkLibrary)(nil) @@ -1180,11 +1366,25 @@ func (module *SdkLibrary) generateTestAndSystemScopesByDefault() bool { return module.sdkLibraryProperties.Generate_system_and_test_apis } +func (module *SdkLibrary) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath { + if module.implLibraryModule != nil { + return module.implLibraryModule.DexJarBuildPath(ctx) + } + return makeUnsetDexJarPath() +} + +func (module *SdkLibrary) DexJarInstallPath() android.Path { + if module.implLibraryModule != nil { + return module.implLibraryModule.DexJarInstallPath() + } + return nil +} + func (module *SdkLibrary) getGeneratedApiScopes(ctx android.EarlyModuleContext) apiScopes { // Check to see if any scopes have been explicitly enabled. If any have then all // must be. anyScopesExplicitlyEnabled := false - for _, scope := range allApiScopes { + for _, scope := range AllApiScopes { scopeProperties := module.scopeToProperties[scope] if scopeProperties.Enabled != nil { anyScopesExplicitlyEnabled = true @@ -1194,7 +1394,7 @@ func (module *SdkLibrary) getGeneratedApiScopes(ctx android.EarlyModuleContext) var generatedScopes apiScopes enabledScopes := make(map[*apiScope]struct{}) - for _, scope := range allApiScopes { + for _, scope := range AllApiScopes { scopeProperties := module.scopeToProperties[scope] // If any scopes are explicitly enabled then ignore the legacy enabled status. // This is to ensure that any new usages of this module type do not rely on legacy @@ -1214,7 +1414,7 @@ func (module *SdkLibrary) getGeneratedApiScopes(ctx android.EarlyModuleContext) // Now check to make sure that any scope that is extended by an enabled scope is also // enabled. - for _, scope := range allApiScopes { + for _, scope := range AllApiScopes { if _, ok := enabledScopes[scope]; ok { extends := scope.extends if extends != nil { @@ -1231,6 +1431,10 @@ func (module *SdkLibrary) getGeneratedApiScopes(ctx android.EarlyModuleContext) var _ android.ModuleWithMinSdkVersionCheck = (*SdkLibrary)(nil) func (module *SdkLibrary) CheckMinSdkVersion(ctx android.ModuleContext) { + CheckMinSdkVersion(ctx, &module.Library) +} + +func CheckMinSdkVersion(ctx android.ModuleContext, module *Library) { android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx), func(c android.ModuleContext, do android.PayloadDepsCallback) { ctx.WalkDeps(func(child android.Module, parent android.Module) bool { isExternal := !module.depIsInSameApex(ctx, child) @@ -1263,14 +1467,27 @@ func IsXmlPermissionsFileDepTag(depTag blueprint.DependencyTag) bool { var implLibraryTag = sdkLibraryComponentTag{name: "impl-library"} +var _ android.InstallNeededDependencyTag = sdkLibraryComponentTag{} + +// To satisfy the CopyDirectlyInAnyApexTag interface. Implementation library of the sdk library +// in an apex is considered to be directly in the apex, as if it was listed in java_libs. +func (t sdkLibraryComponentTag) CopyDirectlyInAnyApex() {} + +var _ android.CopyDirectlyInAnyApexTag = implLibraryTag + +func (t sdkLibraryComponentTag) InstallDepNeeded() bool { + return t.name == "xml-permissions-file" || t.name == "impl-library" +} + // Add the dependencies on the child modules in the component deps mutator. func (module *SdkLibrary) ComponentDepsMutator(ctx android.BottomUpMutatorContext) { for _, apiScope := range module.getGeneratedApiScopes(ctx) { // Add dependencies to the stubs library stubModuleName := module.stubsLibraryModuleName(apiScope) - // Use JavaApiLibraryName function to be redirected to stubs generated from .txt if applicable - stubModuleName = android.JavaApiLibraryName(ctx.Config(), stubModuleName) - ctx.AddVariationDependencies(nil, apiScope.stubsTag, stubModuleName) + ctx.AddVariationDependencies(nil, apiScope.everythingStubsTag, stubModuleName) + + exportableStubModuleName := module.exportableStubsLibraryModuleName(apiScope) + ctx.AddVariationDependencies(nil, apiScope.exportableStubsTag, exportableStubModuleName) // Add a dependency on the stubs source in order to access both stubs source and api information. ctx.AddVariationDependencies(nil, apiScope.stubsSourceAndApiTag, module.stubsSourceModuleName(apiScope)) @@ -1326,37 +1543,21 @@ func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { m += "Please see the documentation of the prebuilt_apis module type (and a usage example in prebuilts/sdk) for a convenient way to generate these." ctx.ModuleErrorf(m) } - if module.requiresRuntimeImplementationLibrary() { - // Only add the deps for the library if it is actually going to be built. - module.Library.deps(ctx) - } -} - -func (module *SdkLibrary) OutputFiles(tag string) (android.Paths, error) { - paths, err := module.commonOutputFiles(tag) - 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) + if disableSourceApexVariant(ctx) { + // Prebuilts are active, do not create the installation rules for the source javalib. + // Even though the source javalib is not used, we need to hide it to prevent duplicate installation rules. + // TODO (b/331665856): Implement a principled solution for this. + module.HideFromMake() } module.generateCommonBuildActions(ctx) - // Only build an implementation library if required. - if module.requiresRuntimeImplementationLibrary() { - module.Library.GenerateAndroidBuildActions(ctx) - } + module.stem = proptools.StringDefault(module.overridableProperties.Stem, ctx.ModuleName()) + + module.provideHiddenAPIPropertyInfo(ctx) // Collate the components exported by this module. All scope specific modules are exported but // the impl and xml component modules are not. @@ -1379,11 +1580,56 @@ func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) exportedComponents[ctx.OtherModuleName(to)] = struct{}{} } + + if tag == implLibraryTag { + if dep, ok := android.OtherModuleProvider(ctx, to, JavaInfoProvider); ok { + module.implLibraryHeaderJars = append(module.implLibraryHeaderJars, dep.HeaderJars...) + module.implLibraryModule = to.(*Library) + android.SetProvider(ctx, JavaInfoProvider, dep) + } + } }) + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) + if !apexInfo.IsForPlatform() { + module.hideApexVariantFromMake = true + } + + if module.implLibraryModule != nil { + if ctx.Device() { + module.classesJarPaths = android.Paths{module.implLibraryModule.implementationJarFile} + module.bootDexJarPath = module.implLibraryModule.bootDexJarPath + module.uncompressDexState = module.implLibraryModule.uncompressDexState + module.active = module.implLibraryModule.active + } + + module.outputFile = module.implLibraryModule.outputFile + module.dexJarFile = makeDexJarPathFromPath(module.implLibraryModule.dexJarFile.Path()) + module.headerJarFile = module.implLibraryModule.headerJarFile + module.implementationAndResourcesJar = module.implLibraryModule.implementationAndResourcesJar + module.builtInstalledForApex = module.implLibraryModule.builtInstalledForApex + module.dexpreopter.configPath = module.implLibraryModule.dexpreopter.configPath + module.dexpreopter.outputProfilePathOnHost = module.implLibraryModule.dexpreopter.outputProfilePathOnHost + + // Properties required for Library.AndroidMkEntries + module.logtagsSrcs = module.implLibraryModule.logtagsSrcs + module.dexpreopter.builtInstalled = module.implLibraryModule.dexpreopter.builtInstalled + module.jacocoReportClassesFile = module.implLibraryModule.jacocoReportClassesFile + module.dexer.proguardDictionary = module.implLibraryModule.dexer.proguardDictionary + module.dexer.proguardUsageZip = module.implLibraryModule.dexer.proguardUsageZip + module.linter.reports = module.implLibraryModule.linter.reports + module.linter.outputs.depSets = module.implLibraryModule.LintDepSets() + + if !module.Host() { + module.hostdexInstallFile = module.implLibraryModule.hostdexInstallFile + } + + android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: module.implLibraryModule.uniqueSrcFiles.Strings()}) + } + // Make the set of components exported by this module available for use elsewhere. exportedComponentInfo := android.ExportedComponentsInfo{Components: android.SortedKeys(exportedComponents)} - ctx.SetProvider(android.ExportedComponentsInfoProvider, exportedComponentInfo) + android.SetProvider(ctx, android.ExportedComponentsInfoProvider, exportedComponentInfo) // Provide additional information for inclusion in an sdk's generated .info file. additionalSdkInfo := map[string]interface{}{} @@ -1396,14 +1642,26 @@ func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) scopes[scope.name] = scopeInfo scopeInfo["current_api"] = scope.snapshotRelativeCurrentApiTxtPath(baseModuleName) scopeInfo["removed_api"] = scope.snapshotRelativeRemovedApiTxtPath(baseModuleName) - if p := scopePaths.latestApiPath; p.Valid() { - scopeInfo["latest_api"] = p.Path().String() + if p := scopePaths.latestApiPaths; len(p) > 0 { + // The last path in the list is the one that applies to this scope, the + // preceding ones, if any, are for the scope(s) that it extends. + scopeInfo["latest_api"] = p[len(p)-1].String() } - if p := scopePaths.latestRemovedApiPath; p.Valid() { - scopeInfo["latest_removed_api"] = p.Path().String() + if p := scopePaths.latestRemovedApiPaths; len(p) > 0 { + // The last path in the list is the one that applies to this scope, the + // preceding ones, if any, are for the scope(s) that it extends. + scopeInfo["latest_removed_api"] = p[len(p)-1].String() } } - ctx.SetProvider(android.AdditionalSdkInfoProvider, android.AdditionalSdkInfo{additionalSdkInfo}) + android.SetProvider(ctx, android.AdditionalSdkInfoProvider, android.AdditionalSdkInfo{additionalSdkInfo}) + module.setOutputFiles(ctx) + if module.requiresRuntimeImplementationLibrary() && module.implLibraryModule != nil { + setOutputFiles(ctx, module.implLibraryModule.Module) + } +} + +func (module *SdkLibrary) BuiltInstalledForApex() []dexpreopterInstall { + return module.builtInstalledForApex } func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries { @@ -1411,8 +1669,9 @@ func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries { return nil } entriesList := module.Library.AndroidMkEntries() + entries := &entriesList[0] + entries.Required = append(entries.Required, module.implLibraryModuleName()) if module.sharedLibrary() { - entries := &entriesList[0] entries.Required = append(entries.Required, module.xmlPermissionsModuleName()) } return entriesList @@ -1453,12 +1712,16 @@ func latestPrebuiltApiModuleName(name string, apiScope *apiScope) string { return PrebuiltApiModuleName(name, apiScope.name, "latest") } +func latestPrebuiltApiCombinedModuleName(name string, apiScope *apiScope) string { + return PrebuiltApiCombinedModuleName(name, apiScope.name, "latest") +} + func (module *SdkLibrary) latestApiFilegroupName(apiScope *apiScope) string { return ":" + module.latestApiModuleName(apiScope) } func (module *SdkLibrary) latestApiModuleName(apiScope *apiScope) string { - return latestPrebuiltApiModuleName(module.distStem(), apiScope) + return latestPrebuiltApiCombinedModuleName(module.distStem(), apiScope) } func (module *SdkLibrary) latestRemovedApiFilegroupName(apiScope *apiScope) string { @@ -1466,7 +1729,7 @@ func (module *SdkLibrary) latestRemovedApiFilegroupName(apiScope *apiScope) stri } func (module *SdkLibrary) latestRemovedApiModuleName(apiScope *apiScope) string { - return latestPrebuiltApiModuleName(module.distStem()+"-removed", apiScope) + return latestPrebuiltApiCombinedModuleName(module.distStem()+"-removed", apiScope) } func (module *SdkLibrary) latestIncompatibilitiesFilegroupName(apiScope *apiScope) string { @@ -1477,6 +1740,34 @@ func (module *SdkLibrary) latestIncompatibilitiesModuleName(apiScope *apiScope) return latestPrebuiltApiModuleName(module.distStem()+"-incompatibilities", apiScope) } +func (module *SdkLibrary) contributesToApiSurface(c android.Config) bool { + _, exists := c.GetApiLibraries()[module.Name()] + return exists +} + +// The listed modules are the special java_sdk_libraries where apiScope.kind do not match the +// api surface that the module contribute to. For example, the public droidstubs and java_library +// do not contribute to the public api surface, but contributes to the core platform api surface. +// This method returns the full api surface stub lib that +// the generated java_api_library should depend on. +func (module *SdkLibrary) alternativeFullApiSurfaceStubLib() string { + if val, ok := apiLibraryAdditionalProperties[module.Name()]; ok { + return val.FullApiSurfaceStubLib + } + return "" +} + +// The listed modules' stubs contents do not match the corresponding txt files, +// but require additional api contributions to generate the full stubs. +// This method returns the name of the additional api contribution module +// for corresponding sdk_library modules. +func (module *SdkLibrary) apiLibraryAdditionalApiContribution() string { + if val, ok := apiLibraryAdditionalProperties[module.Name()]; ok { + return val.AdditionalApiContribution + } + return "" +} + func childModuleVisibility(childVisibility []string) []string { if childVisibility == nil { // No child visibility set. The child will use the visibility of the sdk_library. @@ -1497,24 +1788,22 @@ func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) props := struct { Name *string Visibility []string - Instrument bool Libs []string Static_libs []string Apex_available []string + Stem *string }{ Name: proptools.StringPtr(module.implLibraryModuleName()), Visibility: visibility, - // Set the instrument property to ensure it is instrumented when instrumentation is required. - Instrument: true, - // 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, + + Libs: append(module.properties.Libs, module.sdkLibraryProperties.Impl_only_libs...), + + Static_libs: append(module.properties.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(), + + Stem: proptools.StringPtr(module.Name()), } properties := []interface{}{ @@ -1524,48 +1813,49 @@ func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) &module.dexProperties, &module.dexpreoptProperties, &module.linter.properties, + &module.overridableProperties, &props, module.sdkComponentPropertiesForChildLibrary(), } mctx.CreateModule(LibraryFactory, properties...) } -// Creates a static java library that has API stubs -func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) { - props := struct { - Name *string - Visibility []string - Srcs []string - Installable *bool - Sdk_version *string - System_modules *string - Patch_module *string - Libs []string - Static_libs []string - Compile_dex *bool - Java_version *string - Openjdk9 struct { - Srcs []string - Javacflags []string - } - Dist struct { - Targets []string - Dest *string - Dir *string - Tag *string - } - }{} - - props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope)) - props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_library_visibility) +type libraryProperties struct { + Name *string + Visibility []string + Srcs []string + Installable *bool + Sdk_version *string + System_modules *string + Patch_module *string + Libs []string + Static_libs []string + Compile_dex *bool + Java_version *string + Openjdk9 struct { + Srcs []string + Javacflags []string + } + Dist struct { + Targets []string + Dest *string + Dir *string + Tag *string + } + Is_stubs_module *bool +} + +func (module *SdkLibrary) stubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope) libraryProperties { + props := libraryProperties{} + props.Visibility = []string{"//visibility:override", "//visibility:private"} // sources are generated from the droiddoc - props.Srcs = []string{":" + module.stubsSourceModuleName(apiScope)} sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope) props.Sdk_version = proptools.StringPtr(sdkVersion) props.System_modules = module.deviceProperties.System_modules props.Patch_module = module.properties.Patch_module props.Installable = proptools.BoolPtr(false) props.Libs = module.sdkLibraryProperties.Stub_only_libs + props.Libs = append(props.Libs, module.scopeToProperties[apiScope].Libs...) props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs // The stub-annotations library contains special versions of the annotations // with CLASS retention policy, so that they're kept. @@ -1577,21 +1867,26 @@ func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext // We compile the stubs for 1.8 in line with the main android.jar stubs, and potential // interop with older developer tools that don't support 1.9. props.Java_version = proptools.StringPtr("1.8") + props.Is_stubs_module = proptools.BoolPtr(true) - // The imports need to be compiled to dex if the java_sdk_library requests it. - compileDex := module.dexProperties.Compile_dex - if module.stubLibrariesCompiledForDex() { - compileDex = proptools.BoolPtr(true) - } - props.Compile_dex = compileDex + return props +} - // Dist the class jar artifact for sdk builds. - if !Bool(module.sdkLibraryProperties.No_dist) { - props.Dist.Targets = []string{"sdk", "win_sdk"} - props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.distStem())) - props.Dist.Dir = proptools.StringPtr(module.apiDistPath(apiScope)) - props.Dist.Tag = proptools.StringPtr(".jar") - } +// Creates a static java library that has API stubs +func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) { + + props := module.stubsLibraryProps(mctx, apiScope) + props.Name = proptools.StringPtr(module.sourceStubsLibraryModuleName(apiScope)) + props.Srcs = []string{":" + module.stubsSourceModuleName(apiScope)} + + mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) +} + +// Create a static java library that compiles the "exportable" stubs +func (module *SdkLibrary) createExportableStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) { + props := module.stubsLibraryProps(mctx, apiScope) + props.Name = proptools.StringPtr(module.exportableSourceStubsLibraryModuleName(apiScope)) + props.Srcs = []string{":" + module.stubsSourceModuleName(apiScope) + "{.exportable}"} mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) } @@ -1617,6 +1912,7 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC Merge_inclusion_annotations_dirs []string Generate_stubs *bool Previous_api *string + Aconfig_declarations []string Check_api struct { Current ApiToCheck Last_released ApiToCheck @@ -1653,6 +1949,7 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC props.Libs = module.properties.Libs props.Libs = append(props.Libs, module.properties.Static_libs...) props.Libs = append(props.Libs, module.sdkLibraryProperties.Stub_only_libs...) + props.Libs = append(props.Libs, module.scopeToProperties[apiScope].Libs...) props.Aidl.Include_dirs = module.deviceProperties.Aidl.Include_dirs props.Aidl.Local_include_dirs = module.deviceProperties.Aidl.Local_include_dirs props.Java_version = module.properties.Java_version @@ -1660,26 +1957,22 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC props.Annotations_enabled = module.sdkLibraryProperties.Annotations_enabled props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs + props.Aconfig_declarations = module.sdkLibraryProperties.Aconfig_declarations droidstubsArgs := []string{} if len(module.sdkLibraryProperties.Api_packages) != 0 { droidstubsArgs = append(droidstubsArgs, "--stub-packages "+strings.Join(module.sdkLibraryProperties.Api_packages, ":")) } - if len(module.sdkLibraryProperties.Hidden_api_packages) != 0 { - droidstubsArgs = append(droidstubsArgs, - android.JoinWithPrefix(module.sdkLibraryProperties.Hidden_api_packages, " --hide-package ")) - } droidstubsArgs = append(droidstubsArgs, module.sdkLibraryProperties.Droiddoc_options...) - disabledWarnings := []string{ - "BroadcastBehavior", - "DeprecationMismatch", - "HiddenSuperclass", - "HiddenTypeParameter", - "MissingPermission", - "SdkConstant", - "Todo", - "Typo", - "UnavailableSymbol", + disabledWarnings := []string{"HiddenSuperclass"} + if proptools.BoolDefault(module.sdkLibraryProperties.Api_lint.Legacy_errors_allowed, true) { + disabledWarnings = append(disabledWarnings, + "BroadcastBehavior", + "DeprecationMismatch", + "MissingPermission", + "SdkConstant", + "Todo", + ) } droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide ")) @@ -1739,23 +2032,153 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC if !Bool(module.sdkLibraryProperties.No_dist) { // Dist the api txt and removed api txt artifacts for sdk builds. distDir := proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api")) + stubsTypeTagPrefix := "" + if mctx.Config().ReleaseHiddenApiExportableStubs() { + stubsTypeTagPrefix = ".exportable" + } for _, p := range []struct { tag string pattern string }{ - {tag: ".api.txt", pattern: "%s.txt"}, - {tag: ".removed-api.txt", pattern: "%s-removed.txt"}, + // "exportable" api files are copied to the dist directory instead of the + // "everything" api files when "RELEASE_HIDDEN_API_EXPORTABLE_STUBS" build flag + // is set. Otherwise, the "everything" api files are copied to the dist directory. + {tag: "%s.api.txt", pattern: "%s.txt"}, + {tag: "%s.removed-api.txt", pattern: "%s-removed.txt"}, } { props.Dists = append(props.Dists, android.Dist{ Targets: []string{"sdk", "win_sdk"}, Dir: distDir, Dest: proptools.StringPtr(fmt.Sprintf(p.pattern, module.distStem())), - Tag: proptools.StringPtr(p.tag), + Tag: proptools.StringPtr(fmt.Sprintf(p.tag, stubsTypeTagPrefix)), }) } } - mctx.CreateModule(DroidstubsFactory, &props).(*Droidstubs).CallHookIfAvailable(mctx) + mctx.CreateModule(DroidstubsFactory, &props, module.sdkComponentPropertiesForChildLibrary()).(*Droidstubs).CallHookIfAvailable(mctx) +} + +func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope, alternativeFullApiSurfaceStub string) { + props := struct { + Name *string + Visibility []string + Api_contributions []string + Libs []string + Static_libs []string + Full_api_surface_stub *string + System_modules *string + Enable_validation *bool + Stubs_type *string + }{} + + props.Name = proptools.StringPtr(module.apiLibraryModuleName(apiScope)) + props.Visibility = []string{"//visibility:override", "//visibility:private"} + + apiContributions := []string{} + + // Api surfaces are not independent of each other, but have subset relationships, + // and so does the api files. To generate from-text stubs for api surfaces other than public, + // all subset api domains' api_contriubtions must be added as well. + scope := apiScope + for scope != nil { + apiContributions = append(apiContributions, module.stubsSourceModuleName(scope)+".api.contribution") + scope = scope.extends + } + if apiScope == apiScopePublic { + additionalApiContribution := module.apiLibraryAdditionalApiContribution() + if additionalApiContribution != "" { + apiContributions = append(apiContributions, additionalApiContribution) + } + } + + props.Api_contributions = apiContributions + props.Libs = module.properties.Libs + props.Libs = append(props.Libs, module.sdkLibraryProperties.Stub_only_libs...) + props.Libs = append(props.Libs, module.scopeToProperties[apiScope].Libs...) + props.Libs = append(props.Libs, "stub-annotations") + props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs + props.Full_api_surface_stub = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName()) + if alternativeFullApiSurfaceStub != "" { + props.Full_api_surface_stub = proptools.StringPtr(alternativeFullApiSurfaceStub) + } + + // android_module_lib_stubs_current.from-text only comprises api contributions from art, conscrypt and i18n. + // Thus, replace with android_module_lib_stubs_current_full.from-text, which comprises every api domains. + if apiScope.kind == android.SdkModule { + props.Full_api_surface_stub = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName() + "_full.from-text") + } + + // java_sdk_library modules that set sdk_version as none does not depend on other api + // domains. Therefore, java_api_library created from such modules should not depend on + // full_api_surface_stubs but create and compile stubs by the java_api_library module + // itself. + if module.SdkVersion(mctx).Kind == android.SdkNone { + props.Full_api_surface_stub = nil + } + + props.System_modules = module.deviceProperties.System_modules + props.Enable_validation = proptools.BoolPtr(true) + props.Stubs_type = proptools.StringPtr("everything") + + mctx.CreateModule(ApiLibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) +} + +func (module *SdkLibrary) topLevelStubsLibraryProps(mctx android.DefaultableHookContext, apiScope *apiScope, doDist bool) libraryProperties { + props := libraryProperties{} + + props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_library_visibility) + sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope) + props.Sdk_version = proptools.StringPtr(sdkVersion) + + props.System_modules = module.deviceProperties.System_modules + + // The imports need to be compiled to dex if the java_sdk_library requests it. + compileDex := module.dexProperties.Compile_dex + if module.stubLibrariesCompiledForDex() { + compileDex = proptools.BoolPtr(true) + } + props.Compile_dex = compileDex + + if !Bool(module.sdkLibraryProperties.No_dist) && doDist { + props.Dist.Targets = []string{"sdk", "win_sdk"} + props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.distStem())) + props.Dist.Dir = proptools.StringPtr(module.apiDistPath(apiScope)) + props.Dist.Tag = proptools.StringPtr(".jar") + } + + return props +} + +func (module *SdkLibrary) createTopLevelStubsLibrary( + mctx android.DefaultableHookContext, apiScope *apiScope, contributesToApiSurface bool) { + + // Dist the "everything" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is false + doDist := !mctx.Config().ReleaseHiddenApiExportableStubs() + props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist) + props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope)) + + // Add the stub compiling java_library/java_api_library as static lib based on build config + staticLib := module.sourceStubsLibraryModuleName(apiScope) + if mctx.Config().BuildFromTextStub() && contributesToApiSurface { + staticLib = module.apiLibraryModuleName(apiScope) + } + props.Static_libs = append(props.Static_libs, staticLib) + + mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) +} + +func (module *SdkLibrary) createTopLevelExportableStubsLibrary( + mctx android.DefaultableHookContext, apiScope *apiScope) { + + // Dist the "exportable" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is true + doDist := mctx.Config().ReleaseHiddenApiExportableStubs() + props := module.topLevelStubsLibraryProps(mctx, apiScope, doDist) + props.Name = proptools.StringPtr(module.exportableStubsLibraryModuleName(apiScope)) + + staticLib := module.exportableSourceStubsLibraryModuleName(apiScope) + props.Static_libs = append(props.Static_libs, staticLib) + + mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) } func (module *SdkLibrary) compareAgainstLatestApi(apiScope *apiScope) bool { @@ -1768,6 +2191,9 @@ func (module *SdkLibrary) DepIsInSameApex(mctx android.BaseModuleContext, dep an if depTag == xmlPermissionsFileTag { return true } + if dep.Name() == module.implLibraryModuleName() { + return true + } return module.Library.DepIsInSameApex(mctx, dep) } @@ -1776,6 +2202,10 @@ func (module *SdkLibrary) UniqueApexVariations() bool { return module.uniqueApexVariations() } +func (module *SdkLibrary) ContributeToApi() bool { + return proptools.BoolDefault(module.sdkLibraryProperties.Contribute_to_android_api, false) +} + // Creates the xml file that publicizes the runtime library func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) { moduleMinApiLevel := module.Library.MinSdkVersion(mctx) @@ -1792,6 +2222,7 @@ func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) { Min_device_sdk *string Max_device_sdk *string Sdk_library_min_api_level *string + Uses_libs_dependencies []string }{ Name: proptools.StringPtr(module.xmlPermissionsModuleName()), Lib_name: proptools.StringPtr(module.BaseModuleName()), @@ -1801,6 +2232,7 @@ func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) { Min_device_sdk: module.commonSdkLibraryProperties.Min_device_sdk, Max_device_sdk: module.commonSdkLibraryProperties.Max_device_sdk, Sdk_library_min_api_level: &moduleMinApiLevelStr, + Uses_libs_dependencies: module.usesLibraryProperties.Uses_libs, } mctx.CreateModule(sdkLibraryXmlFactory, &props) @@ -1838,12 +2270,12 @@ func PrebuiltJars(ctx android.BaseModuleContext, baseName string, s android.SdkS // If either this or the other module are on the platform then this will return // false. func withinSameApexesAs(ctx android.BaseModuleContext, other android.Module) bool { - apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) - otherApexInfo := ctx.OtherModuleProvider(other, android.ApexInfoProvider).(android.ApexInfo) + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) + otherApexInfo, _ := android.OtherModuleProvider(ctx, other, android.ApexInfoProvider) return len(otherApexInfo.InApexVariants) > 0 && reflect.DeepEqual(apexInfo.InApexVariants, otherApexInfo.InApexVariants) } -func (module *SdkLibrary) sdkJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec, headerJars bool) android.Paths { +func (module *SdkLibrary) sdkJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths { // If the client doesn't set sdk_version, but if this library prefers stubs over // the impl library, let's provide the widest API surface possible. To do so, // force override sdk_version to module_current so that the closest possible API @@ -1860,11 +2292,7 @@ func (module *SdkLibrary) sdkJars(ctx android.BaseModuleContext, sdkVersion andr // * No sdk_version specified on the referencing module. // * The referencing module is in the same apex as this. if sdkVersion.Kind == android.SdkPrivate || withinSameApexesAs(ctx, module) { - if headerJars { - return module.HeaderJars() - } else { - return module.ImplementationJars() - } + return module.implLibraryHeaderJars } } @@ -1873,12 +2301,7 @@ func (module *SdkLibrary) sdkJars(ctx android.BaseModuleContext, sdkVersion andr // to satisfy SdkLibraryDependency interface func (module *SdkLibrary) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths { - return module.sdkJars(ctx, sdkVersion, true /*headerJars*/) -} - -// to satisfy SdkLibraryDependency interface -func (module *SdkLibrary) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths { - return module.sdkJars(ctx, sdkVersion, false /*headerJars*/) + return module.sdkJars(ctx, sdkVersion) } var javaSdkLibrariesKey = android.NewOnceKey("javaSdkLibraries") @@ -1898,7 +2321,7 @@ func (module *SdkLibrary) getApiDir() string { // once for public API level and once for system API level func (module *SdkLibrary) CreateInternalModules(mctx android.DefaultableHookContext) { // If the module has been disabled then don't create any child modules. - if !module.Enabled() { + if !module.Enabled(mctx) { return } @@ -1954,6 +2377,19 @@ func (module *SdkLibrary) CreateInternalModules(mctx android.DefaultableHookCont module.createStubsSourcesAndApi(mctx, scope, module.stubsSourceModuleName(scope), scope.droidstubsArgs) module.createStubsLibrary(mctx, scope) + module.createExportableStubsLibrary(mctx, scope) + + alternativeFullApiSurfaceStubLib := "" + if scope == apiScopePublic { + alternativeFullApiSurfaceStubLib = module.alternativeFullApiSurfaceStubLib() + } + contributesToApiSurface := module.contributesToApiSurface(mctx.Config()) || alternativeFullApiSurfaceStubLib != "" + if contributesToApiSurface { + module.createApiLibrary(mctx, scope, alternativeFullApiSurfaceStubLib) + } + + module.createTopLevelStubsLibrary(mctx, scope, contributesToApiSurface) + module.createTopLevelExportableStubsLibrary(mctx, scope) } if module.requiresRuntimeImplementationLibrary() { @@ -2006,6 +2442,14 @@ type sdkLibraryComponentNamingScheme interface { stubsLibraryModuleName(scope *apiScope, baseName string) string stubsSourceModuleName(scope *apiScope, baseName string) string + + apiLibraryModuleName(scope *apiScope, baseName string) string + + sourceStubsLibraryModuleName(scope *apiScope, baseName string) string + + exportableStubsLibraryModuleName(scope *apiScope, baseName string) string + + exportableSourceStubsLibraryModuleName(scope *apiScope, baseName string) string } type defaultNamingScheme struct { @@ -2019,27 +2463,53 @@ func (s *defaultNamingScheme) stubsSourceModuleName(scope *apiScope, baseName st return scope.stubsSourceModuleName(baseName) } +func (s *defaultNamingScheme) apiLibraryModuleName(scope *apiScope, baseName string) string { + return scope.apiLibraryModuleName(baseName) +} + +func (s *defaultNamingScheme) sourceStubsLibraryModuleName(scope *apiScope, baseName string) string { + return scope.sourceStubLibraryModuleName(baseName) +} + +func (s *defaultNamingScheme) exportableStubsLibraryModuleName(scope *apiScope, baseName string) string { + return scope.exportableStubsLibraryModuleName(baseName) +} + +func (s *defaultNamingScheme) exportableSourceStubsLibraryModuleName(scope *apiScope, baseName string) string { + return scope.exportableSourceStubsLibraryModuleName(baseName) +} + var _ sdkLibraryComponentNamingScheme = (*defaultNamingScheme)(nil) +func hasStubsLibrarySuffix(name string, apiScope *apiScope) bool { + return strings.HasSuffix(name, apiScope.stubsLibraryModuleNameSuffix()) || + strings.HasSuffix(name, apiScope.exportableStubsLibraryModuleNameSuffix()) +} + func moduleStubLinkType(name string) (stub bool, ret sdkLinkType) { + name = strings.TrimSuffix(name, ".from-source") + // This suffix-based approach is fragile and could potentially mis-trigger. // TODO(b/155164730): Clean this up when modules no longer reference sdk_lib stubs directly. - if strings.HasSuffix(name, apiScopePublic.stubsLibraryModuleNameSuffix()) { + if hasStubsLibrarySuffix(name, apiScopePublic) { if name == "hwbinder.stubs" || name == "libcore_private.stubs" { // Due to a previous bug, these modules were not considered stubs, so we retain that. return false, javaPlatform } return true, javaSdk } - if strings.HasSuffix(name, apiScopeSystem.stubsLibraryModuleNameSuffix()) { + if hasStubsLibrarySuffix(name, apiScopeSystem) { return true, javaSystem } - if strings.HasSuffix(name, apiScopeModuleLib.stubsLibraryModuleNameSuffix()) { + if hasStubsLibrarySuffix(name, apiScopeModuleLib) { return true, javaModule } - if strings.HasSuffix(name, apiScopeTest.stubsLibraryModuleNameSuffix()) { + if hasStubsLibrarySuffix(name, apiScopeTest) { return true, javaSystem } + if hasStubsLibrarySuffix(name, apiScopeSystemServer) { + return true, javaSystemServer + } return false, javaPlatform } @@ -2060,7 +2530,7 @@ func SdkLibraryFactory() android.Module { // Initialize the map from scope to scope specific properties. scopeToProperties := make(map[*apiScope]*ApiScopeProperties) - for _, scope := range allApiScopes { + for _, scope := range AllApiScopes { scopeToProperties[scope] = scope.scopeSpecificProperties(module) } module.scopeToProperties = scopeToProperties @@ -2087,48 +2557,9 @@ func SdkLibraryFactory() android.Module { module.CreateInternalModules(ctx) } }) - android.InitBazelModule(module) return module } -type bazelSdkLibraryAttributes struct { - Public bazel.StringAttribute - System bazel.StringAttribute - Test bazel.StringAttribute - Module_lib bazel.StringAttribute - System_server bazel.StringAttribute -} - -// java_sdk_library bp2build converter -func (module *SdkLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) { - if ctx.ModuleType() != "java_sdk_library" { - return - } - - nameToAttr := make(map[string]bazel.StringAttribute) - - for _, scope := range module.getGeneratedApiScopes(ctx) { - apiSurfaceFile := path.Join(module.getApiDir(), scope.apiFilePrefix+"current.txt") - var scopeStringAttribute bazel.StringAttribute - scopeStringAttribute.SetValue(apiSurfaceFile) - nameToAttr[scope.name] = scopeStringAttribute - } - - attrs := bazelSdkLibraryAttributes{ - Public: nameToAttr["public"], - System: nameToAttr["system"], - Test: nameToAttr["test"], - Module_lib: nameToAttr["module-lib"], - System_server: nameToAttr["system-server"], - } - props := bazel.BazelTargetModuleProperties{ - Rule_class: "java_sdk_library", - Bzl_load_location: "//build/bazel/rules/java:sdk_library.bzl", - } - - ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, &attrs) -} - // // SDK library prebuilts // @@ -2165,6 +2596,11 @@ type sdkLibraryImportProperties struct { // If not empty, classes are restricted to the specified packages and their sub-packages. Permitted_packages []string + + // Name of the source soong module that gets shadowed by this prebuilt + // If unspecified, follows the naming convention that the source module of + // the prebuilt is Name() without "prebuilt_" prefix + Source_module_name *string } type SdkLibraryImport struct { @@ -2183,16 +2619,13 @@ type SdkLibraryImport struct { commonToSdkLibraryAndImport - // The reference to the implementation library created by the source module. - // Is nil if the source module does not exist. - implLibraryModule *Library - // The reference to the xml permissions module created by the source module. // Is nil if the source module does not exist. xmlPermissionsFileModule *sdkLibraryXml // Build path to the dex implementation jar obtained from the prebuilt_apex, if any. - dexJarFile OptionalDexJarPath + dexJarFile OptionalDexJarPath + dexJarFileErr error // Expected install file path of the source module(sdk_library) // or dex implementation jar obtained from the prebuilt_apex, if any. @@ -2214,7 +2647,7 @@ var allScopeStructType = createAllScopePropertiesStructType() // Dynamically create a structure type for each apiscope in allApiScopes. func createAllScopePropertiesStructType() reflect.Type { var fields []reflect.StructField - for _, apiScope := range allApiScopes { + for _, apiScope := range AllApiScopes { field := reflect.StructField{ Name: apiScope.fieldName, Type: reflect.TypeOf(sdkLibraryScopeProperties{}), @@ -2232,7 +2665,7 @@ func createPropertiesInstance() (interface{}, map[*apiScope]*sdkLibraryScopeProp allScopePropertiesStruct := allScopePropertiesPtr.Elem() scopeProperties := make(map[*apiScope]*sdkLibraryScopeProperties) - for _, apiScope := range allApiScopes { + for _, apiScope := range AllApiScopes { field := allScopePropertiesStruct.FieldByName(apiScope.fieldName) scopeProperties[apiScope] = field.Addr().Interface().(*sdkLibraryScopeProperties) } @@ -2277,6 +2710,10 @@ func (module *SdkLibraryImport) Name() string { return module.prebuilt.Name(module.ModuleBase.Name()) } +func (module *SdkLibraryImport) BaseModuleName() string { + return proptools.StringDefault(module.properties.Source_module_name, module.ModuleBase.Name()) +} + func (module *SdkLibraryImport) createInternalModules(mctx android.DefaultableHookContext) { // If the build is configured to use prebuilts then force this to be preferred. @@ -2294,6 +2731,10 @@ func (module *SdkLibraryImport) createInternalModules(mctx android.DefaultableHo if len(scopeProperties.Stub_srcs) > 0 { module.createPrebuiltStubsSources(mctx, apiScope, scopeProperties) } + + if scopeProperties.Current_api != nil { + module.createPrebuiltApiContribution(mctx, apiScope, scopeProperties) + } } javaSdkLibraries := javaSdkLibraries(mctx.Config()) @@ -2305,15 +2746,20 @@ func (module *SdkLibraryImport) createInternalModules(mctx android.DefaultableHo func (module *SdkLibraryImport) createJavaImportForStubs(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) { // Creates a java import for the jar with ".stubs" suffix props := struct { - Name *string - Sdk_version *string - Libs []string - Jars []string - Compile_dex *bool + Name *string + Source_module_name *string + Created_by_java_sdk_library_name *string + Sdk_version *string + Libs []string + Jars []string + Compile_dex *bool + Is_stubs_module *bool android.UserSuppliedPrebuiltProperties }{} props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope)) + props.Source_module_name = proptools.StringPtr(apiScope.stubsLibraryModuleName(module.BaseModuleName())) + props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName()) props.Sdk_version = scopeProperties.Sdk_version // Prepend any of the libs from the legacy public properties to the libs for each of the // scopes to avoid having to duplicate them in each scope. @@ -2329,24 +2775,52 @@ func (module *SdkLibraryImport) createJavaImportForStubs(mctx android.Defaultabl compileDex = proptools.BoolPtr(true) } props.Compile_dex = compileDex + props.Is_stubs_module = proptools.BoolPtr(true) mctx.CreateModule(ImportFactory, &props, module.sdkComponentPropertiesForChildLibrary()) } func (module *SdkLibraryImport) createPrebuiltStubsSources(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) { props := struct { - Name *string - Srcs []string + Name *string + Source_module_name *string + Created_by_java_sdk_library_name *string + Srcs []string android.UserSuppliedPrebuiltProperties }{} props.Name = proptools.StringPtr(module.stubsSourceModuleName(apiScope)) + props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName())) + props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName()) props.Srcs = scopeProperties.Stub_srcs // The stubs source is preferred if the java_sdk_library_import is preferred. props.CopyUserSuppliedPropertiesFromPrebuilt(&module.prebuilt) - mctx.CreateModule(PrebuiltStubsSourcesFactory, &props) + mctx.CreateModule(PrebuiltStubsSourcesFactory, &props, module.sdkComponentPropertiesForChildLibrary()) +} + +func (module *SdkLibraryImport) createPrebuiltApiContribution(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) { + api_file := scopeProperties.Current_api + api_surface := &apiScope.name + + props := struct { + Name *string + Source_module_name *string + Created_by_java_sdk_library_name *string + Api_surface *string + Api_file *string + Visibility []string + }{} + + props.Name = proptools.StringPtr(module.stubsSourceModuleName(apiScope) + ".api.contribution") + props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()) + ".api.contribution") + props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName()) + props.Api_surface = api_surface + props.Api_file = api_file + props.Visibility = []string{"//visibility:override", "//visibility:public"} + + mctx.CreateModule(ApiContributionImportFactory, &props, module.sdkComponentPropertiesForChildLibrary()) } // Add the dependencies on the child module in the component deps mutator so that it @@ -2358,7 +2832,7 @@ func (module *SdkLibraryImport) ComponentDepsMutator(ctx android.BottomUpMutator } // Add dependencies to the prebuilt stubs library - ctx.AddVariationDependencies(nil, apiScope.stubsTag, android.PrebuiltNameFromSource(module.stubsLibraryModuleName(apiScope))) + ctx.AddVariationDependencies(nil, apiScope.prebuiltStubsTag, android.PrebuiltNameFromSource(module.stubsLibraryModuleName(apiScope))) if len(scopeProperties.Stub_srcs) > 0 { // Add dependencies to the prebuilt stubs source library @@ -2382,14 +2856,6 @@ 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 @@ -2423,18 +2889,6 @@ func (module *SdkLibraryImport) MinSdkVersion(ctx android.EarlyModuleContext) an var _ hiddenAPIModule = (*SdkLibraryImport)(nil) -func (module *SdkLibraryImport) OutputFiles(tag string) (android.Paths, error) { - 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) @@ -2483,14 +2937,18 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo if ctx.Device() { // If this is a variant created for a prebuilt_apex then use the dex implementation jar // obtained from the associated deapexer module. - ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + ai, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) if ai.ForPrebuiltApex { // Get the path of the dex implementation jar from the `deapexer` module. - di := android.FindDeapexerProviderForModule(ctx) - if di == nil { - return // An error has been reported by FindDeapexerProviderForModule. + di, err := android.FindDeapexerProviderForModule(ctx) + if err != nil { + // An error was found, possibly due to multiple apexes in the tree that export this library + // Defer the error till a client tries to call DexJarBuildPath + module.dexJarFileErr = err + module.initHiddenAPIError(err) + return } - dexJarFileApexRootRelative := apexRootRelativePathToJavaLib(module.BaseModuleName()) + dexJarFileApexRootRelative := ApexRootRelativePathToJavaLib(module.BaseModuleName()) if dexOutputPath := di.PrebuiltExportPath(dexJarFileApexRootRelative); dexOutputPath != nil { dexJarFile := makeDexJarPathFromPath(dexOutputPath) module.dexJarFile = dexJarFile @@ -2499,16 +2957,13 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo module.installFile = installPath module.initHiddenAPI(ctx, dexJarFile, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil) - module.dexpreopter.installPath = module.dexpreopter.getInstallPath(ctx, installPath) + module.dexpreopter.installPath = module.dexpreopter.getInstallPath(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), installPath) module.dexpreopter.isSDKLibrary = true - module.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &module.dexpreopter) + module.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName()), &module.dexpreopter) if profilePath := di.PrebuiltExportPath(dexJarFileApexRootRelative + ".prof"); profilePath != nil { module.dexpreopter.inputProfilePathOnHost = profilePath } - - // Dexpreopting. - 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. @@ -2516,6 +2971,11 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo } } } + + module.setOutputFiles(ctx) + if module.implLibraryModule != nil { + setOutputFiles(ctx, module.implLibraryModule.Module) + } } func (module *SdkLibraryImport) sdkJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec, headerJars bool) android.Paths { @@ -2540,23 +3000,20 @@ func (module *SdkLibraryImport) SdkHeaderJars(ctx android.BaseModuleContext, sdk return module.sdkJars(ctx, sdkVersion, true) } -// to satisfy SdkLibraryDependency interface -func (module *SdkLibraryImport) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec) android.Paths { - // This module is just a wrapper for the stubs. - return module.sdkJars(ctx, sdkVersion, false) -} - // to satisfy UsesLibraryDependency interface -func (module *SdkLibraryImport) DexJarBuildPath() OptionalDexJarPath { +func (module *SdkLibraryImport) DexJarBuildPath(ctx android.ModuleErrorfContext) OptionalDexJarPath { // The dex implementation jar extracted from the .apex file should be used in preference to the // source. + if module.dexJarFileErr != nil { + ctx.ModuleErrorf(module.dexJarFileErr.Error()) + } if module.dexJarFile.IsSet() { return module.dexJarFile } if module.implLibraryModule == nil { return makeUnsetDexJarPath() } else { - return module.implLibraryModule.DexJarBuildPath() + return module.implLibraryModule.DexJarBuildPath(ctx) } } @@ -2639,6 +3096,10 @@ func (module *SdkLibraryImport) RequiredFilesFromPrebuiltApex(ctx android.BaseMo return requiredFilesFromPrebuiltApexForImport(name, &module.dexpreopter) } +func (j *SdkLibraryImport) UseProfileGuidedDexpreopt() bool { + return proptools.Bool(j.importDexpreoptProperties.Dex_preopt.Profile_guided) +} + // java_sdk_library_xml type sdkLibraryXml struct { android.ModuleBase @@ -2688,6 +3149,11 @@ type sdkLibraryXmlProperties struct { // // This value comes from the ApiLevel of the MinSdkVersion property. Sdk_library_min_api_level *string + + // Uses-libs dependencies that the shared library requires to work correctly. + // + // This will add dependency="foo:bar" to the <library> section. + Uses_libs_dependencies []string } // java_sdk_library_xml builds the permission xml file for a java_sdk_library. @@ -2719,10 +3185,7 @@ func (module *sdkLibraryXml) SubDir() string { return "permissions" } -// from android.PrebuiltEtcModule -func (module *sdkLibraryXml) OutputFile() android.OutputPath { - return module.outputFilePath -} +var _ etc.PrebuiltEtcModule = (*sdkLibraryXml)(nil) // from android.ApexModule func (module *sdkLibraryXml) AvailableFor(what string) bool { @@ -2745,11 +3208,11 @@ func (module *sdkLibraryXml) ShouldSupportSdkVersion(ctx android.BaseModuleConte // File path to the runtime implementation library func (module *sdkLibraryXml) implPath(ctx android.ModuleContext) string { implName := proptools.String(module.properties.Lib_name) - if apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo); !apexInfo.IsForPlatform() { + if apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider); !apexInfo.IsForPlatform() { // TODO(b/146468504): ApexVariationName() is only a soong module name, not apex name. // In most cases, this works fine. But when apex_name is set or override_apex is used // this can be wrong. - return fmt.Sprintf("/apex/%s/javalib/%s.jar", apexInfo.ApexVariationName, implName) + return fmt.Sprintf("/apex/%s/javalib/%s.jar", apexInfo.BaseApexName, implName) } partition := "system" if module.SocSpecific() { @@ -2793,7 +3256,14 @@ func formattedOptionalAttribute(attrName string, value *string) string { if value == nil { return "" } - return fmt.Sprintf(` %s=\"%s\"\n`, attrName, *value) + return fmt.Sprintf(" %s=\"%s\"\n", attrName, *value) +} + +func formattedDependenciesAttribute(dependencies []string) string { + if dependencies == nil { + return "" + } + return fmt.Sprintf(" dependency=\"%s\"\n", strings.Join(dependencies, ":")) } func (module *sdkLibraryXml) permissionsContents(ctx android.ModuleContext) string { @@ -2805,32 +3275,33 @@ func (module *sdkLibraryXml) permissionsContents(ctx android.ModuleContext) stri 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) + dependenciesAttr := formattedDependenciesAttribute(module.properties.Uses_libs_dependencies) // <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` + libraryTag = " <apex-library\n" } else { - libraryTag = ` <library\n` + 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`, + "<?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, @@ -2838,26 +3309,27 @@ func (module *sdkLibraryXml) permissionsContents(ctx android.ModuleContext) stri implicitUntilAttr, minSdkAttr, maxSdkAttr, - ` />\n`, - `</permissions>\n`}, "") + dependenciesAttr, + " />\n", + "</permissions>\n", + }, "") } func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleContext) { - module.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform() + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) + module.hideApexVariantFromMake = !apexInfo.IsForPlatform() libName := proptools.String(module.properties.Lib_name) module.selfValidate(ctx) xmlContent := module.permissionsContents(ctx) module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath - rule := android.NewRuleBuilder(pctx, ctx) - rule.Command(). - Text("/bin/bash -c \"echo -e '" + xmlContent + "'\" > "). - Output(module.outputFilePath) - - rule.Build("java_sdk_xml", "Permission XML") + android.WriteFileRuleVerbatim(ctx, module.outputFilePath, xmlContent) module.installDirPath = android.PathForModuleInstall(ctx, "etc", module.SubDir()) + ctx.PackageFile(module.installDirPath, libName+".xml", module.outputFilePath) + + ctx.SetOutputFiles(android.OutputPaths{module.outputFilePath}.Paths(), "") } func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries { @@ -3065,7 +3537,7 @@ func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMembe s.Stem = sdk.distStem() s.Scopes = make(map[*apiScope]*scopeProperties) - for _, apiScope := range allApiScopes { + for _, apiScope := range AllApiScopes { paths := sdk.findScopePaths(apiScope) if paths == nil { continue @@ -3101,7 +3573,8 @@ func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMembe s.Min_device_sdk = sdk.commonSdkLibraryProperties.Min_device_sdk s.Max_device_sdk = sdk.commonSdkLibraryProperties.Max_device_sdk - if sdk.dexpreopter.dexpreoptProperties.Dex_preopt_result.Profile_guided { + implLibrary := sdk.getImplLibraryModule() + if implLibrary != nil && implLibrary.dexpreopter.dexpreoptProperties.Dex_preopt_result.Profile_guided { s.DexPreoptProfileGuided = proptools.BoolPtr(true) } } @@ -3126,7 +3599,7 @@ func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberCo stem := s.Stem - for _, apiScope := range allApiScopes { + for _, apiScope := range AllApiScopes { if properties, ok := s.Scopes[apiScope]; ok { scopeSet := propertySet.AddPropertySet(apiScope.propertyName) diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go index 1d0c13d4b..a8a1494ee 100644 --- a/java/sdk_library_test.go +++ b/java/sdk_library_test.go @@ -35,6 +35,14 @@ func TestJavaSdkLibrary(t *testing.T) { "29": {"foo"}, "30": {"bar", "barney", "baz", "betty", "foo", "fred", "quuz", "wilma"}, }), + android.FixtureModifyConfig(func(config android.Config) { + config.SetApiLibraries([]string{"foo"}) + }), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildFlags = map[string]string{ + "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", + } + }), ).RunTestWithBp(t, ` droiddoc_exported_dir { name: "droiddoc-templates-sdk", @@ -71,6 +79,8 @@ func TestJavaSdkLibrary(t *testing.T) { name: "quuz", public: { jars: ["c.jar"], + current_api: "api/current.txt", + removed_api: "api/removed.txt", }, } java_sdk_library_import { @@ -121,18 +131,22 @@ func TestJavaSdkLibrary(t *testing.T) { result.ModuleForTests(apiScopeSystem.stubsSourceModuleName("foo"), "android_common") result.ModuleForTests(apiScopeTest.stubsSourceModuleName("foo"), "android_common") result.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo")+".api.contribution", "") + result.ModuleForTests(apiScopePublic.apiLibraryModuleName("foo"), "android_common") result.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common") result.ModuleForTests("foo.api.public.28", "") result.ModuleForTests("foo.api.system.28", "") result.ModuleForTests("foo.api.test.28", "") - exportedComponentsInfo := result.ModuleProvider(foo.Module(), android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo) + exportedComponentsInfo, _ := android.SingletonModuleProvider(result, foo.Module(), android.ExportedComponentsInfoProvider) expectedFooExportedComponents := []string{ - "foo-removed.api.public.latest", - "foo-removed.api.system.latest", - "foo.api.public.latest", - "foo.api.system.latest", + "foo-removed.api.combined.public.latest", + "foo-removed.api.combined.system.latest", + "foo.api.combined.public.latest", + "foo.api.combined.system.latest", "foo.stubs", + "foo.stubs.exportable", + "foo.stubs.exportable.system", + "foo.stubs.exportable.test", "foo.stubs.source", "foo.stubs.source.system", "foo.stubs.source.test", @@ -169,13 +183,16 @@ func TestJavaSdkLibrary(t *testing.T) { 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") + // test if quuz have created the api_contribution module + result.ModuleForTests(apiScopePublic.stubsSourceModuleName("quuz")+".api.contribution", "") + + fooImplDexJar := result.ModuleForTests("foo.impl", "android_common").Rule("d8") + // tests if kotlinc generated files are NOT excluded from output of foo.impl. + android.AssertStringDoesNotContain(t, "foo.impl dex", fooImplDexJar.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") + barImplDexJar := result.ModuleForTests("bar.impl", "android_common").Rule("d8") + // tests if kotlinc generated files are excluded from output of bar.impl. + android.AssertStringDoesContain(t, "bar.impl dex", barImplDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module") } func TestJavaSdkLibrary_UpdatableLibrary(t *testing.T) { @@ -210,19 +227,21 @@ func TestJavaSdkLibrary_UpdatableLibrary(t *testing.T) { `) // 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\"`) + fooUpdatable := result.ModuleForTests("fooUpdatable.xml", "android_common").Output("fooUpdatable.xml") + fooUpdatableContents := android.ContentFromFileRuleForTests(t, result.TestContext, fooUpdatable) + android.AssertStringDoesContain(t, "fooUpdatable.xml contents", fooUpdatableContents, `on-bootclasspath-since="U"`) + android.AssertStringDoesContain(t, "fooUpdatable.xml contents", fooUpdatableContents, `on-bootclasspath-before="V"`) + android.AssertStringDoesContain(t, "fooUpdatable.xml contents", fooUpdatableContents, `min-device-sdk="W"`) + android.AssertStringDoesContain(t, "fooUpdatable.xml contents", fooUpdatableContents, `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`) + fooPermissions := result.ModuleForTests("foo.xml", "android_common").Output("foo.xml") + fooPermissionsContents := android.ContentFromFileRuleForTests(t, result.TestContext, fooPermissions) + android.AssertStringDoesNotContain(t, "foo.xml contents", fooPermissionsContents, `on-bootclasspath-since`) + android.AssertStringDoesNotContain(t, "foo.xml contents", fooPermissionsContents, `on-bootclasspath-before`) + android.AssertStringDoesNotContain(t, "foo.xml contents", fooPermissionsContents, `min-device-sdk`) + android.AssertStringDoesNotContain(t, "foo.xml contents", fooPermissionsContents, `max-device-sdk`) } func TestJavaSdkLibrary_UpdatableLibrary_Validation_ValidVersion(t *testing.T) { @@ -353,9 +372,10 @@ func TestJavaSdkLibrary_UpdatableLibrary_usesNewTag(t *testing.T) { } `) // 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`) + fooUpdatable := result.ModuleForTests("foo.xml", "android_common").Output("foo.xml") + fooUpdatableContents := android.ContentFromFileRuleForTests(t, result.TestContext, fooUpdatable) + android.AssertStringDoesContain(t, "foo.xml contents", fooUpdatableContents, `<apex-library`) + android.AssertStringDoesNotContain(t, "foo.xml contents", fooUpdatableContents, `<library`) } func TestJavaSdkLibrary_StubOrImplOnlyLibs(t *testing.T) { @@ -407,10 +427,9 @@ func TestJavaSdkLibrary_StubOrImplOnlyLibs(t *testing.T) { android.AssertStringListContainsEquals(t, "bad combined inputs for "+sdklib, combineJarInputs, depPath, combined) } for _, expectation := range expectations { - verify("sdklib", expectation.lib, expectation.on_impl_classpath, expectation.in_impl_combined) verify("sdklib.impl", expectation.lib, expectation.on_impl_classpath, expectation.in_impl_combined) - stubName := apiScopePublic.stubsLibraryModuleName("sdklib") + stubName := apiScopePublic.sourceStubLibraryModuleName("sdklib") verify(stubName, expectation.lib, expectation.on_stub_classpath, expectation.in_stub_combined) } } @@ -473,7 +492,7 @@ func TestJavaSdkLibrary_AccessOutputFiles_NoAnnotations(t *testing.T) { 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`)). + ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": unsupported module reference tag ".public.annotations.zip"`)). RunTestWithBp(t, ` java_sdk_library { name: "foo", @@ -498,7 +517,7 @@ func TestJavaSdkLibrary_AccessOutputFiles_MissingScope(t *testing.T) { PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("foo"), ). - ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`"foo" does not provide api scope system`)). + ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "bar" variant "android_common": unsupported module reference tag ".system.stubs.source"`)). RunTestWithBp(t, ` java_sdk_library { name: "foo", @@ -521,6 +540,11 @@ func TestJavaSdkLibrary_Deps(t *testing.T) { prepareForJavaTest, PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("sdklib"), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildFlags = map[string]string{ + "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", + } + }), ).RunTestWithBp(t, ` java_sdk_library { name: "sdklib", @@ -535,10 +559,11 @@ func TestJavaSdkLibrary_Deps(t *testing.T) { CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{ `dex2oatd`, - `sdklib-removed.api.public.latest`, - `sdklib.api.public.latest`, + `sdklib-removed.api.combined.public.latest`, + `sdklib.api.combined.public.latest`, `sdklib.impl`, `sdklib.stubs`, + `sdklib.stubs.exportable`, `sdklib.stubs.source`, `sdklib.xml`, }) @@ -581,7 +606,7 @@ func TestJavaSdkLibraryImport_AccessOutputFiles_Invalid(t *testing.T) { t.Run("stubs.source", func(t *testing.T) { prepareForJavaTest. - ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`stubs.source not available for api scope public`)). + ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo" is not a SourceFileProducer or having valid output file for tag ".public.stubs.source"`)). RunTestWithBp(t, bp+` java_library { name: "bar", @@ -596,7 +621,7 @@ func TestJavaSdkLibraryImport_AccessOutputFiles_Invalid(t *testing.T) { t.Run("api.txt", func(t *testing.T) { prepareForJavaTest. - ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`api.txt not available for api scope public`)). + ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo" is not a SourceFileProducer or having valid output file for tag ".public.api.txt"`)). RunTestWithBp(t, bp+` java_library { name: "bar", @@ -610,7 +635,7 @@ func TestJavaSdkLibraryImport_AccessOutputFiles_Invalid(t *testing.T) { t.Run("removed-api.txt", func(t *testing.T) { prepareForJavaTest. - ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`removed-api.txt not available for api scope public`)). + ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo" is not a SourceFileProducer or having valid output file for tag ".public.removed-api.txt"`)). RunTestWithBp(t, bp+` java_library { name: "bar", @@ -911,6 +936,11 @@ func TestJavaSdkLibraryImport_WithSource(t *testing.T) { prepareForJavaTest, PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("sdklib"), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildFlags = map[string]string{ + "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", + } + }), ).RunTestWithBp(t, ` java_sdk_library { name: "sdklib", @@ -933,15 +963,17 @@ func TestJavaSdkLibraryImport_WithSource(t *testing.T) { CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{ `dex2oatd`, `prebuilt_sdklib`, - `sdklib-removed.api.public.latest`, - `sdklib.api.public.latest`, + `sdklib-removed.api.combined.public.latest`, + `sdklib.api.combined.public.latest`, `sdklib.impl`, `sdklib.stubs`, + `sdklib.stubs.exportable`, `sdklib.stubs.source`, `sdklib.xml`, }) CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{ + `all_apex_contributions`, `prebuilt_sdklib.stubs`, `sdklib.impl`, // This should be prebuilt_sdklib.stubs but is set to sdklib.stubs because the @@ -957,6 +989,11 @@ func testJavaSdkLibraryImport_Preferred(t *testing.T, prefer string, preparer an PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("sdklib"), preparer, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildFlags = map[string]string{ + "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", + } + }), ).RunTestWithBp(t, ` java_sdk_library { name: "sdklib", @@ -1005,15 +1042,17 @@ func testJavaSdkLibraryImport_Preferred(t *testing.T, prefer string, preparer an CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{ `prebuilt_sdklib`, - `sdklib-removed.api.public.latest`, - `sdklib.api.public.latest`, + `sdklib-removed.api.combined.public.latest`, + `sdklib.api.combined.public.latest`, `sdklib.impl`, `sdklib.stubs`, + `sdklib.stubs.exportable`, `sdklib.stubs.source`, `sdklib.xml`, }) CheckModuleDependencies(t, result.TestContext, "prebuilt_sdklib", "android_common", []string{ + `all_apex_contributions`, `dex2oatd`, `prebuilt_sdklib.stubs`, `prebuilt_sdklib.stubs.source`, @@ -1046,18 +1085,131 @@ func TestJavaSdkLibraryImport_Preferred(t *testing.T) { t.Run("prefer", func(t *testing.T) { testJavaSdkLibraryImport_Preferred(t, "prefer: true,", android.NullFixturePreparer) }) +} - t.Run("use_source_config_var", func(t *testing.T) { - testJavaSdkLibraryImport_Preferred(t, - "use_source_config_var: {config_namespace: \"acme\", var_name: \"use_source\"},", - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.VendorVars = map[string]map[string]string{ - "acme": { - "use_source": "false", - }, - } - })) - }) +// If a module is listed in `mainline_module_contributions, it should be used +// It will supersede any other source vs prebuilt selection mechanism like `prefer` attribute +func TestSdkLibraryImport_MetadataModuleSupersedesPreferred(t *testing.T) { + bp := ` + apex_contributions { + name: "my_mainline_module_contributions", + api_domain: "my_mainline_module", + contents: [ + // legacy mechanism prefers the prebuilt + // mainline_module_contributions supersedes this since source is listed explicitly + "sdklib.prebuilt_preferred_using_legacy_flags", + + // legacy mechanism prefers the source + // mainline_module_contributions supersedes this since prebuilt is listed explicitly + "prebuilt_sdklib.source_preferred_using_legacy_flags", + ], + } + java_sdk_library { + name: "sdklib.prebuilt_preferred_using_legacy_flags", + srcs: ["a.java"], + sdk_version: "none", + system_modules: "none", + public: { + enabled: true, + }, + system: { + enabled: true, + } + } + java_sdk_library_import { + name: "sdklib.prebuilt_preferred_using_legacy_flags", + prefer: true, // prebuilt is preferred using legacy mechanism + public: { + jars: ["a.jar"], + stub_srcs: ["a.java"], + current_api: "current.txt", + removed_api: "removed.txt", + annotations: "annotations.zip", + }, + system: { + jars: ["a.jar"], + stub_srcs: ["a.java"], + current_api: "current.txt", + removed_api: "removed.txt", + annotations: "annotations.zip", + }, + } + java_sdk_library { + name: "sdklib.source_preferred_using_legacy_flags", + srcs: ["a.java"], + sdk_version: "none", + system_modules: "none", + public: { + enabled: true, + }, + system: { + enabled: true, + } + } + java_sdk_library_import { + name: "sdklib.source_preferred_using_legacy_flags", + prefer: false, // source is preferred using legacy mechanism + public: { + jars: ["a.jar"], + stub_srcs: ["a.java"], + current_api: "current.txt", + removed_api: "removed.txt", + annotations: "annotations.zip", + }, + system: { + jars: ["a.jar"], + stub_srcs: ["a.java"], + current_api: "current.txt", + removed_api: "removed.txt", + annotations: "annotations.zip", + }, + } + + // rdeps + java_library { + name: "public", + srcs: ["a.java"], + libs: [ + // this should get source since source is listed in my_mainline_module_contributions + "sdklib.prebuilt_preferred_using_legacy_flags.stubs", + "sdklib.prebuilt_preferred_using_legacy_flags.stubs.system", + + // this should get prebuilt since source is listed in my_mainline_module_contributions + "sdklib.source_preferred_using_legacy_flags.stubs", + "sdklib.source_preferred_using_legacy_flags.stubs.system", + + ], + } + ` + result := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis("sdklib.source_preferred_using_legacy_flags", "sdklib.prebuilt_preferred_using_legacy_flags"), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildFlags = map[string]string{ + "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions", + } + }), + ).RunTestWithBp(t, bp) + + // Make sure that rdeps get the correct source vs prebuilt based on mainline_module_contributions + public := result.ModuleForTests("public", "android_common") + rule := public.Output("javac/public.jar") + inputs := rule.Implicits.Strings() + expectedInputs := []string{ + // source + "out/soong/.intermediates/sdklib.prebuilt_preferred_using_legacy_flags.stubs/android_common/turbine-combined/sdklib.prebuilt_preferred_using_legacy_flags.stubs.jar", + "out/soong/.intermediates/sdklib.prebuilt_preferred_using_legacy_flags.stubs.system/android_common/turbine-combined/sdklib.prebuilt_preferred_using_legacy_flags.stubs.system.jar", + + // prebuilt + "out/soong/.intermediates/prebuilt_sdklib.source_preferred_using_legacy_flags.stubs/android_common/combined/sdklib.source_preferred_using_legacy_flags.stubs.jar", + "out/soong/.intermediates/prebuilt_sdklib.source_preferred_using_legacy_flags.stubs.system/android_common/combined/sdklib.source_preferred_using_legacy_flags.stubs.system.jar", + } + for _, expected := range expectedInputs { + if !android.InList(expected, inputs) { + t.Errorf("expected %q to contain %q", inputs, expected) + } + } } func TestJavaSdkLibraryEnforce(t *testing.T) { @@ -1078,7 +1230,6 @@ func TestJavaSdkLibraryEnforce(t *testing.T) { libraryType string fromPartition string toPartition string - enforceVendorInterface bool enforceProductInterface bool enforceJavaSdkLibraryCheck bool allowList []string @@ -1113,9 +1264,6 @@ func TestJavaSdkLibraryEnforce(t *testing.T) { android.FixtureWithRootAndroidBp(bpFile), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.EnforceProductPartitionInterface = proptools.BoolPtr(info.enforceProductInterface) - if info.enforceVendorInterface { - variables.DeviceVndkVersion = proptools.StringPtr("current") - } variables.EnforceInterPartitionJavaSdkLibrary = proptools.BoolPtr(info.enforceJavaSdkLibraryCheck) variables.InterPartitionJavaLibraryAllowList = info.allowList }), @@ -1143,7 +1291,6 @@ func TestJavaSdkLibraryEnforce(t *testing.T) { libraryType: "java_library", fromPartition: "product", toPartition: "system", - enforceVendorInterface: true, enforceProductInterface: true, enforceJavaSdkLibraryCheck: false, }, "") @@ -1152,7 +1299,6 @@ func TestJavaSdkLibraryEnforce(t *testing.T) { libraryType: "java_library", fromPartition: "product", toPartition: "system", - enforceVendorInterface: true, enforceProductInterface: false, enforceJavaSdkLibraryCheck: true, }, "") @@ -1161,7 +1307,6 @@ func TestJavaSdkLibraryEnforce(t *testing.T) { libraryType: "java_library", fromPartition: "product", toPartition: "system", - enforceVendorInterface: true, enforceProductInterface: true, enforceJavaSdkLibraryCheck: true, }, errorMessage) @@ -1170,7 +1315,6 @@ func TestJavaSdkLibraryEnforce(t *testing.T) { libraryType: "java_library", fromPartition: "vendor", toPartition: "system", - enforceVendorInterface: true, enforceProductInterface: true, enforceJavaSdkLibraryCheck: true, }, errorMessage) @@ -1179,7 +1323,6 @@ func TestJavaSdkLibraryEnforce(t *testing.T) { libraryType: "java_library", fromPartition: "vendor", toPartition: "system", - enforceVendorInterface: true, enforceProductInterface: true, enforceJavaSdkLibraryCheck: true, allowList: []string{"bar"}, @@ -1189,7 +1332,6 @@ func TestJavaSdkLibraryEnforce(t *testing.T) { libraryType: "java_library", fromPartition: "vendor", toPartition: "product", - enforceVendorInterface: true, enforceProductInterface: true, enforceJavaSdkLibraryCheck: true, }, errorMessage) @@ -1198,7 +1340,6 @@ func TestJavaSdkLibraryEnforce(t *testing.T) { libraryType: "java_sdk_library", fromPartition: "product", toPartition: "system", - enforceVendorInterface: true, enforceProductInterface: true, enforceJavaSdkLibraryCheck: true, }, "") @@ -1207,7 +1348,6 @@ func TestJavaSdkLibraryEnforce(t *testing.T) { libraryType: "java_sdk_library", fromPartition: "vendor", toPartition: "system", - enforceVendorInterface: true, enforceProductInterface: true, enforceJavaSdkLibraryCheck: true, }, "") @@ -1216,7 +1356,6 @@ func TestJavaSdkLibraryEnforce(t *testing.T) { libraryType: "java_sdk_library", fromPartition: "vendor", toPartition: "product", - enforceVendorInterface: true, enforceProductInterface: true, enforceJavaSdkLibraryCheck: true, }, "") @@ -1232,6 +1371,11 @@ func TestJavaSdkLibraryDist(t *testing.T) { "sdklib_group_foo", "sdklib_owner_foo", "foo"), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildFlags = map[string]string{ + "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", + } + }), ).RunTestWithBp(t, ` java_sdk_library { name: "sdklib_no_group", @@ -1288,7 +1432,7 @@ func TestJavaSdkLibraryDist(t *testing.T) { for _, tt := range testCases { t.Run(tt.module, func(t *testing.T) { - m := result.ModuleForTests(tt.module+".stubs", "android_common").Module().(*Library) + m := result.ModuleForTests(apiScopePublic.exportableStubsLibraryModuleName(tt.module), "android_common").Module().(*Library) dists := m.Dists() if len(dists) != 1 { t.Fatalf("expected exactly 1 dist entry, got %d", len(dists)) @@ -1313,11 +1457,11 @@ func TestSdkLibrary_CheckMinSdkVersion(t *testing.T) { preparer.RunTestWithBp(t, ` java_sdk_library { name: "sdklib", - srcs: ["a.java"], - static_libs: ["util"], - min_sdk_version: "30", + srcs: ["a.java"], + static_libs: ["util"], + min_sdk_version: "30", unsafe_ignore_missing_latest_api: true, - } + } java_library { name: "util", @@ -1412,3 +1556,351 @@ func TestJavaSdkLibrary_StubOnlyLibs_PassedToDroidstubs(t *testing.T) { fooStubsSources := result.ModuleForTests("foo.stubs.source", "android_common").Module().(*Droidstubs) android.AssertStringListContains(t, "foo stubs should depend on bar-lib", fooStubsSources.Javadoc.properties.Libs, "bar-lib") } + +func TestJavaSdkLibrary_Scope_Libs_PassedToDroidstubs(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis("foo"), + ).RunTestWithBp(t, ` + java_sdk_library { + name: "foo", + srcs: ["a.java"], + public: { + enabled: true, + libs: ["bar-lib"], + }, + } + + java_library { + name: "bar-lib", + srcs: ["b.java"], + } + `) + + // The foo.stubs.source should depend on bar-lib + fooStubsSources := result.ModuleForTests("foo.stubs.source", "android_common").Module().(*Droidstubs) + android.AssertStringListContains(t, "foo stubs should depend on bar-lib", fooStubsSources.Javadoc.properties.Libs, "bar-lib") +} + +func TestJavaSdkLibrary_ApiLibrary(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis("foo"), + android.FixtureModifyConfig(func(config android.Config) { + config.SetApiLibraries([]string{"foo"}) + }), + ).RunTestWithBp(t, ` + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + system: { + enabled: true, + }, + module_lib: { + enabled: true, + }, + test: { + enabled: true, + }, + } + `) + + testCases := []struct { + scope *apiScope + apiContributions []string + fullApiSurfaceStub string + }{ + { + scope: apiScopePublic, + apiContributions: []string{"foo.stubs.source.api.contribution"}, + fullApiSurfaceStub: "android_stubs_current", + }, + { + scope: apiScopeSystem, + apiContributions: []string{"foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"}, + fullApiSurfaceStub: "android_system_stubs_current", + }, + { + scope: apiScopeTest, + apiContributions: []string{"foo.stubs.source.test.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"}, + fullApiSurfaceStub: "android_test_stubs_current", + }, + { + scope: apiScopeModuleLib, + apiContributions: []string{"foo.stubs.source.module_lib.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"}, + fullApiSurfaceStub: "android_module_lib_stubs_current_full.from-text", + }, + } + + for _, c := range testCases { + m := result.ModuleForTests(c.scope.apiLibraryModuleName("foo"), "android_common").Module().(*ApiLibrary) + android.AssertArrayString(t, "Module expected to contain api contributions", c.apiContributions, m.properties.Api_contributions) + android.AssertStringEquals(t, "Module expected to contain full api surface api library", c.fullApiSurfaceStub, *m.properties.Full_api_surface_stub) + } +} + +func TestStaticDepStubLibrariesVisibility(t *testing.T) { + android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis("foo"), + android.FixtureMergeMockFs( + map[string][]byte{ + "A.java": nil, + "dir/Android.bp": []byte( + ` + java_library { + name: "bar", + srcs: ["A.java"], + libs: ["foo.stubs.from-source"], + } + `), + "dir/A.java": nil, + }, + ).ExtendWithErrorHandler( + android.FixtureExpectsAtLeastOneErrorMatchingPattern( + `module "bar" variant "android_common": depends on //.:foo.stubs.from-source which is not visible to this module`)), + ).RunTestWithBp(t, ` + java_sdk_library { + name: "foo", + srcs: ["A.java"], + } + `) +} + +func TestSdkLibraryDependency(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "30": {"bar", "foo"}, + }), + ).RunTestWithBp(t, + ` + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + } + + java_sdk_library { + name: "bar", + srcs: ["c.java", "b.java"], + libs: [ + "foo", + ], + uses_libs: [ + "foo", + ], + } +`) + + barPermissions := result.ModuleForTests("bar.xml", "android_common").Output("bar.xml") + barContents := android.ContentFromFileRuleForTests(t, result.TestContext, barPermissions) + android.AssertStringDoesContain(t, "bar.xml java_sdk_xml command", barContents, `dependency="foo"`) +} + +func TestSdkLibraryExportableStubsLibrary(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis("foo"), + android.FixtureModifyConfig(func(config android.Config) { + config.SetApiLibraries([]string{"foo"}) + }), + ).RunTestWithBp(t, ` + aconfig_declarations { + name: "bar", + package: "com.example.package", + container: "com.android.foo", + srcs: [ + "bar.aconfig", + ], + } + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + system: { + enabled: true, + }, + module_lib: { + enabled: true, + }, + test: { + enabled: true, + }, + aconfig_declarations: [ + "bar", + ], + } + `) + + exportableStubsLibraryModuleName := apiScopePublic.exportableStubsLibraryModuleName("foo") + exportableSourceStubsLibraryModuleName := apiScopePublic.exportableSourceStubsLibraryModuleName("foo") + + // Check modules generation + topLevelModule := result.ModuleForTests(exportableStubsLibraryModuleName, "android_common") + result.ModuleForTests(exportableSourceStubsLibraryModuleName, "android_common") + + // Check static lib dependency + android.AssertBoolEquals(t, "exportable top level stubs library module depends on the"+ + "exportable source stubs library module", true, + CheckModuleHasDependency(t, result.TestContext, exportableStubsLibraryModuleName, + "android_common", exportableSourceStubsLibraryModuleName), + ) + android.AssertArrayString(t, "exportable source stub library is a static lib of the"+ + "top level exportable stubs library", []string{exportableSourceStubsLibraryModuleName}, + topLevelModule.Module().(*Library).properties.Static_libs) +} + +// For java libraries depending on java_sdk_library(_import) via libs, assert that +// rdep gets stubs of source if source is listed in apex_contributions and prebuilt has prefer (legacy mechanism) +func TestStubResolutionOfJavaSdkLibraryInLibs(t *testing.T) { + bp := ` + apex_contributions { + name: "my_mainline_module_contributions", + api_domain: "my_mainline_module", + contents: ["sdklib"], // source is selected using apex_contributions, but prebuilt is selected using prefer + } + java_sdk_library { + name: "sdklib", + srcs: ["a.java"], + sdk_version: "none", + system_modules: "none", + public: { + enabled: true, + }, + } + java_sdk_library_import { + name: "sdklib", + public: { + jars: ["a.jar"], + stub_srcs: ["a.java"], + current_api: "current.txt", + removed_api: "removed.txt", + annotations: "annotations.zip", + }, + prefer: true, // Set prefer explicitly on the prebuilt. We will assert that rdep gets source in a test case. + } + // rdeps + java_library { + name: "mymodule", + srcs: ["a.java"], + sdk_version: "current", + libs: ["sdklib",], // this should be dynamically resolved to sdklib.stubs (source) or prebuilt_sdklib.stubs (prebuilt) + } + ` + + fixture := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis("sdklib"), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildFlags = map[string]string{ + // We can use any of the apex contribution build flags from build/soong/android/config.go#mainlineApexContributionBuildFlags here + "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions", + } + }), + ) + + result := fixture.RunTestWithBp(t, bp) + // Make sure that rdeps get the correct source vs prebuilt based on mainline_module_contributions + public := result.ModuleForTests("mymodule", "android_common") + rule := public.Output("javac/mymodule.jar") + inputs := rule.Implicits.Strings() + android.AssertStringListContains(t, "Could not find the expected stub on classpath", inputs, "out/soong/.intermediates/sdklib.stubs/android_common/turbine-combined/sdklib.stubs.jar") +} + +// test that rdep gets resolved to the correct version of a java_sdk_library (source or a specific prebuilt) +func TestMultipleSdkLibraryPrebuilts(t *testing.T) { + bp := ` + apex_contributions { + name: "my_mainline_module_contributions", + api_domain: "my_mainline_module", + contents: ["%s"], + } + java_sdk_library { + name: "sdklib", + srcs: ["a.java"], + sdk_version: "none", + system_modules: "none", + public: { + enabled: true, + }, + } + java_sdk_library_import { + name: "sdklib.v1", //prebuilt + source_module_name: "sdklib", + public: { + jars: ["a.jar"], + stub_srcs: ["a.java"], + current_api: "current.txt", + removed_api: "removed.txt", + annotations: "annotations.zip", + }, + } + java_sdk_library_import { + name: "sdklib.v2", //prebuilt + source_module_name: "sdklib", + public: { + jars: ["a.jar"], + stub_srcs: ["a.java"], + current_api: "current.txt", + removed_api: "removed.txt", + annotations: "annotations.zip", + }, + } + // rdeps + java_library { + name: "mymodule", + srcs: ["a.java"], + libs: ["sdklib.stubs",], + } + ` + testCases := []struct { + desc string + selectedDependencyName string + expectedStubPath string + }{ + { + desc: "Source library is selected using apex_contributions", + selectedDependencyName: "sdklib", + expectedStubPath: "out/soong/.intermediates/sdklib.stubs/android_common/turbine-combined/sdklib.stubs.jar", + }, + { + desc: "Prebuilt library v1 is selected using apex_contributions", + selectedDependencyName: "prebuilt_sdklib.v1", + expectedStubPath: "out/soong/.intermediates/prebuilt_sdklib.v1.stubs/android_common/combined/sdklib.stubs.jar", + }, + { + desc: "Prebuilt library v2 is selected using apex_contributions", + selectedDependencyName: "prebuilt_sdklib.v2", + expectedStubPath: "out/soong/.intermediates/prebuilt_sdklib.v2.stubs/android_common/combined/sdklib.stubs.jar", + }, + } + + fixture := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis("sdklib", "sdklib.v1", "sdklib.v2"), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildFlags = map[string]string{ + "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions", + } + }), + ) + + for _, tc := range testCases { + result := fixture.RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName)) + + // Make sure that rdeps get the correct source vs prebuilt based on mainline_module_contributions + public := result.ModuleForTests("mymodule", "android_common") + rule := public.Output("javac/mymodule.jar") + inputs := rule.Implicits.Strings() + android.AssertStringListContains(t, "Could not find the expected stub on classpath", inputs, tc.expectedStubPath) + } +} diff --git a/java/sdk_version_test.go b/java/sdk_version_test.go new file mode 100644 index 000000000..88351d2ef --- /dev/null +++ b/java/sdk_version_test.go @@ -0,0 +1,66 @@ +// Copyright 2024 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 stringPtr(v string) *string { + return &v +} + +func TestSystemSdkFromVendor(t *testing.T) { + fixtures := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_sdk_version = intPtr(34) + variables.Platform_sdk_codename = stringPtr("VanillaIceCream") + variables.Platform_version_active_codenames = []string{"VanillaIceCream"} + variables.Platform_systemsdk_versions = []string{"33", "34", "VanillaIceCream"} + variables.DeviceSystemSdkVersions = []string{"VanillaIceCream"} + }), + FixtureWithPrebuiltApis(map[string][]string{ + "33": {}, + "34": {}, + "35": {}, + }), + ) + + fixtures.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("incompatible sdk version")). + RunTestWithBp(t, ` + android_app { + name: "foo", + srcs: ["a.java"], + vendor: true, + sdk_version: "system_35", + }`) + + result := fixtures.RunTestWithBp(t, ` + android_app { + name: "foo", + srcs: ["a.java"], + vendor: true, + sdk_version: "system_current", + }`) + fooModule := result.ModuleForTests("foo", "android_common") + fooClasspath := fooModule.Rule("javac").Args["classpath"] + + android.AssertStringDoesContain(t, "foo classpath", fooClasspath, "prebuilts/sdk/34/system/android.jar") + android.AssertStringDoesNotContain(t, "foo classpath", fooClasspath, "prebuilts/sdk/35/system/android.jar") + android.AssertStringDoesNotContain(t, "foo classpath", fooClasspath, "prebuilts/sdk/current/system/android.jar") +} diff --git a/java/support_libraries.go b/java/support_libraries.go index af7c3c2a0..c483fc17a 100644 --- a/java/support_libraries.go +++ b/java/support_libraries.go @@ -32,7 +32,7 @@ func supportLibrariesMakeVarsProvider(ctx android.MakeVarsContext) { dir := ctx.ModuleDir(module) switch { case strings.HasPrefix(dir, "prebuilts/sdk/current/extras"), - dir == "prebuilts/sdk/current/androidx", + strings.HasPrefix(dir, "prebuilts/sdk/current/androidx"), dir == "prebuilts/sdk/current/car", dir == "prebuilts/sdk/current/optional", dir == "prebuilts/sdk/current/support": diff --git a/java/system_modules.go b/java/system_modules.go index 0efa1a41c..8e2d5d8ff 100644 --- a/java/system_modules.go +++ b/java/system_modules.go @@ -19,6 +19,7 @@ import ( "strings" "github.com/google/blueprint" + "github.com/google/blueprint/proptools" "android/soong/android" ) @@ -55,13 +56,15 @@ var ( `${config.MergeZipsCmd} -j ${workDir}/module.jar ${workDir}/classes.jar $in && ` + // Note: The version of the java.base module created must match the version // of the jlink tool which consumes it. - `${config.JmodCmd} create --module-version ${config.JlinkVersion} --target-platform android ` + + // Use LINUX-OTHER to be compatible with JDK 21+ (b/294137077) + `${config.JmodCmd} create --module-version ${config.JlinkVersion} --target-platform LINUX-OTHER ` + ` --class-path ${workDir}/module.jar ${workDir}/jmod/java.base.jmod && ` + `${config.JlinkCmd} --module-path ${workDir}/jmod --add-modules java.base --output ${outDir} ` + // Note: The system-modules jlink plugin is disabled because (a) it is not // useful on Android, and (b) it causes errors with later versions of jlink // when the jdk.internal.module is absent from java.base (as it is here). ` --disable-plugin system-modules && ` + + `rm -rf ${workDir} && ` + `cp ${config.JrtFsJar} ${outDir}/lib/`, CommandDeps: []string{ "${moduleInfoJavaPath}", @@ -159,7 +162,7 @@ func (system *SystemModules) GenerateAndroidBuildActions(ctx android.ModuleConte var jars android.Paths ctx.VisitDirectDepsWithTag(systemModulesLibsTag, func(module android.Module) { - dep, _ := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo) + dep, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider) jars = append(jars, dep.HeaderJars...) }) @@ -209,7 +212,7 @@ func (system *SystemModules) AndroidMk() android.AndroidMkData { // type and the one to use is selected at runtime. func systemModulesImportFactory() android.Module { module := &systemModulesImport{} - module.AddProperties(&module.properties) + module.AddProperties(&module.properties, &module.prebuiltProperties) android.InitPrebuiltModule(module, &module.properties.Libs) android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon) android.InitDefaultableModule(module) @@ -218,13 +221,39 @@ func systemModulesImportFactory() android.Module { type systemModulesImport struct { SystemModules - prebuilt android.Prebuilt + prebuilt android.Prebuilt + prebuiltProperties prebuiltSystemModulesProperties +} + +type prebuiltSystemModulesProperties struct { + // Name of the source soong module that gets shadowed by this prebuilt + // If unspecified, follows the naming convention that the source module of + // the prebuilt is Name() without "prebuilt_" prefix + Source_module_name *string } func (system *systemModulesImport) Name() string { return system.prebuilt.Name(system.ModuleBase.Name()) } +// BaseModuleName returns the source module that will get shadowed by this prebuilt +// e.g. +// +// java_system_modules_import { +// name: "my_system_modules.v1", +// source_module_name: "my_system_modules", +// } +// +// java_system_modules_import { +// name: "my_system_modules.v2", +// source_module_name: "my_system_modules", +// } +// +// `BaseModuleName` for both will return `my_system_modules` +func (system *systemModulesImport) BaseModuleName() string { + return proptools.StringDefault(system.prebuiltProperties.Source_module_name, system.ModuleBase.Name()) +} + func (system *systemModulesImport) Prebuilt() *android.Prebuilt { return &system.prebuilt } diff --git a/java/system_modules_test.go b/java/system_modules_test.go index 7b5a3867e..336dd2134 100644 --- a/java/system_modules_test.go +++ b/java/system_modules_test.go @@ -15,6 +15,7 @@ package java import ( + "fmt" "testing" "android/soong/android" @@ -24,7 +25,7 @@ func getModuleHeaderJarsAsRelativeToTopPaths(result *android.TestResult, moduleN paths := []string{} for _, moduleName := range moduleNames { module := result.Module(moduleName, "android_common") - info := result.ModuleProvider(module, JavaInfoProvider).(JavaInfo) + info, _ := android.SingletonModuleProvider(result, module, JavaInfoProvider) paths = append(paths, info.HeaderJars.RelativeToTop().Strings()...) } return paths @@ -111,3 +112,85 @@ func TestJavaSystemModulesMixSourceAndPrebuilt(t *testing.T) { expectedPrebuiltPaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "prebuilt_system-module1", "prebuilt_system-module2") android.AssertArrayString(t, "prebuilt system modules inputs", expectedPrebuiltPaths, prebuiltInputs.RelativeToTop().Strings()) } + +func TestMultipleSystemModulesPrebuilts(t *testing.T) { + bp := ` + // an rdep + java_library { + name: "foo", + sdk_version: "none", + system_modules: "my_system_modules", + } + + // multiple variations of java_system_modules + // source + java_system_modules { + name: "my_system_modules", + libs: ["bar"], + } + java_library { + name: "bar", + srcs: ["bar.java"], + } + // prebuilt "v1" + java_system_modules_import { + name: "my_system_modules.v1", + source_module_name: "my_system_modules", + libs: ["bar.v1"], + } + java_import { + name: "bar.v1", + source_module_name: "bar", + jars: ["bar.v1.jar"], + } + // prebuilt "v2" + java_system_modules_import { + name: "my_system_modules.v2", + source_module_name: "my_system_modules", + libs: ["bar.v2"], + } + java_import { + name: "bar.v2", + source_module_name: "bar", + jars: ["bar.v2.jar"], + } + + // selectors + apex_contributions { + name: "myapex_contributions", + contents: ["%v"], + } + ` + testCases := []struct { + desc string + selectedDependencyName string + }{ + { + desc: "Source system_modules is selected using apex_contributions", + selectedDependencyName: "my_system_modules", + }, + { + desc: "Prebuilt system_modules v1 is selected using apex_contributions", + selectedDependencyName: "prebuilt_my_system_modules.v1", + }, + { + desc: "Prebuilt system_modules v2 is selected using apex_contributions", + selectedDependencyName: "prebuilt_my_system_modules.v2", + }, + } + + for _, tc := range testCases { + res := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildFlags = map[string]string{ + "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions", + } + }), + ).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName)) + + // check that rdep gets the correct variation of system_modules + hasDep := CheckModuleHasDependency(t, res.TestContext, "foo", "android_common", tc.selectedDependencyName) + android.AssertBoolEquals(t, fmt.Sprintf("expected dependency from foo to %s\n", tc.selectedDependencyName), true, hasDep) + } +} diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go index 17d301b70..bad2cf1cf 100644 --- a/java/systemserver_classpath_fragment.go +++ b/java/systemserver_classpath_fragment.go @@ -69,6 +69,7 @@ func (p *platformSystemServerClasspathModule) GenerateAndroidBuildActions(ctx an configuredJars = configuredJars.AppendList(&standaloneConfiguredJars) classpathJars = append(classpathJars, standaloneClasspathJars...) p.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars) + p.classpathFragmentBase().installClasspathProto(ctx) } func (p *platformSystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList { @@ -87,9 +88,6 @@ type SystemServerClasspathModule struct { 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 { @@ -129,9 +127,6 @@ func (s *SystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.Mo 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 { @@ -195,7 +190,7 @@ func (b systemServerClasspathFragmentContentDependencyTag) SdkMemberType(child a return javaSdkLibrarySdkMemberType } - return javaSystemserverLibsSdkMemberType + return JavaSystemserverLibsSdkMemberType } func (b systemServerClasspathFragmentContentDependencyTag) ExportMember() bool { @@ -242,7 +237,6 @@ func (s *SystemServerClasspathModule) ComponentDepsMutator(ctx android.BottomUpM 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 { @@ -320,6 +314,10 @@ func (module *prebuiltSystemServerClasspathModule) RequiredFilesFromPrebuiltApex return nil } +func (module *prebuiltSystemServerClasspathModule) UseProfileGuidedDexpreopt() bool { + return false +} + var _ android.RequiredFilesFromPrebuiltApex = (*prebuiltSystemServerClasspathModule)(nil) func prebuiltSystemServerClasspathModuleFactory() android.Module { diff --git a/java/test_spec_test.go b/java/test_spec_test.go new file mode 100644 index 000000000..4144dad69 --- /dev/null +++ b/java/test_spec_test.go @@ -0,0 +1,122 @@ +package java + +import ( + "strings" + "testing" + + "android/soong/android" + soongTesting "android/soong/testing" + "android/soong/testing/test_spec_proto" + "google.golang.org/protobuf/proto" +) + +func TestTestSpec(t *testing.T) { + bp := `test_spec { + name: "module-name", + teamId: "12345", + tests: [ + "java-test-module-name-one", + "java-test-module-name-two" + ] + } + + java_test { + name: "java-test-module-name-one", + } + + java_test { + name: "java-test-module-name-two", + }` + result := runTestSpecTest(t, android.FixtureExpectsNoErrors, bp) + + module := result.ModuleForTests("module-name", "") + + // Check that the provider has the right contents + data, _ := android.SingletonModuleProvider(result, module.Module(), soongTesting.TestSpecProviderKey) + if !strings.HasSuffix( + data.IntermediatePath.String(), "/intermediateTestSpecMetadata.pb", + ) { + t.Errorf( + "Missing intermediates path in provider: %s", + data.IntermediatePath.String(), + ) + } + + metadata := android.ContentFromFileRuleForTests(t, result.TestContext, + module.Output(data.IntermediatePath.String())) + + metadataList := make([]*test_spec_proto.TestSpec_OwnershipMetadata, 0, 2) + teamId := "12345" + bpFilePath := "Android.bp" + targetNames := []string{ + "java-test-module-name-one", "java-test-module-name-two", + } + + for _, test := range targetNames { + targetName := test + metadata := test_spec_proto.TestSpec_OwnershipMetadata{ + TrendyTeamId: &teamId, + TargetName: &targetName, + Path: &bpFilePath, + } + metadataList = append(metadataList, &metadata) + } + testSpecMetadata := test_spec_proto.TestSpec{OwnershipMetadataList: metadataList} + protoData, _ := proto.Marshal(&testSpecMetadata) + expectedMetadata := string(protoData) + + if metadata != expectedMetadata { + t.Errorf( + "Retrieved metadata: %s doesn't contain expectedMetadata: %s", metadata, + expectedMetadata, + ) + } + + // Tests for all_test_spec singleton. + singleton := result.SingletonForTests("all_test_specs") + rule := singleton.Rule("all_test_specs_rule") + prebuiltOs := result.Config.PrebuiltOS() + expectedCmd := "out/soong/host/" + prebuiltOs + "/bin/metadata -rule test_spec -inputFile out/soong/all_test_spec_paths.rsp -outputFile out/soong/ownership/all_test_specs.pb" + expectedOutputFile := "out/soong/ownership/all_test_specs.pb" + expectedInputFile := "out/soong/.intermediates/module-name/intermediateTestSpecMetadata.pb" + if !strings.Contains( + strings.TrimSpace(rule.Output.String()), + expectedOutputFile, + ) { + t.Errorf( + "Retrieved singletonOutputFile: %s is not equal to expectedSingletonOutputFile: %s", + rule.Output.String(), expectedOutputFile, + ) + } + + if !strings.Contains( + strings.TrimSpace(rule.Inputs[0].String()), + expectedInputFile, + ) { + t.Errorf( + "Retrieved singletonInputFile: %s is not equal to expectedSingletonInputFile: %s", + rule.Inputs[0].String(), expectedInputFile, + ) + } + + if !strings.Contains( + strings.TrimSpace(rule.RuleParams.Command), + expectedCmd, + ) { + t.Errorf( + "Retrieved cmd: %s is not equal to expectedCmd: %s", + rule.RuleParams.Command, expectedCmd, + ) + } +} + +func runTestSpecTest( + t *testing.T, errorHandler android.FixtureErrorHandler, bp string, +) *android.TestResult { + return android.GroupFixturePreparers( + soongTesting.PrepareForTestWithTestingBuildComponents, + PrepareForIntegrationTestWithJava, + ). + ExtendWithErrorHandler(errorHandler). + RunTestWithBp(t, bp) +} diff --git a/java/testing.go b/java/testing.go index 6671bf0c7..5ae326d93 100644 --- a/java/testing.go +++ b/java/testing.go @@ -71,7 +71,17 @@ var prepareForTestWithFrameworkDeps = android.GroupFixturePreparers( // Needed for framework defaultJavaDir + "/framework/aidl": nil, // Needed for various deps defined in GatherRequiredDepsForTest() - defaultJavaDir + "/a.java": nil, + defaultJavaDir + "/a.java": nil, + defaultJavaDir + "/api/current.txt": nil, + defaultJavaDir + "/api/removed.txt": nil, + defaultJavaDir + "/api/system-current.txt": nil, + defaultJavaDir + "/api/system-removed.txt": nil, + defaultJavaDir + "/api/test-current.txt": nil, + defaultJavaDir + "/api/test-removed.txt": nil, + defaultJavaDir + "/api/module-lib-current.txt": nil, + defaultJavaDir + "/api/module-lib-removed.txt": nil, + defaultJavaDir + "/api/system-server-current.txt": nil, + defaultJavaDir + "/api/system-server-removed.txt": nil, // Needed for R8 rules on apps "build/make/core/proguard.flags": nil, @@ -108,8 +118,6 @@ var PrepareForTestWithDexpreoptWithoutFakeDex2oatd = android.GroupFixturePrepare dexpreopt.PrepareForTestByEnablingDexpreopt, ) -var PrepareForTestWithOverlayBuildComponents = android.FixtureRegisterWithContext(registerOverlayBuildComponents) - // Prepare a fixture to use all java module types, mutators and singletons fully. // // This should only be used by tests that want to run with as much of the build enabled as possible. @@ -220,6 +228,29 @@ func FixtureWithPrebuiltApisAndExtensions(apiLevel2Modules map[string][]string, ) } +func FixtureWithPrebuiltIncrementalApis(apiLevel2Modules map[string][]string) android.FixturePreparer { + mockFS := android.MockFS{} + path := "prebuilts/sdk/Android.bp" + + bp := fmt.Sprintf(` + prebuilt_apis { + name: "sdk", + api_dirs: ["%s"], + allow_incremental_platform_api: true, + imports_sdk_version: "none", + imports_compile_dex: true, + } + `, strings.Join(android.SortedKeys(apiLevel2Modules), `", "`)) + + for release, modules := range apiLevel2Modules { + mockFS.Merge(prebuiltApisFilesForModules([]string{release}, modules)) + } + return android.GroupFixturePreparers( + android.FixtureAddTextFile(path, bp), + android.FixtureMergeMockFs(mockFS), + ) +} + func prebuiltApisFilesForModules(apiLevels []string, modules []string) map[string][]byte { libs := append([]string{"android"}, modules...) @@ -352,6 +383,7 @@ func registerRequiredBuildComponentsForTest(ctx android.RegistrationContext) { RegisterSystemModulesBuildComponents(ctx) registerSystemserverClasspathBuildComponents(ctx) registerLintBuildComponents(ctx) + android.RegisterApexContributionsBuildComponents(ctx) } // gatherRequiredDepsForTest gathers the module definitions used by @@ -375,11 +407,22 @@ func gatherRequiredDepsForTest() string { "legacy.core.platform.api.stubs", "stable.core.platform.api.stubs", + "android_stubs_current_exportable", + "android_system_stubs_current_exportable", + "android_test_stubs_current_exportable", + "android_module_lib_stubs_current_exportable", + "android_system_server_stubs_current_exportable", + "core.current.stubs.exportable", + "legacy.core.platform.api.stubs.exportable", + "kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8", "kotlin-annotations", "stub-annotations", + + "aconfig-annotations-lib", + "unsupportedappusage", } for _, extra := range extraModules { @@ -394,26 +437,98 @@ func gatherRequiredDepsForTest() string { `, extra) } - extraApiLibraryModules := map[string]string{ - "android_stubs_current.from-text": "api/current.txt", - "android_system_stubs_current.from-text": "api/system-current.txt", - "android_test_stubs_current.from-text": "api/test-current.txt", - "android_module_lib_stubs_current.from-text": "api/module-lib-current.txt", - "android_module_lib_stubs_current_full.from-text": "api/module-lib-current.txt", - "android_system_server_stubs_current.from-text": "api/system-server-current.txt", - "core.current.stubs.from-text": "api/current.txt", - "legacy.core.platform.api.stubs.from-text": "api/current.txt", - "stable.core.platform.api.stubs.from-text": "api/current.txt", - "core-lambda-stubs.from-text": "api/current.txt", + type droidstubsStruct struct { + name string + apiSurface string + apiFile string + removedFile string + } + + var publicDroidstubs = droidstubsStruct{ + name: "api-stubs-docs-non-updatable", + apiSurface: "public", + apiFile: "api/current.txt", + removedFile: "api/removed.txt", + } + var systemDroidstubs = droidstubsStruct{ + name: "system-api-stubs-docs-non-updatable", + apiSurface: "system", + apiFile: "api/system-current.txt", + removedFile: "api/system-removed.txt", + } + var testDroidstubs = droidstubsStruct{ + name: "test-api-stubs-docs-non-updatable", + apiSurface: "test", + apiFile: "api/test-current.txt", + removedFile: "api/test-removed.txt", + } + var moduleLibDroidstubs = droidstubsStruct{ + name: "module-lib-api-stubs-docs-non-updatable", + apiSurface: "module-lib", + apiFile: "api/module-lib-current.txt", + removedFile: "api/module-lib-removed.txt", + } + var systemServerDroidstubs = droidstubsStruct{ + // This module does not exist but is named this way for consistency + name: "system-server-api-stubs-docs-non-updatable", + apiSurface: "system-server", + apiFile: "api/system-server-current.txt", + removedFile: "api/system-server-removed.txt", + } + var droidstubsStructs = []droidstubsStruct{ + publicDroidstubs, + systemDroidstubs, + testDroidstubs, + moduleLibDroidstubs, + systemServerDroidstubs, } - for libName, apiFile := range extraApiLibraryModules { + extraApiLibraryModules := map[string]droidstubsStruct{ + "android_stubs_current.from-text": publicDroidstubs, + "android_system_stubs_current.from-text": systemDroidstubs, + "android_test_stubs_current.from-text": testDroidstubs, + "android_module_lib_stubs_current.from-text": moduleLibDroidstubs, + "android_module_lib_stubs_current_full.from-text": moduleLibDroidstubs, + "android_system_server_stubs_current.from-text": systemServerDroidstubs, + "core.current.stubs.from-text": publicDroidstubs, + "legacy.core.platform.api.stubs.from-text": publicDroidstubs, + "stable.core.platform.api.stubs.from-text": publicDroidstubs, + "core-lambda-stubs.from-text": publicDroidstubs, + "android-non-updatable.stubs.from-text": publicDroidstubs, + "android-non-updatable.stubs.system.from-text": systemDroidstubs, + "android-non-updatable.stubs.test.from-text": testDroidstubs, + "android-non-updatable.stubs.module_lib.from-text": moduleLibDroidstubs, + "android-non-updatable.stubs.test_module_lib": moduleLibDroidstubs, + } + + for _, droidstubs := range droidstubsStructs { bp += fmt.Sprintf(` - java_api_library { - name: "%s", - api_files: ["%s"], - } - `, libName, apiFile) + droidstubs { + name: "%s", + api_surface: "%s", + check_api: { + current: { + api_file: "%s", + removed_api_file: "%s", + } + } + } + `, + droidstubs.name, + droidstubs.apiSurface, + droidstubs.apiFile, + droidstubs.removedFile, + ) + } + + for libName, droidstubs := range extraApiLibraryModules { + bp += fmt.Sprintf(` + java_api_library { + name: "%s", + api_contributions: ["%s"], + stubs_type: "everything", + } + `, libName, droidstubs.name+".api.contribution") } bp += ` @@ -465,10 +580,15 @@ func gatherRequiredDepsForTest() string { } ` + bp += ` + all_apex_contributions { + name: "all_apex_contributions", + } +` return bp } -func CheckModuleDependencies(t *testing.T, ctx *android.TestContext, name, variant string, expected []string) { +func getModuleDependencies(t *testing.T, ctx *android.TestContext, name, variant string) []string { t.Helper() module := ctx.ModuleForTests(name, variant).Module() deps := []string{} @@ -477,11 +597,29 @@ func CheckModuleDependencies(t *testing.T, ctx *android.TestContext, name, varia }) sort.Strings(deps) + return deps +} + +// CheckModuleDependencies checks if the expected dependencies of the module are +// identical to the actual dependencies. +func CheckModuleDependencies(t *testing.T, ctx *android.TestContext, name, variant string, expected []string) { + deps := getModuleDependencies(t, ctx, name, variant) + if actual := deps; !reflect.DeepEqual(expected, actual) { t.Errorf("expected %#q, found %#q", expected, actual) } } +// CheckModuleHasDependency returns true if the module depends on the expected dependency. +func CheckModuleHasDependency(t *testing.T, ctx *android.TestContext, name, variant string, expected string) bool { + for _, dep := range getModuleDependencies(t, ctx, name, variant) { + if dep == expected { + return true + } + } + return false +} + // CheckPlatformBootclasspathModules returns the apex:module pair for the modules depended upon by // the platform-bootclasspath module. func CheckPlatformBootclasspathModules(t *testing.T, result *android.TestResult, name string, expected []string) { @@ -494,7 +632,7 @@ func CheckPlatformBootclasspathModules(t *testing.T, result *android.TestResult, 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) + info, _ := android.SingletonModuleProvider(result, p, ClasspathFragmentProtoContentInfoProvider) android.AssertBoolEquals(t, "classpath proto generated", generated, info.ClasspathFragmentProtoGenerated) android.AssertStringEquals(t, "classpath proto contents", contents, info.ClasspathFragmentProtoContents.String()) @@ -514,7 +652,7 @@ func ApexNamePairsFromModules(ctx *android.TestContext, modules []android.Module func apexNamePairFromModule(ctx *android.TestContext, module android.Module) string { name := module.Name() var apex string - apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo) + apexInfo, _ := android.SingletonModuleProvider(ctx, module, android.ApexInfoProvider) if apexInfo.IsForPlatform() { apex = "platform" } else { @@ -573,7 +711,7 @@ var PrepareForTestWithFakeApexMutator = android.GroupFixturePreparers( func registerFakeApexMutator(ctx android.RegistrationContext) { ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { - ctx.BottomUp("apex", fakeApexMutator).Parallel() + ctx.Transition("apex", &fakeApexMutator{}) }) } @@ -588,16 +726,30 @@ var _ apexModuleBase = (*SdkLibrary)(nil) // `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) { +type fakeApexMutator struct{} + +func (f *fakeApexMutator) Split(ctx android.BaseModuleContext) []string { + switch ctx.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) + return []string{"", "apex1000"} + } + return []string{""} +} + +func (f *fakeApexMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string { + return sourceVariation +} + +func (f *fakeApexMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string { + return incomingVariation +} + +func (f *fakeApexMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) { + if variation != "" { + apexInfo := android.ApexInfo{ + ApexVariationName: "apex1000", } + android.SetProvider(ctx, android.ApexInfoProvider, apexInfo) } } diff --git a/java/tradefed.go b/java/tradefed.go index ebbdec13d..349b327e8 100644 --- a/java/tradefed.go +++ b/java/tradefed.go @@ -30,8 +30,8 @@ func tradefedJavaLibraryFactory() android.Module { return module } -func tradefedJavaLibraryInstall(ctx android.ModuleContext, path android.Path) android.Paths { +func tradefedJavaLibraryInstall(ctx android.ModuleContext, path android.Path) android.InstallPaths { installedPath := ctx.InstallFile(android.PathForModuleInstall(ctx, "tradefed"), ctx.ModuleName()+".jar", path) - return android.Paths{installedPath} + return android.InstallPaths{installedPath} } |