diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/android')
9 files changed, 257 insertions, 350 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java index 3c723466f0..5d95ce9a50 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java @@ -17,6 +17,7 @@ package com.google.devtools.build.lib.rules.android; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.devtools.build.lib.syntax.Type.STRING; import static java.nio.charset.StandardCharsets.ISO_8859_1; import com.google.auto.value.AutoValue; @@ -183,15 +184,19 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { AndroidResources.validateRuleContext(ruleContext); final AndroidDataContext dataContext = androidSemantics.makeContextForNative(ruleContext); + Map<String, String> manifestValues = StampedAndroidManifest.getManifestValues(ruleContext); StampedAndroidManifest manifest = AndroidManifest.fromAttributes(ruleContext, dataContext, androidSemantics) .mergeWithDeps( dataContext, androidSemantics, + ruleContext, resourceDeps, - ApplicationManifest.getManifestValues(ruleContext), - ApplicationManifest.useLegacyMerging(ruleContext)); + manifestValues, + ruleContext.getRule().isAttrDefined("manifest_merger", STRING) + ? ruleContext.attributes().get("manifest_merger", STRING) + : null); AndroidAaptVersion aaptVersion = AndroidAaptVersion.chooseTargetAaptVersion(ruleContext); final ResourceApk resourceApk = @@ -201,7 +206,7 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { manifest, /* conditionalKeepRules = */ shouldShrinkResourceCycles( dataContext.getAndroidConfig(), ruleContext, shrinkResources), - ApplicationManifest.getManifestValues(ruleContext), + manifestValues, aaptVersion, AndroidResources.from(ruleContext, "resource_files"), AndroidAssets.from(ruleContext), diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinaryMobileInstall.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinaryMobileInstall.java index 29529bbfc0..0669f0bad3 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinaryMobileInstall.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinaryMobileInstall.java @@ -62,7 +62,7 @@ public final class AndroidBinaryMobileInstall { final ResourceApk incrementalResourceApk; final ResourceApk splitResourceApk; - Map<String, String> manifestValues = ApplicationManifest.getManifestValues(ruleContext); + Map<String, String> manifestValues = StampedAndroidManifest.getManifestValues(ruleContext); incrementalResourceApk = ProcessedAndroidData.processIncrementalBinaryDataFrom( diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java index 98ec4dafde..f7657ed8b2 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java @@ -221,7 +221,7 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment } } - // TODO(corysmith): Move to ApplicationManifest when no longer needed as a public function. + // TODO(corysmith): Move to an appropriate place when no longer needed as a public function. @Nullable public static AndroidAaptVersion chooseTargetAaptVersion(RuleContext ruleContext) throws RuleErrorException { diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java index b484479169..8ee5a16afc 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java @@ -36,6 +36,7 @@ import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.collect.nestedset.Order; import com.google.devtools.build.lib.packages.BuildType; +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.DataBinding.DataBindingContext; import com.google.devtools.build.lib.rules.java.ClasspathConfiguredFragment; @@ -97,13 +98,14 @@ public abstract class AndroidLocalTestBase implements RuleConfiguredTargetFactor buildResourceApk( dataContext, androidSemantics, + ruleContext, DataBinding.contextFrom(ruleContext, dataContext.getAndroidConfig()), AndroidManifest.fromAttributes(ruleContext, dataContext), AndroidResources.from(ruleContext, "resource_files"), AndroidAssets.from(ruleContext), ResourceDependencies.fromRuleDeps(ruleContext, /* neverlink = */ false), AssetDependencies.fromRuleDeps(ruleContext, /* neverlink = */ false), - ApplicationManifest.getManifestValues(ruleContext), + StampedAndroidManifest.getManifestValues(ruleContext), AndroidAaptVersion.chooseTargetAaptVersion(ruleContext)); attributesBuilder.addRuntimeClassPathEntry(resourceApk.getResourceJavaClassJar()); @@ -492,6 +494,7 @@ public abstract class AndroidLocalTestBase implements RuleConfiguredTargetFactor static ResourceApk buildResourceApk( AndroidDataContext dataContext, AndroidSemantics androidSemantics, + RuleErrorConsumer errorConsumer, DataBindingContext dataBindingContext, AndroidManifest manifest, AndroidResources resources, @@ -506,9 +509,10 @@ public abstract class AndroidLocalTestBase implements RuleConfiguredTargetFactor manifest.mergeWithDeps( dataContext, androidSemantics, + errorConsumer, resourceDeps, manifestValues, - /* useLegacyMerger = */ false); + /* manifestMerger = */ null); return ProcessedAndroidData.processLocalTestDataFrom( dataContext, diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidManifest.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidManifest.java index 160ccb3980..6a552b8c7b 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidManifest.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidManifest.java @@ -13,13 +13,19 @@ // limitations under the License. package com.google.devtools.build.lib.rules.android; +import static com.google.common.base.Strings.isNullOrEmpty; + +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.SymlinkAction; +import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; 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.AndroidManifestMerger; import com.google.devtools.build.lib.rules.java.JavaUtil; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.vfs.PathFragment; @@ -74,7 +80,7 @@ public class AndroidManifest { Artifact rawManifest = null; if (AndroidResources.definesAndroidResources(ruleContext.attributes())) { AndroidResources.validateRuleContext(ruleContext); - rawManifest = ApplicationManifest.getManifestFromAttributes(ruleContext); + rawManifest = ruleContext.getPrerequisiteArtifact("manifest", Mode.TARGET); } return from( @@ -110,9 +116,16 @@ public class AndroidManifest { /** * Inner method to create an AndroidManifest. * - * <p>AndroidSemantics-specific processing will be used if a non-null AndroidSemantics is passed. + * @param rawManifest If non-null, the returned object will wrap this manifest. Otherwise, the + * returned object will wrap a generated dummy manifest. + * @param androidSemantics If non-null, will invoke + * AndroidSemantics#renameManifest(AndroidDataContext, AndroidManifest)} to do + * platform-specific processing on the manifest. + * @param pkg If non-null, this Android package will be used for the manifest, and the manifest + * will be stamped with it when the {@link #stamp(AndroidDataContext)} method is called. + * Otherwise, the default package, based on the current target's Bazel package, will be used. */ - static AndroidManifest from( + public static AndroidManifest from( AndroidDataContext dataContext, RuleErrorConsumer errorConsumer, @Nullable Artifact rawManifest, @@ -129,21 +142,40 @@ public class AndroidManifest { if (rawManifest == null) { // Generate a dummy manifest return StampedAndroidManifest.createEmpty( - dataContext.getActionConstructionContext(), pkg, /* exported = */ false); + dataContext.getActionConstructionContext(), pkg, exportsManifest); } - Artifact renamedManifest; + AndroidManifest raw = new AndroidManifest(rawManifest, pkg, exportsManifest); + if (androidSemantics != null) { - renamedManifest = androidSemantics.renameManifest(dataContext, rawManifest); - } else { - renamedManifest = ApplicationManifest.renameManifestIfNeeded(dataContext, rawManifest); + return androidSemantics.renameManifest(dataContext, raw); } + return raw.renameManifestIfNeeded(dataContext); + } - return new AndroidManifest(renamedManifest, pkg, exportsManifest); + AndroidManifest renameManifestIfNeeded(AndroidDataContext dataContext) + throws InterruptedException { + if (manifest.getFilename().equals("AndroidManifest.xml")) { + return this; + } 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 updateManifest(manifestSymlink); + } } - AndroidManifest(AndroidManifest other, Artifact manifest) { - this(manifest, other.pkg, other.exported); + public AndroidManifest updateManifest(Artifact manifest) { + return new AndroidManifest(manifest, pkg, exported); } /** @@ -158,25 +190,18 @@ public class AndroidManifest { /** If needed, stamps the manifest with the correct Java package */ public StampedAndroidManifest stamp(AndroidDataContext dataContext) { - return new StampedAndroidManifest( - ApplicationManifest.maybeSetManifestPackage(dataContext, manifest, pkg).orElse(manifest), - pkg, - exported); - } + Artifact outputManifest = getManifest(); + if (!isNullOrEmpty(pkg)) { + outputManifest = dataContext.getUniqueDirectoryArtifact("_renamed", "AndroidManifest.xml"); + new ManifestMergerActionBuilder() + .setManifest(manifest) + .setLibrary(true) + .setCustomPackage(pkg) + .setManifestOutput(outputManifest) + .build(dataContext); + } - /** - * Stamps the manifest with values from the "manifest_values" attributes. - * - * <p>If no manifest values are specified, the manifest will remain unstamped. - */ - public StampedAndroidManifest stampWithManifestValues( - RuleContext ruleContext, AndroidDataContext dataContext, AndroidSemantics androidSemantics) { - return mergeWithDeps( - dataContext, - androidSemantics, - ResourceDependencies.empty(), - ApplicationManifest.getManifestValues(ruleContext), - ApplicationManifest.useLegacyMerging(ruleContext)); + return new StampedAndroidManifest(outputManifest, pkg, exported); } /** @@ -186,27 +211,79 @@ public class AndroidManifest { * * <p>If there is no merging to be done and no manifest values are specified, the manifest will * remain unstamped. + * + * @param manifestMerger if not null, a string dictating which manifest merger to use */ public StampedAndroidManifest mergeWithDeps( AndroidDataContext dataContext, AndroidSemantics androidSemantics, + RuleErrorConsumer errorConsumer, ResourceDependencies resourceDeps, Map<String, String> manifestValues, - boolean useLegacyMerger) { - Artifact newManifest = - ApplicationManifest.maybeMergeWith( - dataContext, - androidSemantics, - manifest, - resourceDeps, - manifestValues, - useLegacyMerger, - pkg) - .orElse(manifest); + @Nullable String manifestMerger) { + Map<Artifact, Label> mergeeManifests = getMergeeManifests(resourceDeps.getResourceContainers()); + + Artifact newManifest; + if (useLegacyMerging(errorConsumer, dataContext.getAndroidConfig(), manifestMerger)) { + newManifest = + androidSemantics + .maybeDoLegacyManifestMerging(mergeeManifests, dataContext, manifest) + .orElse(manifest); + + } else if (!mergeeManifests.isEmpty() || !manifestValues.isEmpty()) { + newManifest = dataContext.getUniqueDirectoryArtifact("_merged", "AndroidManifest.xml"); + + new ManifestMergerActionBuilder() + .setManifest(manifest) + .setMergeeManifests(mergeeManifests) + .setLibrary(false) + .setManifestValues(manifestValues) + .setCustomPackage(pkg) + .setManifestOutput(newManifest) + .setLogOut(dataContext.getUniqueDirectoryArtifact("_merged", "manifest_merger_log.txt")) + .build(dataContext); + + } else { + newManifest = manifest; + } return new StampedAndroidManifest(newManifest, pkg, exported); } + /** + * Checks if the legacy manifest merger should be used, based on an optional string specifying the + * merger to use. + */ + private 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<Artifact, Label> getMergeeManifests( + Iterable<ValidatedAndroidResources> transitiveData) { + ImmutableSortedMap.Builder<Artifact, Label> builder = + ImmutableSortedMap.orderedBy(Artifact.EXEC_PATH_COMPARATOR); + for (ValidatedAndroidResources d : transitiveData) { + if (d.isManifestExported()) { + builder.put(d.getManifest(), d.getLabel()); + } + } + return builder.build(); + } + public Artifact getManifest() { return manifest; } @@ -283,7 +360,7 @@ public class AndroidManifest { @Override public boolean equals(Object object) { - if (object == null || getClass() != object.getClass()) { + if (!(object instanceof AndroidManifest)) { return false; } diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSemantics.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSemantics.java index 9e06e3750f..b7d2b7d4fb 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSemantics.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSemantics.java @@ -36,9 +36,9 @@ import java.util.Optional; */ public interface AndroidSemantics { - default Artifact renameManifest(AndroidDataContext dataContext, Artifact rawManifest) - throws InterruptedException { - return ApplicationManifest.renameManifestIfNeeded(dataContext, rawManifest); + default AndroidManifest renameManifest( + AndroidDataContext dataContext, AndroidManifest rawManifest) throws InterruptedException { + return rawManifest.renameManifestIfNeeded(dataContext); } default Optional<Artifact> maybeDoLegacyManifestMerging( diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkData.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkData.java index e23d77156e..023b95b89c 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkData.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkData.java @@ -109,23 +109,16 @@ public abstract class AndroidSkylarkData String pkg = fromNoneable(customPackage, String.class); try (SkylarkErrorReporter errorReporter = SkylarkErrorReporter.from(ctx.getActionConstructionContext(), location, env)) { - if (pkg == null) { - pkg = - AndroidManifest.getDefaultPackage( - env.getCallerLabel(), ctx.getActionConstructionContext(), errorReporter); - } - } - - Artifact primaryManifest = fromNoneable(manifest, Artifact.class); - if (primaryManifest == null) { - return StampedAndroidManifest.createEmpty(ctx.getActionConstructionContext(), pkg, exported) + return AndroidManifest.from( + ctx, + errorReporter, + fromNoneable(manifest, Artifact.class), + getAndroidSemantics(), + pkg, + exported) + .stamp(ctx) .toProvider(); } - - // If needed, rename the manifest to "AndroidManifest.xml", which aapt expects. - Artifact renamedManifest = getAndroidSemantics().renameManifest(ctx, primaryManifest); - - return new AndroidManifest(renamedManifest, pkg, exported).stamp(ctx).toProvider(); } @Override @@ -419,6 +412,7 @@ public abstract class AndroidSkylarkData AndroidLocalTestBase.buildResourceApk( ctx, getAndroidSemantics(), + errorReporter, DataBinding.asDisabledDataBindingContext(), rawManifest, AndroidResources.from(errorReporter, getFileProviders(resources), "resource_files"), @@ -548,10 +542,10 @@ public abstract class AndroidSkylarkData rawManifest.mergeWithDeps( ctx, getAndroidSemantics(), + errorReporter, resourceDeps, manifestValues, - ApplicationManifest.useLegacyMerging( - errorReporter, ctx.getAndroidConfig(), manifestMerger)); + manifestMerger); ResourceApk resourceApk = ProcessedAndroidData.processBinaryDataFrom( diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java b/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java deleted file mode 100644 index 3b4f239a1a..0000000000 --- a/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java +++ /dev/null @@ -1,272 +0,0 @@ -// 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.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.configuredtargets.RuleConfiguredTarget.Mode; -import com.google.devtools.build.lib.cmdline.Label; -import com.google.devtools.build.lib.packages.RuleErrorConsumer; -import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidManifestMerger; -import com.google.devtools.build.lib.syntax.Type; -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 { - - 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; - } - - 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); - } - - 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; - } - } - - /** - * 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( - "<?xml version=\"1.0\" encoding=\"utf-8\"?>", - "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"", - " package=\"" + manifestPackage + "\">", - " <application>", - " </application>", - "</manifest>"); - 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<String, String> getManifestValues(RuleContext context) { - Map<String, String> 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); - } - - private ApplicationManifest() {} - - static Optional<Artifact> maybeMergeWith( - AndroidDataContext dataContext, - AndroidSemantics androidSemantics, - Artifact primaryManifest, - ResourceDependencies resourceDeps, - Map<String, String> manifestValues, - boolean useLegacyMerging, - String customPackage) { - Map<Artifact, Label> 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<Artifact, Label> getMergeeManifests( - Iterable<ValidatedAndroidResources> transitiveData) { - ImmutableSortedMap.Builder<Artifact, Label> builder = - ImmutableSortedMap.orderedBy(Artifact.EXEC_PATH_COMPARATOR); - for (ValidatedAndroidResources d : transitiveData) { - if (d.isManifestExported()) { - builder.put(d.getManifest(), d.getLabel()); - } - } - return builder.build(); - } - - static Optional<Artifact> 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); - } -} diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/StampedAndroidManifest.java b/src/main/java/com/google/devtools/build/lib/rules/android/StampedAndroidManifest.java index a0ef0dd9d7..5cb4dd1df0 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/StampedAndroidManifest.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/StampedAndroidManifest.java @@ -13,10 +13,19 @@ // limitations under the License. package com.google.devtools.build.lib.rules.android; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableMap; 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.configuredtargets.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; +import com.google.devtools.build.lib.syntax.Type; +import java.util.Map; +import java.util.TreeMap; import javax.annotation.Nullable; /** An {@link AndroidManifest} stamped with the correct package. */ @@ -55,27 +64,117 @@ public class StampedAndroidManifest extends AndroidManifest { /** Creates an empty manifest stamped with a specified package. */ public static StampedAndroidManifest createEmpty( ActionConstructionContext context, String pkg, boolean exported) { - return new StampedAndroidManifest( - ApplicationManifest.generateManifest(context, pkg), pkg, exported); + Artifact generatedManifest = + context.getUniqueDirectoryArtifact("_generated", "AndroidManifest.xml"); + + String contents = + Joiner.on("\n") + .join( + "<?xml version=\"1.0\" encoding=\"utf-8\"?>", + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"", + " package=\"" + pkg + "\">", + " <application>", + " </application>", + "</manifest>"); + context.registerAction( + FileWriteAction.create(context, generatedManifest, contents, /*makeExecutable=*/ false)); + return new StampedAndroidManifest(generatedManifest, pkg, exported); } public StampedAndroidManifest addMobileInstallStubApplication(RuleContext ruleContext) throws InterruptedException { - return new StampedAndroidManifest( - ApplicationManifest.addMobileInstallStubApplication(ruleContext, getManifest()), - getPackage(), - isExported()); + + 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(getManifest()) + .addOutput(stubManifest) + .addOutput(stubData); + CustomCommandLine.Builder commandLine = + CustomCommandLine.builder() + .add("--mode=mobile_install") + .addExecPath("--input_manifest", getManifest()) + .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 new StampedAndroidManifest(stubManifest, getPackage(), isExported()); + } + + public static Map<String, String> getManifestValues(RuleContext context) { + if (!context.attributes().isAttributeValueExplicitlySpecified("manifest_values")) { + return ImmutableMap.of(); + } + + Map<String, String> manifestValues = + new TreeMap<>(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 StampedAndroidManifest createSplitManifest( RuleContext ruleContext, String splitName, boolean hasCode) { - return new StampedAndroidManifest( - ApplicationManifest.createSplitManifest(ruleContext, getManifest(), splitName, hasCode), - getPackage(), - isExported()); + // aapt insists that manifests be called AndroidManifest.xml, even though they have to be + // explicitly designated as manifests on the command line + Artifact splitManifest = + 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(getManifest()) + .addOutput(splitManifest); + CustomCommandLine.Builder commandLine = + CustomCommandLine.builder() + .addExecPath("--main_manifest", getManifest()) + .addExecPath("--split_manifest", splitManifest) + .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 new StampedAndroidManifest(splitManifest, getPackage(), isExported()); } public AndroidManifestInfo toProvider() { return AndroidManifestInfo.of(getManifest(), getPackage(), isExported()); } + + @Override + public boolean equals(Object object) { + return (object instanceof StampedAndroidManifest && super.equals(object)); + } } |