| // 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.common.collect.Iterables; |
| import com.google.common.collect.Lists; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.analysis.FileProvider; |
| import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; |
| 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.config.CompilationMode; |
| import com.google.devtools.build.lib.cmdline.Label; |
| 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.rules.android.ResourceContainer.ResourceType; |
| import com.google.devtools.build.lib.syntax.Type; |
| import com.google.devtools.build.lib.util.Preconditions; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.TreeMap; |
| import javax.annotation.Nullable; |
| |
| /** Represents a AndroidManifest, that may have been merged from dependencies. */ |
| public final class ApplicationManifest { |
| public static ApplicationManifest fromResourcesRule(RuleContext ruleContext) { |
| final AndroidResourcesProvider resources = AndroidCommon.getAndroidResources(ruleContext); |
| if (resources == null) { |
| ruleContext.attributeError("manifest", "a resources or manifest attribute is mandatory."); |
| return null; |
| } |
| return new ApplicationManifest( |
| ruleContext, Iterables.getOnlyElement(resources.getDirectAndroidResources()).getManifest()); |
| } |
| |
| public ApplicationManifest createSplitManifest( |
| RuleContext ruleContext, 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 " + splitName) |
| .setMnemonic("AndroidBuildSplitManifest") |
| .addArgument("--main_manifest") |
| .addInputArgument(manifest) |
| .addArgument("--split_manifest") |
| .addOutputArgument(result) |
| .addArgument("--split") |
| .addArgument(splitName) |
| .addArgument(hasCode ? "--hascode" : "--nohascode"); |
| |
| String overridePackage = manifestValues.get("applicationId"); |
| if (overridePackage != null) { |
| builder |
| .addArgument("--override_package") |
| .addArgument(overridePackage); |
| } |
| |
| ruleContext.registerAction(builder.build(ruleContext)); |
| return new ApplicationManifest(ruleContext, result); |
| } |
| |
| public ApplicationManifest addMobileInstallStubApplication(RuleContext ruleContext) |
| throws InterruptedException { |
| |
| Artifact stubManifest = ruleContext.getImplicitOutputArtifact( |
| AndroidRuleClasses.MOBILE_INSTALL_STUB_APPLICATION_MANIFEST); |
| |
| SpawnAction.Builder builder = new SpawnAction.Builder() |
| .setExecutable(ruleContext.getExecutablePrerequisite("$stubify_manifest", Mode.HOST)) |
| .setProgressMessage("Injecting mobile install stub application") |
| .setMnemonic("InjectMobileInstallStubApplication") |
| .addArgument("--mode=mobile_install") |
| .addArgument("--input_manifest") |
| .addInputArgument(manifest) |
| .addArgument("--output_manifest") |
| .addOutputArgument(stubManifest) |
| .addArgument("--output_datafile") |
| .addOutputArgument(ruleContext.getImplicitOutputArtifact( |
| AndroidRuleClasses.MOBILE_INSTALL_STUB_APPLICATION_DATA)); |
| |
| String overridePackage = manifestValues.get("applicationId"); |
| if (overridePackage != null) { |
| builder.addArgument("--override_package"); |
| builder.addArgument(overridePackage); |
| } |
| |
| ruleContext.registerAction(builder.build(ruleContext)); |
| |
| return new ApplicationManifest(ruleContext, stubManifest); |
| } |
| |
| public ApplicationManifest addInstantRunStubApplication(RuleContext ruleContext) |
| throws InterruptedException { |
| |
| Artifact stubManifest = ruleContext.getImplicitOutputArtifact( |
| AndroidRuleClasses.INSTANT_RUN_STUB_APPLICATION_MANIFEST); |
| |
| SpawnAction.Builder builder = new SpawnAction.Builder() |
| .setExecutable(ruleContext.getExecutablePrerequisite("$stubify_manifest", Mode.HOST)) |
| .setProgressMessage("Injecting instant run stub application") |
| .setMnemonic("InjectInstantRunStubApplication") |
| .addArgument("--mode=instant_run") |
| .addArgument("--input_manifest") |
| .addInputArgument(manifest) |
| .addArgument("--output_manifest") |
| .addOutputArgument(stubManifest); |
| |
| ruleContext.registerAction(builder.build(ruleContext)); |
| |
| return new ApplicationManifest(ruleContext, stubManifest); |
| } |
| |
| public static ApplicationManifest fromRule(RuleContext ruleContext) { |
| return new ApplicationManifest( |
| ruleContext, ruleContext.getPrerequisiteArtifact("manifest", Mode.TARGET)); |
| } |
| |
| public static ApplicationManifest fromExplicitManifest( |
| RuleContext ruleContext, Artifact manifest) { |
| return new ApplicationManifest(ruleContext, manifest); |
| } |
| |
| /** |
| * 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) { |
| Artifact generatedManifest = ruleContext.getUniqueDirectoryArtifact( |
| ruleContext.getRule().getName() + "_generated", new PathFragment("AndroidManifest.xml"), |
| ruleContext.getBinOrGenfilesDirectory()); |
| |
| String manifestPackage = AndroidCommon.getJavaPackage(ruleContext); |
| 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 new ApplicationManifest(ruleContext, generatedManifest); |
| } |
| |
| private static ImmutableMap<String, String> getManifestValues(RuleContext context) { |
| Map<String, String> manifestValues = new TreeMap<>(); |
| // applicationId is set from manifest_values or android_resources.rename_manifest_package |
| // with descending priority. |
| AndroidResourcesProvider resourcesProvider = AndroidCommon.getAndroidResources(context); |
| if (resourcesProvider != null) { |
| ResourceContainer resourceContainer = Iterables.getOnlyElement( |
| resourcesProvider.getDirectAndroidResources()); |
| if (resourceContainer.getRenameManifestPackage() != null) { |
| manifestValues.put("applicationId", resourceContainer.getRenameManifestPackage()); |
| } |
| } |
| 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.expandMakeVariables("manifest_values", manifestValues.get(variable))); |
| } |
| return ImmutableMap.copyOf(manifestValues); |
| } |
| |
| private final Artifact manifest; |
| private final ImmutableMap<String, String> manifestValues; |
| |
| private ApplicationManifest(RuleContext ruleContext, Artifact manifest) { |
| this.manifest = manifest; |
| this.manifestValues = getManifestValues(ruleContext); |
| } |
| |
| public ApplicationManifest mergeWith(RuleContext ruleContext, ResourceDependencies resourceDeps) { |
| Map<Artifact, Label> mergeeManifests = getMergeeManifests(resourceDeps.getResources()); |
| |
| boolean legacy = true; |
| if (ruleContext.isLegalFragment(AndroidConfiguration.class) |
| && ruleContext.getRule().isAttrDefined("manifest_merger", STRING)) { |
| AndroidManifestMerger merger = AndroidManifestMerger.fromString( |
| ruleContext.attributes().get("manifest_merger", STRING)); |
| if (merger == null) { |
| merger = ruleContext.getFragment(AndroidConfiguration.class).getManifestMerger(); |
| } |
| legacy = merger == AndroidManifestMerger.LEGACY; |
| } |
| |
| if (legacy) { |
| if (!mergeeManifests.isEmpty()) { |
| Artifact outputManifest = ruleContext.getUniqueDirectoryArtifact( |
| ruleContext.getRule().getName() + "_merged", "AndroidManifest.xml", |
| ruleContext.getBinOrGenfilesDirectory()); |
| AndroidManifestMergeHelper.createMergeManifestAction(ruleContext, getManifest(), |
| mergeeManifests.keySet(), ImmutableList.of("all"), outputManifest); |
| return new ApplicationManifest(ruleContext, 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(getManifest()) |
| .setMergeeManifests(mergeeManifests) |
| .setLibrary(false) |
| .setManifestValues(manifestValues) |
| .setCustomPackage(AndroidCommon.getJavaPackage(ruleContext)) |
| .setManifestOutput(outputManifest) |
| .setLogOut(mergeLog) |
| .build(ruleContext); |
| return new ApplicationManifest(ruleContext, outputManifest); |
| } |
| } |
| return this; |
| } |
| |
| private static Map<Artifact, Label> getMergeeManifests( |
| Iterable<ResourceContainer> resourceContainers) { |
| ImmutableSortedMap.Builder<Artifact, Label> builder = |
| ImmutableSortedMap.orderedBy(Artifact.EXEC_PATH_COMPARATOR); |
| for (ResourceContainer r : resourceContainers) { |
| if (r.isManifestExported()) { |
| builder.put(r.getManifest(), r.getLabel()); |
| } |
| } |
| return builder.build(); |
| } |
| |
| public ApplicationManifest renamePackage(RuleContext ruleContext, String customPackage) { |
| if (isNullOrEmpty(customPackage)) { |
| return this; |
| } |
| Artifact outputManifest = ruleContext.getUniqueDirectoryArtifact( |
| ruleContext.getRule().getName() + "_renamed", "AndroidManifest.xml", |
| ruleContext.getBinOrGenfilesDirectory()); |
| new ManifestMergerActionBuilder(ruleContext) |
| .setManifest(getManifest()) |
| .setLibrary(true) |
| .setCustomPackage(customPackage) |
| .setManifestOutput(outputManifest) |
| .build(ruleContext); |
| return new ApplicationManifest(ruleContext, outputManifest); |
| } |
| |
| /** Packages up the manifest with assets from the rule and dependent resources. |
| * @throws InterruptedException */ |
| public ResourceApk packWithAssets( |
| Artifact resourceApk, |
| RuleContext ruleContext, |
| ResourceDependencies resourceDeps, |
| Artifact rTxt, |
| boolean incremental, |
| Artifact proguardCfg) throws InterruptedException { |
| LocalResourceContainer data = new LocalResourceContainer.Builder(ruleContext) |
| .withAssets( |
| AndroidCommon.getAssetDir(ruleContext), |
| ruleContext.getPrerequisitesIf( |
| // TODO(bazel-team): Remove the ResourceType construct. |
| ResourceType.ASSETS.getAttribute(), |
| Mode.TARGET, |
| FileProvider.class)).build(); |
| |
| return createApk( |
| ruleContext, |
| false, /* isLibrary */ |
| resourceDeps, |
| ResourceConfigurationFilter.empty(ruleContext), |
| ImmutableList.<String>of(), /* uncompressedExtensions */ |
| true, /* crunchPng */ |
| ImmutableList.<String>of(), /* densities */ |
| incremental, |
| ResourceContainer.builderFromRule(ruleContext) |
| .setAssetsAndResourcesFrom(data) |
| .setManifest(getManifest()) |
| .setRTxt(rTxt) |
| .setApk(resourceApk) |
| .build(), |
| data, |
| proguardCfg, |
| null, /* Artifact mainDexProguardCfg */ |
| null, /* Artifact manifestOut */ |
| null /* Artifact mergedResources */, |
| null /* Artifact dataBindingInfoZip */); |
| } |
| |
| /** Packages up the manifest with resource and assets from the LocalResourceContainer. */ |
| public ResourceApk packWithDataAndResources( |
| RuleContext ruleContext, |
| LocalResourceContainer data, |
| ResourceDependencies resourceDeps, |
| Artifact rTxt, |
| Artifact symbols, |
| Artifact manifestOut, |
| Artifact mergedResources, |
| boolean alwaysExportManifest) throws InterruptedException { |
| if (ruleContext.hasErrors()) { |
| return null; |
| } |
| ResourceContainer.Builder resourceContainer = |
| ResourceContainer.builderFromRule(ruleContext) |
| .setAssetsAndResourcesFrom(data) |
| .setManifest(getManifest()) |
| .setRTxt(rTxt) |
| .setSymbols(symbols) |
| .setJavaPackageFrom(JavaPackageSource.MANIFEST); |
| if (alwaysExportManifest) { |
| resourceContainer.setManifestExported(true); |
| } |
| return createApk( |
| ruleContext, |
| true, /* isLibrary */ |
| resourceDeps, |
| ResourceConfigurationFilter.empty(ruleContext), |
| ImmutableList.<String>of(), /* List<String> uncompressedExtensions */ |
| false, /* crunchPng */ |
| ImmutableList.<String>of(), /* List<String> densities */ |
| false, /* incremental */ |
| resourceContainer.build(), |
| data, |
| null, /* Artifact proguardCfg */ |
| null, /* Artifact mainDexProguardCfg */ |
| manifestOut, |
| mergedResources, |
| null /* Artifact dataBindingInfoZip */); |
| } |
| |
| /** Packages up the manifest with resource and assets from the rule and dependent resources. */ |
| public ResourceApk packWithDataAndResources( |
| @Nullable Artifact resourceApk, |
| RuleContext ruleContext, |
| boolean isLibrary, |
| ResourceDependencies resourceDeps, |
| Artifact rTxt, |
| Artifact symbols, |
| ResourceConfigurationFilter configurationFilters, |
| List<String> uncompressedExtensions, |
| boolean crunchPng, |
| List<String> densities, |
| boolean incremental, |
| Artifact proguardCfg, |
| @Nullable Artifact mainDexProguardCfg, |
| Artifact manifestOut, |
| Artifact mergedResources, |
| Artifact dataBindingInfoZip) throws InterruptedException { |
| LocalResourceContainer data = new LocalResourceContainer.Builder(ruleContext) |
| .withAssets( |
| AndroidCommon.getAssetDir(ruleContext), |
| ruleContext.getPrerequisitesIf( |
| // TODO(bazel-team): Remove the ResourceType construct. |
| ResourceType.ASSETS.getAttribute(), |
| Mode.TARGET, |
| FileProvider.class)) |
| .withResources( |
| ruleContext.getPrerequisites( |
| "resource_files", |
| Mode.TARGET, |
| FileProvider.class)).build(); |
| if (ruleContext.hasErrors()) { |
| return null; |
| } |
| return createApk( |
| ruleContext, |
| isLibrary, |
| resourceDeps, |
| configurationFilters, |
| uncompressedExtensions, |
| crunchPng, |
| densities, |
| incremental, |
| ResourceContainer.builderFromRule(ruleContext) |
| .setAssetsAndResourcesFrom(data) |
| .setManifest(getManifest()) |
| .setRTxt(rTxt) |
| .setSymbols(symbols) |
| .setApk(resourceApk) |
| .build(), |
| data, |
| proguardCfg, |
| mainDexProguardCfg, |
| manifestOut, |
| mergedResources, dataBindingInfoZip); |
| } |
| |
| private ResourceApk createApk( |
| RuleContext ruleContext, |
| boolean isLibrary, |
| ResourceDependencies resourceDeps, |
| ResourceConfigurationFilter configurationFilters, |
| List<String> uncompressedExtensions, |
| boolean crunchPng, |
| List<String> densities, |
| boolean incremental, |
| ResourceContainer maybeInlinedResourceContainer, |
| LocalResourceContainer data, |
| Artifact proguardCfg, |
| @Nullable Artifact mainDexProguardCfg, |
| Artifact manifestOut, |
| Artifact mergedResources, |
| Artifact dataBindingInfoZip) throws InterruptedException { |
| ResourceContainer resourceContainer = checkForInlinedResources( |
| maybeInlinedResourceContainer, |
| resourceDeps.getResources(), // TODO(bazel-team): Figure out if we really need to check |
| // the ENTIRE transitive closure, or just the direct dependencies. Given that each rule with |
| // resources would check for inline resources, we can rely on the previous rule to have |
| // checked its dependencies. |
| ruleContext); |
| if (ruleContext.hasErrors()) { |
| return null; |
| } |
| |
| // Filter the resources during analysis to prevent processing of and dependencies on unwanted |
| // resources during execution. |
| resourceContainer = resourceContainer.filter(configurationFilters); |
| resourceDeps = resourceDeps.filter(configurationFilters); |
| |
| ResourceContainer processed; |
| if (isLibrary && AndroidCommon.getAndroidConfig(ruleContext).useParallelResourceProcessing()) { |
| // android_library should only build the APK one way (!incremental). |
| Preconditions.checkArgument(!incremental); |
| Artifact rJavaClassJar = ruleContext.getImplicitOutputArtifact( |
| AndroidRuleClasses.ANDROID_RESOURCES_CLASS_JAR); |
| |
| if (resourceContainer.getSymbols() != null) { |
| new AndroidResourceParsingActionBuilder(ruleContext) |
| .withPrimary(resourceContainer) |
| .setParse(data) |
| .setOutput(resourceContainer.getSymbols()) |
| .build(ruleContext); |
| } |
| |
| AndroidResourceMergingActionBuilder resourcesMergerBuilder = |
| new AndroidResourceMergingActionBuilder(ruleContext) |
| .setJavaPackage(resourceContainer.getJavaPackage()) |
| .withPrimary(resourceContainer) |
| .withDependencies(resourceDeps) |
| .setMergedResourcesOut(mergedResources) |
| .setManifestOut(manifestOut) |
| .setClassJarOut(rJavaClassJar) |
| .setDataBindingInfoZip(dataBindingInfoZip); |
| ResourceContainer merged = resourcesMergerBuilder.build(ruleContext); |
| |
| AndroidResourceValidatorActionBuilder validatorBuilder = |
| new AndroidResourceValidatorActionBuilder(ruleContext) |
| .setJavaPackage(merged.getJavaPackage()) |
| .setDebug( |
| ruleContext.getConfiguration().getCompilationMode() != CompilationMode.OPT) |
| .setMergedResources(mergedResources) |
| .withPrimary(merged) |
| .setSourceJarOut(merged.getJavaSourceJar()) |
| .setRTxtOut(merged.getRTxt()); |
| processed = validatorBuilder.build(ruleContext); |
| } else { |
| AndroidResourcesProcessorBuilder builder = |
| new AndroidResourcesProcessorBuilder(ruleContext) |
| .setLibrary(isLibrary) |
| .setApkOut(resourceContainer.getApk()) |
| .setConfigurationFilters(configurationFilters) |
| .setUncompressedExtensions(uncompressedExtensions) |
| .setCrunchPng(crunchPng) |
| .setJavaPackage(resourceContainer.getJavaPackage()) |
| .setDebug(ruleContext.getConfiguration().getCompilationMode() != CompilationMode.OPT) |
| .setManifestOut(manifestOut) |
| .setMergedResourcesOut(mergedResources) |
| .withPrimary(resourceContainer) |
| .withDependencies(resourceDeps) |
| .setDensities(densities) |
| .setProguardOut(proguardCfg) |
| .setMainDexProguardOut(mainDexProguardCfg) |
| .setDataBindingInfoZip(dataBindingInfoZip) |
| .setApplicationId(manifestValues.get("applicationId")) |
| .setVersionCode(manifestValues.get("versionCode")) |
| .setVersionName(manifestValues.get("versionName")); |
| if (!incremental) { |
| builder |
| .setRTxtOut(resourceContainer.getRTxt()) |
| .setSymbols(resourceContainer.getSymbols()) |
| .setSourceJarOut(resourceContainer.getJavaSourceJar()); |
| } |
| processed = builder.build(ruleContext); |
| } |
| |
| return new ResourceApk( |
| resourceContainer.getApk(), processed.getJavaSourceJar(), processed.getJavaClassJar(), |
| resourceDeps, processed, processed.getManifest(), |
| proguardCfg, mainDexProguardCfg, false); |
| } |
| |
| private static ResourceContainer checkForInlinedResources(ResourceContainer resourceContainer, |
| Iterable<ResourceContainer> resourceContainers, RuleContext ruleContext) { |
| // Dealing with Android library projects |
| if (Iterables.size(resourceContainers) > 1) { |
| if (resourceContainer.getConstantsInlined() |
| && !resourceContainer.getArtifacts(ResourceType.RESOURCES).isEmpty()) { |
| ruleContext.ruleError("This android binary depends on an android " |
| + "library project, so the resources '" |
| + AndroidCommon.getAndroidResources(ruleContext).getLabel() |
| + "' should have the attribute inline_constants set to 0"); |
| return null; |
| } |
| } |
| return resourceContainer; |
| } |
| |
| /** Uses the resource apk from the resources attribute, as opposed to recompiling. */ |
| public ResourceApk useCurrentResources( |
| RuleContext ruleContext, Artifact proguardCfg, @Nullable Artifact mainDexProguardCfg) { |
| ResourceContainer resourceContainer = Iterables.getOnlyElement( |
| AndroidCommon.getAndroidResources(ruleContext).getDirectAndroidResources()); |
| |
| new AndroidAaptActionHelper( |
| ruleContext, |
| resourceContainer.getManifest(), |
| Lists.newArrayList(resourceContainer)) |
| .createGenerateProguardAction(proguardCfg, mainDexProguardCfg); |
| |
| return new ResourceApk( |
| resourceContainer.getApk(), |
| null /* javaSrcJar */, |
| null /* javaClassJar */, |
| ResourceDependencies.empty(), |
| resourceContainer, |
| manifest, |
| proguardCfg, |
| mainDexProguardCfg, |
| false); |
| } |
| |
| /** |
| * Packages up the manifest with resources, and generates the R.java. |
| * @throws InterruptedException |
| * |
| * @deprecated in favor of {@link ApplicationManifest#packWithDataAndResources}. |
| */ |
| @Deprecated |
| public ResourceApk packWithResources( |
| Artifact resourceApk, |
| RuleContext ruleContext, |
| ResourceDependencies resourceDeps, |
| boolean createSource, |
| Artifact proguardCfg, |
| @Nullable Artifact mainDexProguardCfg) throws InterruptedException { |
| |
| TransitiveInfoCollection resourcesPrerequisite = |
| ruleContext.getPrerequisite("resources", Mode.TARGET); |
| ResourceContainer resourceContainer = Iterables.getOnlyElement( |
| resourcesPrerequisite.getProvider(AndroidResourcesProvider.class) |
| .getDirectAndroidResources()); |
| // It's ugly, but flattening now is more performant given the rest of the checks. |
| List<ResourceContainer> resourceContainers = |
| ImmutableList.<ResourceContainer>builder() |
| //.add(resourceContainer) |
| .addAll(resourceDeps.getResources()).build(); |
| |
| // Dealing with Android library projects |
| if (Iterables.size(resourceDeps.getResources()) > 1) { |
| if (resourceContainer.getConstantsInlined() |
| && !resourceContainer.getArtifacts(ResourceType.RESOURCES).isEmpty()) { |
| ruleContext.ruleError("This android_binary depends on an android_library, so the" |
| + " resources '" + AndroidCommon.getAndroidResources(ruleContext).getLabel() |
| + "' should have the attribute inline_constants set to 0"); |
| return null; |
| } |
| } |
| |
| // This binary depends on a library project, so we need to regenerate the |
| // resources. The resulting sources and apk will combine all the resources |
| // contained in the transitive closure of the binary. |
| AndroidAaptActionHelper aaptActionHelper = new AndroidAaptActionHelper(ruleContext, |
| getManifest(), Lists.newArrayList(resourceContainers)); |
| |
| String resourceConfigurationFilters = ResourceConfigurationFilter.extractFilters(ruleContext); |
| List<String> uncompressedExtensions = |
| ruleContext.getTokenizedStringListAttr("nocompress_extensions"); |
| |
| ImmutableList.Builder<String> additionalAaptOpts = ImmutableList.<String>builder(); |
| |
| for (String extension : uncompressedExtensions) { |
| additionalAaptOpts.add("-0").add(extension); |
| } |
| if (!resourceConfigurationFilters.isEmpty()) { |
| additionalAaptOpts.add("-c").add(resourceConfigurationFilters); |
| } |
| |
| Artifact javaSourcesJar = null; |
| |
| if (createSource) { |
| javaSourcesJar = |
| ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_JAVA_SOURCE_JAR); |
| aaptActionHelper.createGenerateResourceSymbolsAction( |
| javaSourcesJar, null, resourceContainer.getJavaPackage(), true); |
| } |
| |
| List<String> densities = ruleContext.getTokenizedStringListAttr("densities"); |
| aaptActionHelper.createGenerateApkAction(resourceApk, |
| resourceContainer.getRenameManifestPackage(), additionalAaptOpts.build(), densities); |
| |
| ResourceContainer updatedResources = resourceContainer.toBuilder() |
| .setLabel(ruleContext.getLabel()) |
| .setApk(resourceApk) |
| .setManifest(getManifest()) |
| .setJavaSourceJar(javaSourcesJar) |
| .setJavaClassJar(null) |
| .setSymbols(null) |
| .build(); |
| |
| aaptActionHelper.createGenerateProguardAction(proguardCfg, mainDexProguardCfg); |
| |
| return new ResourceApk(resourceApk, |
| updatedResources.getJavaSourceJar(), |
| updatedResources.getJavaClassJar(), |
| resourceDeps, updatedResources, manifest, proguardCfg, mainDexProguardCfg, true); |
| } |
| |
| public Artifact getManifest() { |
| return manifest; |
| } |
| } |