diff options
Diffstat (limited to 'java/aar.go')
| -rw-r--r-- | java/aar.go | 241 |
1 files changed, 196 insertions, 45 deletions
diff --git a/java/aar.go b/java/aar.go index c9e08e2b5..180e1d726 100644 --- a/java/aar.go +++ b/java/aar.go @@ -88,28 +88,40 @@ 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"` } type aapt struct { - aaptSrcJar android.Path - exportPackage android.Path - manifestPath android.Path - proguardOptionsFile android.Path - 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 + transitiveAaptResourcePackages android.Paths + 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 @@ -139,6 +151,10 @@ func propagateRROEnforcementMutator(ctx android.TopDownMutatorContext) { } } +func (a *aapt) useResourceProcessorBusyBox() bool { + return BoolDefault(a.aaptProperties.Use_resource_processor, false) +} + func (a *aapt) ExportPackage() android.Path { return a.exportPackage } @@ -175,8 +191,6 @@ 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") - // Find implicit or explicit asset and resource dirs assetDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Asset_dirs, "assets") resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res") @@ -348,6 +362,19 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkCon linkFlags = append(linkFlags, "--static-lib") } + if a.isLibrary && a.useResourceProcessorBusyBox() { + // When building an android_library using ResourceProcessorBusyBox the resources are merged into + // package-res.apk with --merge-only, but --no-static-lib-packages is not used so that R.txt only + // contains resources from this library. + linkFlags = append(linkFlags, "--merge-only") + } else { + // When building and app or when building an android_library without ResourceProcessorBusyBox + // --no-static-lib-packages is used to put all the resources into the app. If ResourceProcessorBusyBox + // is used then the app's R.txt will be post-processed along with the R.txt files from dependencies to + // sort resources into the right packages in R.class. + linkFlags = append(linkFlags, "--no-static-lib-packages") + } + 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") @@ -355,6 +382,7 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkCon 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 compiledResDirs []android.Paths for _, dir := range resDirs { @@ -374,7 +402,23 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkCon // of transitiveStaticLibs. transitiveStaticLibs := android.ReversePaths(staticDeps.resPackages()) - compiledOverlay = append(compiledOverlay, transitiveStaticLibs...) + if a.isLibrary && a.useResourceProcessorBusyBox() { + // 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 _, 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. @@ -437,7 +481,16 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkCon a.assetPackage = android.OptionalPathForPath(assets) } + if a.useResourceProcessorBusyBox() { + rJar := android.PathForModuleOut(ctx, "busybox/R.jar") + resourceProcessorBusyBoxGenerateBinaryR(ctx, rTxt, a.mergedManifestFile, rJar, staticDeps, a.isLibrary) + transitiveRJars = append(transitiveRJars, rJar) + a.rJar = rJar + } + a.aaptSrcJar = srcJar + a.transitiveAaptRJars = transitiveRJars + a.transitiveAaptResourcePackages = staticDeps.resPackages() a.exportPackage = packageRes a.manifestPath = manifestPath a.proguardOptionsFile = proguardOptionsFile @@ -449,7 +502,11 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkCon resPackage: a.exportPackage, manifest: a.manifestPath, additionalManifests: additionalManifests, + rTxt: a.rTxt, + rJar: a.rJar, assets: a.assetPackage, + + usedResourceProcessor: a.useResourceProcessorBusyBox(), }). Transitive(staticResourcesNodesDepSet).Build() a.rroDirsDepSet = android.NewDepSetBuilder[rroDir](android.TOPOLOGICAL). @@ -461,34 +518,93 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkCon Transitive(staticManifestsDepSet).Build() } +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) { + + 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() + } 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") + } + + 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 { - var paths android.Paths + paths := make(android.Paths, 0, len(t)) for _, dep := range t { paths = append(paths, dep.resPackage) } - return android.FirstUniquePaths(paths) + return paths } func (t transitiveAarDeps) manifests() android.Paths { - var paths android.Paths + paths := make(android.Paths, 0, len(t)) for _, dep := range t { paths = append(paths, dep.manifest) paths = append(paths, dep.additionalManifests...) } - return android.FirstUniquePaths(paths) + 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 } func (t transitiveAarDeps) assets() android.Paths { - var paths android.Paths + paths := make(android.Paths, 0, len(t)) for _, dep := range t { if dep.assets.Valid() { paths = append(paths, dep.assets.Path()) @@ -613,9 +729,12 @@ func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) a.stem = proptools.StringDefault(a.overridableDeviceProperties.Stem, ctx.ModuleName()) - ctx.CheckbuildFile(a.proguardOptionsFile) - ctx.CheckbuildFile(a.exportPackage) - ctx.CheckbuildFile(a.aaptSrcJar) + ctx.CheckbuildFile(a.aapt.proguardOptionsFile) + ctx.CheckbuildFile(a.aapt.exportPackage) + ctx.CheckbuildFile(a.aapt.aaptSrcJar) + if a.useResourceProcessorBusyBox() { + ctx.CheckbuildFile(a.aapt.rJar) + } // apps manifests are handled by aapt, don't let Module see them a.properties.Manifest = nil @@ -627,7 +746,22 @@ func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, a.proguardOptionsFile) - a.Module.compile(ctx, a.aaptSrcJar) + var extraSrcJars android.Paths + var extraCombinedJars android.Paths + var extraClasspathJars android.Paths + if a.useResourceProcessorBusyBox() { + // 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, extraSrcJars, extraClasspathJars, extraCombinedJars) a.aarFile = android.PathForModuleOut(ctx, ctx.ModuleName()+".aar") var res android.Paths @@ -729,12 +863,15 @@ type AARImport struct { properties AARImportProperties - classpathFile android.WritablePath - proguardFlags android.WritablePath - exportPackage android.WritablePath - extraAaptPackagesFile android.WritablePath - manifest android.WritablePath - assetsPackage android.WritablePath + classpathFile android.WritablePath + proguardFlags android.WritablePath + exportPackage android.WritablePath + transitiveAaptResourcePackages android.Paths + extraAaptPackagesFile android.WritablePath + manifest android.WritablePath + assetsPackage android.WritablePath + rTxt android.WritablePath + rJar android.WritablePath resourcesNodesDepSet *android.DepSet[*resourcesNode] manifestsDepSet *android.DepSet[android.Path] @@ -903,12 +1040,13 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.classpathFile = extractedAARDir.Join(ctx, "classes-combined.jar") a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt") a.manifest = extractedAARDir.Join(ctx, "AndroidManifest.xml") + aarRTxt := extractedAARDir.Join(ctx, "R.txt") a.assetsPackage = android.PathForModuleOut(ctx, "assets.zip") ctx.Build(pctx, android.BuildParams{ Rule: unzipAAR, Input: a.aarPath, - Outputs: android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest, a.assetsPackage}, + Outputs: android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest, a.assetsPackage, aarRTxt}, Description: "unzip AAR", Args: map[string]string{ "outDir": extractedAARDir.String(), @@ -928,14 +1066,14 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { // 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") + a.rTxt = 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", } @@ -948,25 +1086,35 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { _ = staticRRODirsDepSet staticDeps := transitiveAarDeps(staticResourcesNodesDepSet.ToList()) - // AAPT2 overlays are in lowest to highest priority order, reverse the topological order - // of transitiveStaticLibs. - transitiveStaticLibs := android.ReversePaths(staticDeps.resPackages()) - linkDeps = append(linkDeps, sharedLibs...) - linkDeps = append(linkDeps, transitiveStaticLibs...) + linkDeps = append(linkDeps, staticDeps.resPackages()...) linkFlags = append(linkFlags, libFlags...) - overlayRes := append(android.Paths{flata}, transitiveStaticLibs...) + overlayRes := android.Paths{flata} + + // 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, srcJar, proguardOptionsFile, rTxt, a.extraAaptPackagesFile, + aapt2Link(ctx, a.exportPackage, srcJar, proguardOptionsFile, a.rTxt, a.extraAaptPackagesFile, linkFlags, linkDeps, nil, overlayRes, transitiveAssets, nil) + a.rJar = android.PathForModuleOut(ctx, "busybox/R.jar") + resourceProcessorBusyBoxGenerateBinaryR(ctx, a.rTxt, a.manifest, a.rJar, nil, true) + 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() @@ -981,6 +1129,8 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { _ = staticManifestsDepSet a.manifestsDepSet = manifestDepSetBuilder.Build() + a.transitiveAaptResourcePackages = staticDeps.resPackages() + a.collectTransitiveHeaderJars(ctx) ctx.SetProvider(JavaInfoProvider, JavaInfo{ HeaderJars: android.PathsIfNonNil(a.classpathFile), @@ -988,6 +1138,7 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { TransitiveStaticLibsHeaderJars: a.transitiveStaticLibsHeaderJars, ImplementationAndResourcesJars: android.PathsIfNonNil(a.classpathFile), ImplementationJars: android.PathsIfNonNil(a.classpathFile), + // TransitiveAconfigFiles: // TODO(b/289117800): LOCAL_ACONFIG_FILES for prebuilts }) if proptools.Bool(a.properties.Extract_jni) { |