blob: deb2cb530e38c82645a24c916e634728a520af8f [file] [log] [blame]
// 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.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.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(ruleContext, result, 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(ruleContext, stubManifest, 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.
*
* <p>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)
throws InterruptedException, RuleErrorException {
return fromExplicitManifest(
ruleContext, renameManifestIfNeeded(ruleContext, getManifestFromAttributes(ruleContext)));
}
static Artifact renameManifestIfNeeded(RuleContext ruleContext, 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 =
ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_SYMLINKED_MANIFEST);
SymlinkAction symlinkAction =
new SymlinkAction(
ruleContext.getActionOwner(),
manifest,
manifestSymlink,
"Renaming Android manifest for " + ruleContext.getLabel());
ruleContext.registerAction(symlinkAction);
return manifestSymlink;
}
}
public static ApplicationManifest fromExplicitManifest(RuleContext ruleContext, Artifact manifest)
throws RuleErrorException {
return new ApplicationManifest(
ruleContext, manifest, AndroidAaptVersion.chooseTargetAaptVersion(ruleContext));
}
/**
* Generates an empty manifest for a rule that does not directly specify resources.
*
* <p><strong>Note:</strong> 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(RuleContext ruleContext, String manifestPackage) {
Artifact generatedManifest =
ruleContext.getUniqueDirectoryArtifact(
ruleContext.getRule().getName() + "_generated",
PathFragment.create("AndroidManifest.xml"),
ruleContext.getBinOrGenfilesDirectory());
String contents =
Joiner.on("\n")
.join(
"<?xml version=\"1.0\" encoding=\"utf-8\"?>",
"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"",
" package=\"" + manifestPackage + "\">",
" <application>",
" </application>",
"</manifest>");
ruleContext
.getAnalysisEnvironment()
.registerAction(
FileWriteAction.create(
ruleContext, generatedManifest, contents, /*makeExecutable=*/ false));
return generatedManifest;
}
/** Gets a map of manifest values from this rule's 'manifest_values' attribute */
static ImmutableMap<String, String> getManifestValues(RuleContext context) {
return getManifestValues(
context,
context.attributes().isAttributeValueExplicitlySpecified("manifest_values")
? context.attributes().get("manifest_values", Type.STRING_DICT)
: null);
}
/** Gets and expands an expanded map of manifest values from some raw map of manifest values. */
static ImmutableMap<String, String> getManifestValues(
RuleContext ruleContext, @Nullable Map<String, String> rawMap) {
Map<String, String> manifestValues = new TreeMap<>();
if (rawMap != null) {
manifestValues.putAll(rawMap);
}
for (String variable : manifestValues.keySet()) {
manifestValues.put(
variable,
ruleContext.getExpander().expand("manifest_values", manifestValues.get(variable)));
}
return ImmutableMap.copyOf(manifestValues);
}
public ImmutableMap<String, String> getManifestValues() {
return manifestValues;
}
private final Artifact manifest;
private final ImmutableMap<String, String> manifestValues;
private final AndroidAaptVersion targetAaptVersion;
private ApplicationManifest(
RuleContext ruleContext, Artifact manifest, AndroidAaptVersion targetAaptVersion) {
this.manifest = manifest;
this.manifestValues = getManifestValues(ruleContext);
this.targetAaptVersion = targetAaptVersion;
}
public ApplicationManifest mergeWith(RuleContext ruleContext, ResourceDependencies resourceDeps) {
return maybeMergeWith(
ruleContext,
manifest,
resourceDeps,
manifestValues,
useLegacyMerging(ruleContext),
AndroidCommon.getJavaPackage(ruleContext))
.map(merged -> new ApplicationManifest(ruleContext, merged, targetAaptVersion))
.orElse(this);
}
static Optional<Artifact> maybeMergeWith(
RuleContext ruleContext,
Artifact primaryManifest,
ResourceDependencies resourceDeps,
Map<String, String> manifestValues,
boolean useLegacyMerging,
String customPackage) {
Map<Artifact, Label> mergeeManifests = getMergeeManifests(resourceDeps.getResourceContainers());
if (useLegacyMerging) {
if (!mergeeManifests.isEmpty()) {
Artifact outputManifest =
ruleContext.getUniqueDirectoryArtifact(
ruleContext.getRule().getName() + "_merged",
"AndroidManifest.xml",
ruleContext.getBinOrGenfilesDirectory());
AndroidManifestMergeHelper.createMergeManifestAction(
ruleContext,
primaryManifest,
mergeeManifests.keySet(),
ImmutableList.of("all"),
outputManifest);
return Optional.of(outputManifest);
}
} else {
if (!mergeeManifests.isEmpty() || !manifestValues.isEmpty()) {
Artifact outputManifest =
ruleContext.getUniqueDirectoryArtifact(
ruleContext.getRule().getName() + "_merged",
"AndroidManifest.xml",
ruleContext.getBinOrGenfilesDirectory());
Artifact mergeLog =
ruleContext.getUniqueDirectoryArtifact(
ruleContext.getRule().getName() + "_merged",
"manifest_merger_log.txt",
ruleContext.getBinOrGenfilesDirectory());
new ManifestMergerActionBuilder(ruleContext)
.setManifest(primaryManifest)
.setMergeeManifests(mergeeManifests)
.setLibrary(false)
.setManifestValues(manifestValues)
.setCustomPackage(customPackage)
.setManifestOutput(outputManifest)
.setLogOut(mergeLog)
.build(ruleContext);
return Optional.of(outputManifest);
}
}
return Optional.empty();
}
/** Checks if the legacy manifest merger should be used, based on a rule attribute */
static boolean useLegacyMerging(RuleContext ruleContext) {
return ruleContext.isLegalFragment(AndroidConfiguration.class)
&& ruleContext.getRule().isAttrDefined("manifest_merger", STRING)
&& useLegacyMerging(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(RuleContext ruleContext, @Nullable String mergerString) {
AndroidManifestMerger merger = AndroidManifestMerger.fromString(mergerString);
if (merger == null) {
merger = ruleContext.getFragment(AndroidConfiguration.class).getManifestMerger();
}
if (merger == AndroidManifestMerger.LEGACY) {
ruleContext.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<Artifact, Label> getMergeeManifests(
Iterable<ValidatedAndroidData> transitiveData) {
ImmutableSortedMap.Builder<Artifact, Label> 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(RuleContext ruleContext, String customPackage) {
Optional<Artifact> stamped = maybeSetManifestPackage(ruleContext, manifest, customPackage);
if (!stamped.isPresent()) {
return this;
}
return new ApplicationManifest(ruleContext, stamped.get(), targetAaptVersion);
}
static Optional<Artifact> maybeSetManifestPackage(
RuleContext ruleContext, Artifact manifest, String customPackage) {
if (isNullOrEmpty(customPackage)) {
return Optional.empty();
}
Artifact outputManifest =
ruleContext.getUniqueDirectoryArtifact(
ruleContext.getRule().getName() + "_renamed",
"AndroidManifest.xml",
ruleContext.getBinOrGenfilesDirectory());
new ManifestMergerActionBuilder(ruleContext)
.setManifest(manifest)
.setLibrary(true)
.setCustomPackage(customPackage)
.setManifestOutput(outputManifest)
.build(ruleContext);
return Optional.of(outputManifest);
}
public ResourceApk packTestWithDataAndResources(
RuleContext ruleContext,
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(ruleContext)
.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(resourceContainer);
ResourceContainer finalContainer =
new RClassGeneratorActionBuilder(ruleContext)
.targetAaptVersion(AndroidAaptVersion.chooseTargetAaptVersion(ruleContext))
.withDependencies(resourceDeps)
.setClassJarOut(
ruleContext.getImplicitOutputArtifact(
AndroidRuleClasses.ANDROID_RESOURCES_CLASS_JAR))
.build(processed);
return ResourceApk.of(finalContainer, resourceDeps, proguardCfg, mainDexProguardCfg);
}
/** Packages up the manifest with resource and assets from the LocalResourceContainer. */
public ResourceApk packAarWithDataAndResources(
RuleContext ruleContext,
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(ruleContext)
.setAssets(assets)
.setResources(resources)
.setOutput(symbols)
.buildAndUpdate(ruleContext, resourceContainer);
ResourceContainer merged =
new AndroidResourceMergingActionBuilder(ruleContext)
.setJavaPackage(resourceContainer.getJavaPackage())
.withDependencies(resourceDeps)
.setMergedResourcesOut(mergedResources)
.setManifestOut(manifestOut)
.setClassJarOut(rJavaClassJar)
.setThrowOnResourceConflict(
ruleContext
.getConfiguration()
.getFragment(AndroidConfiguration.class)
.throwOnResourceConflict())
.build(ruleContext, resourceContainer);
ResourceContainer processed =
new AndroidResourceValidatorActionBuilder(ruleContext)
.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(ruleContext, merged);
return ResourceApk.of(processed, resourceDeps);
}
/* Creates an incremental apk from assets and data. */
public ResourceApk packIncrementalBinaryWithDataAndResources(
RuleContext ruleContext,
Artifact resourceApk,
ResourceDependencies resourceDeps,
List<String> 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.fromRuleContext(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(ruleContext)
.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(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,
Artifact resourceApk,
ResourceDependencies resourceDeps,
@Nullable Artifact rTxt,
ResourceFilterFactory resourceFilterFactory,
List<String> 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(ruleContext)
.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(resourceContainer);
ResourceContainer finalContainer =
new RClassGeneratorActionBuilder(ruleContext)
.targetAaptVersion(AndroidAaptVersion.chooseTargetAaptVersion(ruleContext))
.withDependencies(resourceDeps)
.setClassJarOut(
ruleContext.getImplicitOutputArtifact(
AndroidRuleClasses.ANDROID_RESOURCES_CLASS_JAR))
.build(processed);
return ResourceApk.of(finalContainer, resourceDeps, proguardCfg, mainDexProguardCfg);
}
public ResourceApk packLibraryWithDataAndResources(
RuleContext ruleContext,
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(ruleContext)
.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(ruleContext, resourceContainer);
ResourceContainer merged =
new AndroidResourceMergingActionBuilder(ruleContext)
.setJavaPackage(resourceContainer.getJavaPackage())
.withDependencies(resourceDeps)
.setThrowOnResourceConflict(androidConfiguration.throwOnResourceConflict())
.setUseCompiledMerge(skipParsingAction)
.setDataBindingInfoZip(dataBindingInfoZip)
.setMergedResourcesOut(mergedResources)
.setManifestOut(manifestOut)
.setClassJarOut(rJavaClassJar)
.setDataBindingInfoZip(dataBindingInfoZip)
.build(ruleContext, resourceContainer);
ResourceContainer processed =
new AndroidResourceValidatorActionBuilder(ruleContext)
.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(ruleContext, merged);
return ResourceApk.of(processed, resourceDeps);
}
public Artifact getManifest() {
return manifest;
}
}