// Copyright 2015 The Bazel Authors. 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 com.google.devtools.build.lib.rules.android; import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.devtools.build.lib.syntax.Type.STRING; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedMap; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.actions.ActionConstructionContext; import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; import com.google.devtools.build.lib.analysis.actions.FileWriteAction; import com.google.devtools.build.lib.analysis.actions.SpawnAction; import com.google.devtools.build.lib.analysis.actions.SymlinkAction; import com.google.devtools.build.lib.analysis.config.CompilationMode; import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; import com.google.devtools.build.lib.packages.RuleErrorConsumer; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidManifestMerger; import com.google.devtools.build.lib.rules.android.ResourceContainer.Builder.JavaPackageSource; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.TreeMap; import javax.annotation.Nullable; /** Represents a AndroidManifest, that may have been merged from dependencies. */ public final class ApplicationManifest { public ApplicationManifest createSplitManifest( RuleContext ruleContext, String splitName, boolean hasCode) { Artifact result = createSplitManifest(ruleContext, manifest, splitName, hasCode); return new ApplicationManifest(result, manifestValues, targetAaptVersion); } static Artifact createSplitManifest( RuleContext ruleContext, Artifact manifest, String splitName, boolean hasCode) { // aapt insists that manifests be called AndroidManifest.xml, even though they have to be // explicitly designated as manifests on the command line Artifact result = AndroidBinary.getDxArtifact(ruleContext, "split_" + splitName + "/AndroidManifest.xml"); SpawnAction.Builder builder = new SpawnAction.Builder() .setExecutable( ruleContext.getExecutablePrerequisite("$build_split_manifest", Mode.HOST)) .setProgressMessage("Creating manifest for split %s", splitName) .setMnemonic("AndroidBuildSplitManifest") .addInput(manifest) .addOutput(result); CustomCommandLine.Builder commandLine = CustomCommandLine.builder() .addExecPath("--main_manifest", manifest) .addExecPath("--split_manifest", result) .add("--split", splitName); if (hasCode) { commandLine.add("--hascode"); } else { commandLine.add("--nohascode"); } String overridePackage = getManifestValues(ruleContext).get("applicationId"); if (overridePackage != null) { commandLine.add("--override_package", overridePackage); } builder.addCommandLine(commandLine.build()); ruleContext.registerAction(builder.build(ruleContext)); return result; } public ApplicationManifest addMobileInstallStubApplication(RuleContext ruleContext) throws InterruptedException { Artifact stubManifest = addMobileInstallStubApplication(ruleContext, manifest); return new ApplicationManifest(stubManifest, manifestValues, targetAaptVersion); } static Artifact addMobileInstallStubApplication(RuleContext ruleContext, Artifact manifest) throws InterruptedException { Artifact stubManifest = ruleContext.getImplicitOutputArtifact( AndroidRuleClasses.MOBILE_INSTALL_STUB_APPLICATION_MANIFEST); Artifact stubData = ruleContext.getImplicitOutputArtifact( AndroidRuleClasses.MOBILE_INSTALL_STUB_APPLICATION_DATA); SpawnAction.Builder builder = new SpawnAction.Builder() .setExecutable(ruleContext.getExecutablePrerequisite("$stubify_manifest", Mode.HOST)) .setProgressMessage("Injecting mobile install stub application") .setMnemonic("InjectMobileInstallStubApplication") .addInput(manifest) .addOutput(stubManifest) .addOutput(stubData); CustomCommandLine.Builder commandLine = CustomCommandLine.builder() .add("--mode=mobile_install") .addExecPath("--input_manifest", manifest) .addExecPath("--output_manifest", stubManifest) .addExecPath("--output_datafile", stubData); String overridePackage = getManifestValues(ruleContext).get("applicationId"); if (overridePackage != null) { commandLine.add("--override_package", overridePackage); } builder.addCommandLine(commandLine.build()); ruleContext.registerAction(builder.build(ruleContext)); return stubManifest; } public static Artifact getManifestFromAttributes(RuleContext ruleContext) { return ruleContext.getPrerequisiteArtifact("manifest", Mode.TARGET); } /** * Gets the manifest specified in the "manifest" attribute, renaming it if needed. * *

