diff options
Diffstat (limited to 'java/aar.go')
| -rw-r--r-- | java/aar.go | 1004 |
1 files changed, 653 insertions, 351 deletions
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}, - }, - }, - }, - ) -} |