Unlike {@link AndroidSemantics#getManifestForRule(RuleContext)}, this method will not * perform AndroidSemantics-specific manifest processing. This method will do the same work * regardless of the AndroidSemantics implementation being used; that method may do different work * depending on the implementation. */ public static ApplicationManifest renamedFromRule( RuleContext ruleContext, AndroidDataContext dataContext) throws InterruptedException, RuleErrorException { return fromExplicitManifest( ruleContext, renameManifestIfNeeded(dataContext, getManifestFromAttributes(ruleContext))); } static Artifact renameManifestIfNeeded(AndroidDataContext dataContext, Artifact manifest) throws InterruptedException { if (manifest.getFilename().equals("AndroidManifest.xml")) { return manifest; } else { /* * If the manifest file is not named AndroidManifest.xml, we create a symlink named * AndroidManifest.xml to it. aapt requires the manifest to be named as such. */ Artifact manifestSymlink = dataContext.createOutputArtifact(AndroidRuleClasses.ANDROID_SYMLINKED_MANIFEST); dataContext.registerAction( new SymlinkAction( dataContext.getActionConstructionContext().getActionOwner(), manifest, manifestSymlink, "Renaming Android manifest for " + dataContext.getLabel())); return manifestSymlink; } } public static ApplicationManifest fromExplicitManifest(RuleContext ruleContext, Artifact manifest) throws RuleErrorException { return new ApplicationManifest( manifest, getManifestValues(ruleContext), AndroidAaptVersion.chooseTargetAaptVersion(ruleContext)); } /** * Generates an empty manifest for a rule that does not directly specify resources. * *

Note: This generated manifest can then be used as the primary manifest when * merging with dependencies. * * @return the generated ApplicationManifest */ public static ApplicationManifest generatedManifest(RuleContext ruleContext) throws RuleErrorException { return fromExplicitManifest( ruleContext, generateManifest(ruleContext, AndroidCommon.getJavaPackage(ruleContext))); } /** * Creates an action to generate an empty manifest file with a specific package name. * * @return an artifact for the generated manifest */ public static Artifact generateManifest( ActionConstructionContext context, String manifestPackage) { Artifact generatedManifest = context.getUniqueDirectoryArtifact("_generated", "AndroidManifest.xml"); String contents = Joiner.on("\n") .join( "", "", " ", " ", ""); context.registerAction( FileWriteAction.create(context, generatedManifest, contents, /*makeExecutable=*/ false)); return generatedManifest; } /** Gets a map of manifest values from this rule's 'manifest_values' attribute */ public static ImmutableMap getManifestValues(RuleContext context) { Map manifestValues = new TreeMap<>(); if (context.attributes().isAttributeValueExplicitlySpecified("manifest_values")) { manifestValues.putAll(context.attributes().get("manifest_values", Type.STRING_DICT)); } for (String variable : manifestValues.keySet()) { manifestValues.put( variable, context.getExpander().expand("manifest_values", manifestValues.get(variable))); } return ImmutableMap.copyOf(manifestValues); } public ImmutableMap getManifestValues() { return manifestValues; } private final Artifact manifest; private final ImmutableMap manifestValues; private final AndroidAaptVersion targetAaptVersion; private ApplicationManifest( Artifact manifest, ImmutableMap manifestValues, AndroidAaptVersion targetAaptVersion) { this.manifest = manifest; this.manifestValues = manifestValues; this.targetAaptVersion = targetAaptVersion; } public ApplicationManifest mergeWith( RuleContext ruleContext, AndroidDataContext dataContext, AndroidSemantics androidSemantics, ResourceDependencies resourceDeps) { return maybeMergeWith( dataContext, androidSemantics, manifest, resourceDeps, manifestValues, useLegacyMerging(ruleContext), AndroidCommon.getJavaPackage(ruleContext)) .map(merged -> new ApplicationManifest(merged, manifestValues, targetAaptVersion)) .orElse(this); } static Optional maybeMergeWith( AndroidDataContext dataContext, AndroidSemantics androidSemantics, Artifact primaryManifest, ResourceDependencies resourceDeps, Map manifestValues, boolean useLegacyMerging, String customPackage) { Map mergeeManifests = getMergeeManifests(resourceDeps.getResourceContainers()); if (useLegacyMerging) { return androidSemantics.maybeDoLegacyManifestMerging( mergeeManifests, dataContext, primaryManifest); } else { if (!mergeeManifests.isEmpty() || !manifestValues.isEmpty()) { Artifact outputManifest = dataContext.getUniqueDirectoryArtifact("_merged", "AndroidManifest.xml"); Artifact mergeLog = dataContext.getUniqueDirectoryArtifact("_merged", "manifest_merger_log.txt"); new ManifestMergerActionBuilder() .setManifest(primaryManifest) .setMergeeManifests(mergeeManifests) .setLibrary(false) .setManifestValues(manifestValues) .setCustomPackage(customPackage) .setManifestOutput(outputManifest) .setLogOut(mergeLog) .build(dataContext); return Optional.of(outputManifest); } } return Optional.empty(); } /** Checks if the legacy manifest merger should be used, based on a rule attribute */ public static boolean useLegacyMerging(RuleContext ruleContext) { return ruleContext.isLegalFragment(AndroidConfiguration.class) && ruleContext.getRule().isAttrDefined("manifest_merger", STRING) && useLegacyMerging( ruleContext, AndroidCommon.getAndroidConfig(ruleContext), ruleContext.attributes().get("manifest_merger", STRING)); } /** * Checks if the legacy manifest merger should be used, based on an optional string specifying the * merger to use. */ public static boolean useLegacyMerging( RuleErrorConsumer errorConsumer, AndroidConfiguration androidConfig, @Nullable String mergerString) { AndroidManifestMerger merger = AndroidManifestMerger.fromString(mergerString); if (merger == null) { merger = androidConfig.getManifestMerger(); } if (merger == AndroidManifestMerger.LEGACY) { errorConsumer.ruleWarning( "manifest_merger 'legacy' is deprecated. Please update to 'android'.\n" + "See https://developer.android.com/studio/build/manifest-merge.html for more " + "information about the manifest merger."); } return merger == AndroidManifestMerger.LEGACY; } private static Map getMergeeManifests( Iterable transitiveData) { ImmutableSortedMap.Builder builder = ImmutableSortedMap.orderedBy(Artifact.EXEC_PATH_COMPARATOR); for (ValidatedAndroidData d : transitiveData) { if (d.isManifestExported()) { builder.put(d.getManifest(), d.getLabel()); } } return builder.build(); } public ApplicationManifest renamePackage(AndroidDataContext dataContext, String customPackage) { Optional stamped = maybeSetManifestPackage(dataContext, manifest, customPackage); if (!stamped.isPresent()) { return this; } return new ApplicationManifest(stamped.get(), manifestValues, targetAaptVersion); } static Optional maybeSetManifestPackage( AndroidDataContext dataContext, Artifact manifest, String customPackage) { if (isNullOrEmpty(customPackage)) { return Optional.empty(); } Artifact outputManifest = dataContext.getUniqueDirectoryArtifact("_renamed", "AndroidManifest.xml"); new ManifestMergerActionBuilder() .setManifest(manifest) .setLibrary(true) .setCustomPackage(customPackage) .setManifestOutput(outputManifest) .build(dataContext); return Optional.of(outputManifest); } public ResourceApk packTestWithDataAndResources( RuleContext ruleContext, AndroidDataContext dataContext, Artifact resourceApk, ResourceDependencies resourceDeps, @Nullable Artifact rTxt, boolean incremental, Artifact proguardCfg, Artifact mainDexProguardCfg, @Nullable String packageUnderTest, boolean hasLocalResourceFiles) throws InterruptedException, RuleErrorException { ResourceContainer resourceContainer = ResourceContainer.builderFromRule(ruleContext) .setAndroidAssets(AndroidAssets.from(ruleContext)) .setAndroidResources(AndroidResources.from(ruleContext, "local_resource_files")) .setManifest(getManifest()) .setApk(resourceApk) .setRTxt(rTxt) .build(); AndroidResourcesProcessorBuilder builder = new AndroidResourcesProcessorBuilder() .setLibrary(false) .setApkOut(resourceContainer.getApk()) .setUncompressedExtensions(ImmutableList.of()) .setCrunchPng(true) .setJavaPackage(resourceContainer.getJavaPackage()) .setDebug(ruleContext.getConfiguration().getCompilationMode() != CompilationMode.OPT) .withResourceDependencies(resourceDeps) .setProguardOut(proguardCfg) .setMainDexProguardOut(mainDexProguardCfg) .setApplicationId(manifestValues.get("applicationId")) .setVersionCode(manifestValues.get("versionCode")) .setVersionName(manifestValues.get("versionName")) .setThrowOnResourceConflict( ruleContext .getConfiguration() .getFragment(AndroidConfiguration.class) .throwOnResourceConflict()) .setPackageUnderTest(packageUnderTest) .setIsTestWithResources(hasLocalResourceFiles); if (!incremental) { builder .targetAaptVersion(targetAaptVersion) .setRTxtOut(resourceContainer.getRTxt()) .setSymbols(resourceContainer.getSymbols()) .setSourceJarOut(resourceContainer.getJavaSourceJar()); } ResourceContainer processed = builder.build(dataContext, resourceContainer); ResourceContainer finalContainer = new RClassGeneratorActionBuilder() .targetAaptVersion(AndroidAaptVersion.chooseTargetAaptVersion(ruleContext)) .withDependencies(resourceDeps) .setClassJarOut( ruleContext.getImplicitOutputArtifact( AndroidRuleClasses.ANDROID_RESOURCES_CLASS_JAR)) .build(dataContext, processed); return ResourceApk.of(finalContainer, resourceDeps, proguardCfg, mainDexProguardCfg); } /** Packages up the manifest with resource and assets from the LocalResourceContainer. */ public ResourceApk packAarWithDataAndResources( RuleContext ruleContext, AndroidDataContext dataContext, AndroidAssets assets, AndroidResources resources, ResourceDependencies resourceDeps, Artifact rTxt, Artifact symbols, Artifact manifestOut, Artifact mergedResources) throws InterruptedException { ResourceContainer resourceContainer = ResourceContainer.builderFromRule(ruleContext) .setRTxt(rTxt) .setJavaPackageFrom(JavaPackageSource.MANIFEST) .setManifestExported(true) .setManifest(getManifest()) .build(); // android_library should only build the APK one way (!incremental). Artifact rJavaClassJar = ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_CLASS_JAR); resourceContainer = new AndroidResourceParsingActionBuilder() .setAssets(assets) .setResources(resources) .setOutput(symbols) .buildAndUpdate(dataContext, resourceContainer); ResourceContainer merged = new AndroidResourceMergingActionBuilder() .setJavaPackage(resourceContainer.getJavaPackage()) .withDependencies(resourceDeps) .setMergedResourcesOut(mergedResources) .setManifestOut(manifestOut) .setClassJarOut(rJavaClassJar) .setThrowOnResourceConflict( ruleContext .getConfiguration() .getFragment(AndroidConfiguration.class) .throwOnResourceConflict()) .build(dataContext, resourceContainer); ResourceContainer processed = new AndroidResourceValidatorActionBuilder() .setJavaPackage(merged.getJavaPackage()) .setDebug(ruleContext.getConfiguration().getCompilationMode() != CompilationMode.OPT) .setMergedResources(mergedResources) .setRTxtOut(merged.getRTxt()) .setSourceJarOut(merged.getJavaSourceJar()) .setApkOut(resourceContainer.getApk()) // aapt2 related artifacts. Will be generated if the targetAaptVersion is AAPT2. .withDependencies(resourceDeps) .setCompiledSymbols(merged.getCompiledSymbols()) .setAapt2RTxtOut(merged.getAapt2RTxt()) .setAapt2SourceJarOut(merged.getAapt2JavaSourceJar()) .setStaticLibraryOut(merged.getStaticLibrary()) .build(dataContext, merged); return ResourceApk.of(processed, resourceDeps); } /* Creates an incremental apk from assets and data. */ public ResourceApk packIncrementalBinaryWithDataAndResources( RuleContext ruleContext, AndroidDataContext dataContext, Artifact resourceApk, ResourceDependencies resourceDeps, List uncompressedExtensions, boolean crunchPng, Artifact proguardCfg) throws InterruptedException, RuleErrorException { AndroidResources resources = AndroidResources.from(ruleContext, "resource_files"); // Filter the resources during analysis to prevent processing of dependencies on unwanted // resources during execution. ResourceFilterFactory resourceFilterFactory = ResourceFilterFactory.fromRuleContextAndAttrs(ruleContext); ResourceFilter resourceFilter = resourceFilterFactory.getResourceFilter(ruleContext, resourceDeps, resources); resources = resources.filterLocalResources(ruleContext, resourceFilter); resourceDeps = resourceDeps.filter(ruleContext, resourceFilter); // Now that the LocalResourceContainer has been filtered, we can build a filtered resource // container from it. ResourceContainer resourceContainer = ResourceContainer.builderFromRule(ruleContext) .setApk(resourceApk) .setManifest(getManifest()) .setAndroidAssets(AndroidAssets.from(ruleContext)) .setAndroidResources(resources) .build(); ResourceContainer processed = new AndroidResourcesProcessorBuilder() .setLibrary(false) .setApkOut(resourceContainer.getApk()) .setResourceFilterFactory(resourceFilterFactory) .setUncompressedExtensions(uncompressedExtensions) .setCrunchPng(crunchPng) .setJavaPackage(resourceContainer.getJavaPackage()) .setDebug(ruleContext.getConfiguration().getCompilationMode() != CompilationMode.OPT) .withResourceDependencies(resourceDeps) .setProguardOut(proguardCfg) .setApplicationId(manifestValues.get("applicationId")) .setVersionCode(manifestValues.get("versionCode")) .setVersionName(manifestValues.get("versionName")) .setThrowOnResourceConflict( ruleContext .getConfiguration() .getFragment(AndroidConfiguration.class) .throwOnResourceConflict()) .setPackageUnderTest(null) .build(dataContext, resourceContainer); // Intentionally skip building an R class JAR - incremental binaries handle this separately. return ResourceApk.of(processed, resourceDeps, proguardCfg, null); } /** Packages up the manifest with resource and assets from the rule and dependent resources. */ // TODO(bazel-team): this method calls for some refactoring, 15+ params including some nullables. public ResourceApk packBinaryWithDataAndResources( RuleContext ruleContext, AndroidDataContext dataContext, Artifact resourceApk, ResourceDependencies resourceDeps, @Nullable Artifact rTxt, ResourceFilterFactory resourceFilterFactory, List uncompressedExtensions, boolean crunchPng, Artifact proguardCfg, @Nullable Artifact mainDexProguardCfg, boolean conditionalKeepRules, Artifact manifestOut, Artifact mergedResources, @Nullable Artifact dataBindingInfoZip, @Nullable Artifact featureOf, @Nullable Artifact featureAfter) throws InterruptedException, RuleErrorException { AndroidResources resources = AndroidResources.from(ruleContext, "resource_files"); ResourceFilter resourceFilter = resourceFilterFactory.getResourceFilter(ruleContext, resourceDeps, resources); resources = resources.filterLocalResources(ruleContext, resourceFilter); resourceDeps = resourceDeps.filter(ruleContext, resourceFilter); // Now that the LocalResourceContainer has been filtered, we can build a filtered resource // container from it. ResourceContainer resourceContainer = ResourceContainer.builderFromRule(ruleContext) .setAndroidAssets(AndroidAssets.from(ruleContext)) .setAndroidResources(resources) .setManifest(getManifest()) .setRTxt(rTxt) .setApk(resourceApk) .build(); AndroidConfiguration androidConfiguration = ruleContext.getConfiguration().getFragment(AndroidConfiguration.class); boolean skipParsingAction = targetAaptVersion == AndroidAaptVersion.AAPT2 && androidConfiguration.skipParsingAction(); if (conditionalKeepRules && targetAaptVersion != AndroidAaptVersion.AAPT2) { throw ruleContext.throwWithRuleError( "resource cycle shrinking can only be enabled for builds with aapt2"); } ResourceContainer processed = new AndroidResourcesProcessorBuilder() .setLibrary(false) .setApkOut(resourceContainer.getApk()) .setResourceFilterFactory(resourceFilterFactory) .setUncompressedExtensions(uncompressedExtensions) .setCrunchPng(crunchPng) .setJavaPackage(resourceContainer.getJavaPackage()) .setDebug(ruleContext.getConfiguration().getCompilationMode() != CompilationMode.OPT) .setManifestOut(manifestOut) .setMergedResourcesOut(mergedResources) .withResourceDependencies(resourceDeps) .setProguardOut(proguardCfg) .setMainDexProguardOut(mainDexProguardCfg) .conditionalKeepRules(conditionalKeepRules) .setDataBindingInfoZip(dataBindingInfoZip) .setApplicationId(manifestValues.get("applicationId")) .setVersionCode(manifestValues.get("versionCode")) .setVersionName(manifestValues.get("versionName")) .setFeatureOf(featureOf) .setFeatureAfter(featureAfter) .setThrowOnResourceConflict(androidConfiguration.throwOnResourceConflict()) .setUseCompiledResourcesForMerge(skipParsingAction) .targetAaptVersion(targetAaptVersion) .setRTxtOut(resourceContainer.getRTxt()) .setSymbols(resourceContainer.getSymbols()) .setSourceJarOut(resourceContainer.getJavaSourceJar()) .build(dataContext, resourceContainer); ResourceContainer finalContainer = new RClassGeneratorActionBuilder() .targetAaptVersion(AndroidAaptVersion.chooseTargetAaptVersion(ruleContext)) .withDependencies(resourceDeps) .setClassJarOut( ruleContext.getImplicitOutputArtifact( AndroidRuleClasses.ANDROID_RESOURCES_CLASS_JAR)) .build(dataContext, processed); return ResourceApk.of(finalContainer, resourceDeps, proguardCfg, mainDexProguardCfg); } public ResourceApk packLibraryWithDataAndResources( RuleContext ruleContext, AndroidDataContext dataContext, ResourceDependencies resourceDeps, Artifact rTxt, Artifact symbols, Artifact manifestOut, Artifact mergedResources, @Nullable Artifact dataBindingInfoZip) throws InterruptedException, RuleErrorException { AndroidResources resources = AndroidResources.from(ruleContext, "resource_files"); AndroidAssets assets = AndroidAssets.from(ruleContext); ResourceContainer.Builder builder = ResourceContainer.builderFromRule(ruleContext) .setManifest(getManifest()) .setSymbols(symbols) .setRTxt(rTxt) // Request an APK so it can be inherited when a library is used in a binary's // resources attr. // TODO(b/30307842): Remove this once it is no longer needed for resources migration. .setApk(ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_LIBRARY_APK)); if (targetAaptVersion == AndroidAaptVersion.AAPT2) { builder .setAapt2JavaSourceJar( ruleContext.getImplicitOutputArtifact( AndroidRuleClasses.ANDROID_RESOURCES_AAPT2_SOURCE_JAR)) .setAapt2RTxt( ruleContext.getImplicitOutputArtifact( AndroidRuleClasses.ANDROID_RESOURCES_AAPT2_R_TXT)) .setCompiledSymbols( ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_COMPILED_SYMBOLS)) .setStaticLibrary( ruleContext.getImplicitOutputArtifact( AndroidRuleClasses.ANDROID_RESOURCES_AAPT2_LIBRARY_APK)); } ResourceContainer resourceContainer = builder.build(); Artifact rJavaClassJar = ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_CLASS_JAR); AndroidConfiguration androidConfiguration = ruleContext.getConfiguration().getFragment(AndroidConfiguration.class); boolean skipParsingAction = targetAaptVersion == AndroidAaptVersion.AAPT2 && androidConfiguration.skipParsingAction(); AndroidResourceParsingActionBuilder parsingBuilder = new AndroidResourceParsingActionBuilder() .setAssets(assets) .setResources(resources) .setOutput(resourceContainer.getSymbols()) .setCompiledSymbolsOutput(resourceContainer.getCompiledSymbols()); if (dataBindingInfoZip != null && resourceContainer.getCompiledSymbols() != null) { PathFragment unusedInfo = dataBindingInfoZip.getRootRelativePath(); // TODO(corysmith): Centralize the data binding processing and zipping into a single // action. Data binding processing needs to be triggered here as well as the merger to // avoid aapt2 from throwing an error during compilation. parsingBuilder .setDataBindingInfoZip( ruleContext.getDerivedArtifact( unusedInfo.replaceName(unusedInfo.getBaseName() + "_unused.zip"), dataBindingInfoZip.getRoot())) .setManifest(resourceContainer.getManifest()) .setJavaPackage(resourceContainer.getJavaPackage()); } resourceContainer = parsingBuilder.buildAndUpdate(dataContext, resourceContainer); ResourceContainer merged = new AndroidResourceMergingActionBuilder() .setJavaPackage(resourceContainer.getJavaPackage()) .withDependencies(resourceDeps) .setThrowOnResourceConflict(androidConfiguration.throwOnResourceConflict()) .setUseCompiledMerge(skipParsingAction) .setDataBindingInfoZip(dataBindingInfoZip) .setMergedResourcesOut(mergedResources) .setManifestOut(manifestOut) .setClassJarOut(rJavaClassJar) .setDataBindingInfoZip(dataBindingInfoZip) .build(dataContext, resourceContainer); ResourceContainer processed = new AndroidResourceValidatorActionBuilder() .setJavaPackage(merged.getJavaPackage()) .setDebug(ruleContext.getConfiguration().getCompilationMode() != CompilationMode.OPT) .setMergedResources(mergedResources) .setRTxtOut(merged.getRTxt()) .setSourceJarOut(merged.getJavaSourceJar()) .setApkOut(resourceContainer.getApk()) // aapt2 related artifacts. Will be generated if the targetAaptVersion is AAPT2. .withDependencies(resourceDeps) .setCompiledSymbols(merged.getCompiledSymbols()) .setAapt2RTxtOut(merged.getAapt2RTxt()) .setAapt2SourceJarOut(merged.getAapt2JavaSourceJar()) .setStaticLibraryOut(merged.getStaticLibrary()) .build(dataContext, merged); return ResourceApk.of(processed, resourceDeps); } public Artifact getManifest() { return manifest; } }