aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules
diff options
context:
space:
mode:
authorGravatar Andrew Pellegrini <apell@google.com>2016-06-22 20:46:21 +0000
committerGravatar Lukacs Berki <lberki@google.com>2016-06-23 11:10:23 +0000
commite1a4a812971f604d51bdf098b6117d9ba03da1c8 (patch)
treed53b64a6f80fb82cfa43d65c9b3f2f73b9084cdf /src/main/java/com/google/devtools/build/lib/rules
parent6105e2415faa0f36ec6ae3399d90e1a173ef7f58 (diff)
Adds the Android manifest merger as an option for android_binary rules. The merger that is used (legacy or android) is controlled by the manifest_merger attribute on android_binary and the default is controlled by the --android_manifest_merger flag.
RELNOTES: The Android manifest merger is now available as an option for android_binary rules. The merger will honor tools annotations in AndroidManifest.xml and will perform placeholder substitutions using the values specified in android_binary.manifest_values. The merger may be selected by setting the manifest_merger attribute on android_binary. -- MOS_MIGRATED_REVID=125603954
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java31
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinaryOnlyRule.java44
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java48
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java16
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java13
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java167
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/LocalResourceContainer.java5
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/ManifestMergerActionBuilder.java138
8 files changed, 339 insertions, 123 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 500c60165d..c9530a6c69 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
@@ -191,25 +191,6 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory {
LocalResourceContainer.validateRuleContext(ruleContext);
ApplicationManifest ruleManifest = androidSemantics.getManifestForRule(ruleContext);
- String applicationId = null;
- String versionCode = null;
- String versionName = null;
- Map<String, String> manifestValues =
- ruleContext.attributes().get("manifest_values", Type.STRING_DICT);
- if (manifestValues != null) {
- if (manifestValues.containsKey("applicationId")) {
- applicationId = manifestValues.get("applicationId");
- }
- if (manifestValues.containsKey("versionCode")) {
- versionCode = ruleContext.expandMakeVariables("manifest_values",
- manifestValues.get("versionCode"));
- }
- if (manifestValues.containsKey("versionName")) {
- versionName = ruleContext.expandMakeVariables("manifest_values",
- manifestValues.get("versionName"));
- }
- }
-
applicationManifest = ruleManifest.mergeWith(ruleContext, resourceDeps);
resourceApk = applicationManifest.packWithDataAndResources(
@@ -223,9 +204,6 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory {
ruleContext.getTokenizedStringListAttr("nocompress_extensions"),
ruleContext.attributes().get("crunch_png", Type.BOOLEAN),
ruleContext.getTokenizedStringListAttr("densities"),
- applicationId,
- versionCode,
- versionName,
false, /* incremental */
ProguardHelper.getProguardConfigArtifact(ruleContext, ""),
createMainDexProguardSpec(ruleContext),
@@ -247,9 +225,6 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory {
ruleContext.getTokenizedStringListAttr("nocompress_extensions"),
ruleContext.attributes().get("crunch_png", Type.BOOLEAN),
ruleContext.getTokenizedStringListAttr("densities"),
- applicationId,
- versionCode,
- versionName,
true, /* incremental */
ProguardHelper.getProguardConfigArtifact(ruleContext, "incremental"),
null, /* mainDexProguardCfg */
@@ -270,9 +245,6 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory {
ruleContext.getTokenizedStringListAttr("nocompress_extensions"),
ruleContext.attributes().get("crunch_png", Type.BOOLEAN),
ruleContext.getTokenizedStringListAttr("densities"),
- applicationId,
- versionCode,
- versionName,
true, /* incremental */
ProguardHelper.getProguardConfigArtifact(ruleContext, "instant_run"),
null, /* mainDexProguardCfg */
@@ -293,9 +265,6 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory {
ruleContext.getTokenizedStringListAttr("nocompress_extensions"),
ruleContext.attributes().get("crunch_png", Type.BOOLEAN),
ruleContext.getTokenizedStringListAttr("densities"),
- applicationId,
- versionCode,
- versionName,
true, /* incremental */
ProguardHelper.getProguardConfigArtifact(ruleContext, "incremental_split"),
null, /* mainDexProguardCfg */
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinaryOnlyRule.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinaryOnlyRule.java
index a558b05664..e6e116d888 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinaryOnlyRule.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinaryOnlyRule.java
@@ -26,6 +26,7 @@ import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
import com.google.devtools.build.lib.packages.Attribute.AllowedValueSet;
import com.google.devtools.build.lib.packages.RuleClass;
import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
+import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidManifestMerger;
import com.google.devtools.build.lib.rules.android.AndroidRuleClasses.MultidexMode;
/**
@@ -35,15 +36,40 @@ public final class AndroidBinaryOnlyRule implements RuleDefinition {
@Override
public RuleClass build(RuleClass.Builder builder, final RuleDefinitionEnvironment env) {
return builder
-// /* <!-- #BLAZE_RULE(android_binary).ATTRIBUTE(manifest_values) -->
-// A dictionary of values to be overridden in the manifest. Any instance of ${name} in the
-// manifest will be replaced with the value corresponding to name in this dictionary.
-// applicationId, versionCode, versionName, minSdkVersion, targetSdkVersion and
-// maxSdkVersion will also override the corresponding attributes of the manifest and
-// uses-sdk tags. packageName will be ignored and will be set from either applicationId if
-// specified or the package in manifest.
-// <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
- .add(attr("manifest_values", STRING_DICT).undocumented("not ready for production use"))
+ /* <!-- #BLAZE_RULE(android_binary).ATTRIBUTE(manifest_merger) -->
+ Select the manifest merger to use for this rule.<br/>
+ Possible values:
+ <ul>
+ <li><code>manifest_merger = "legacy"</code>: Use the legacy manifest merger. Does not
+ allow features of the android merger like placeholder substitution and tools
+ attributes for defining merge behavior. Removes all
+ <code>&lt;uses-permission&gt;</code> and <code>&lt;uses-permission-sdk-23&gt;</code>
+ tags. Performs a tag-level merge.</li>
+ <li><code>manifest_merger = "android"</code>: Use the android manifest merger. Allows
+ features like placeholder substitution and tools attributes for defining merge
+ behavior. Follows the semantics from
+ <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger">
+ the documentation</a> except it has been modified to also remove all
+ <code>&lt;uses-permission&gt;</code> and <code>&lt;uses-permission-sdk-23&gt;</code>
+ tags. Performs an attribute-level merge.</li>
+ <li><code>manifest_merger = "auto"</code>: Merger is controlled by the
+ <a href="../blaze-user-manual.html#flag--android_manifest_merger">
+ --android_manifest_merger</a> flag.</li>
+ </ul>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("manifest_merger", STRING)
+ .allowedValues(new AllowedValueSet(AndroidManifestMerger.getAttributeValues()))
+ .value(AndroidManifestMerger.getRuleAttributeDefault()))
+ /* <!-- #BLAZE_RULE(android_binary).ATTRIBUTE(manifest_values) -->
+ A dictionary of values to be overridden in the manifest. Any instance of ${name} in the
+ manifest will be replaced with the value corresponding to name in this dictionary.
+ applicationId, versionCode, versionName, minSdkVersion, targetSdkVersion and
+ maxSdkVersion will also override the corresponding attributes of the manifest and
+ uses-sdk tags. packageName will be ignored and will be set from either applicationId if
+ specified or the package in manifest. When manifest_merger is set to legacy, only
+ applicationId, versionCode and versionName will have any effect.
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("manifest_values", STRING_DICT))
/* <!-- #BLAZE_RULE(android_binary).ATTRIBUTE(nocompress_extensions) -->
A list of file extension to leave uncompressed in apk.
<!-- #END_BLAZE_RULE.ATTRIBUTE --> */
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 cdc30bf39f..d8f7cbb724 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
@@ -98,6 +98,16 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment {
}
/**
+ * Converter for {@link AndroidManifestMerger}
+ */
+ public static final class AndroidManifestMergerConverter
+ extends EnumConverter<AndroidManifestMerger> {
+ public AndroidManifestMergerConverter() {
+ super(AndroidManifestMerger.class, "android manifest merger");
+ }
+ }
+
+ /**
* Value used to avoid multiple configurations from conflicting.
*
* <p>This is set to {@code ANDROID} in Android configurations and to {@code MAIN} otherwise. This
@@ -139,6 +149,30 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment {
}
}
+ /** Types of android manifest mergers. */
+ public enum AndroidManifestMerger {
+ LEGACY,
+ ANDROID;
+
+ public static List<String> getAttributeValues() {
+ return ImmutableList.of(LEGACY.name().toLowerCase(), ANDROID.name().toLowerCase(),
+ getRuleAttributeDefault());
+ }
+
+ public static String getRuleAttributeDefault() {
+ return "auto";
+ }
+
+ public static AndroidManifestMerger fromString(String value) {
+ for (AndroidManifestMerger merger : AndroidManifestMerger.values()) {
+ if (merger.name().equalsIgnoreCase(value)) {
+ return merger;
+ }
+ }
+ return null;
+ }
+ }
+
/**
* Android configuration options.
*/
@@ -296,6 +330,14 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment {
help = "Enables the use of an obfuscation map when generating the main dex jar file")
public boolean useProguardPreviousObfuscationMap;
+ @Option(name = "android_manifest_merger",
+ defaultValue = "legacy",
+ category = "semantics",
+ converter = AndroidManifestMergerConverter.class,
+ help = "Selects the manifest merger to use for android_binary rules. Flag to help the"
+ + "transition to the Android manifest merger from the legacy merger.")
+ public AndroidManifestMerger manifestMerger;
+
@Override
public void addAllLabels(Multimap<String, Label> labelMap) {
if (androidCrosstoolTop != null) {
@@ -366,6 +408,7 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment {
private final boolean allowAndroidLibraryDepsWithoutSrcs;
private final boolean useAndroidResourceShrinking;
private final boolean useProguardPreviousObfuscationMap;
+ private final AndroidManifestMerger manifestMerger;
AndroidConfiguration(Options options, Label androidSdk) {
this.sdk = androidSdk;
@@ -389,6 +432,7 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment {
this.allowAndroidLibraryDepsWithoutSrcs = options.allowAndroidLibraryDepsWithoutSrcs;
this.useAndroidResourceShrinking = options.useAndroidResourceShrinking;
this.useProguardPreviousObfuscationMap = options.useProguardPreviousObfuscationMap;
+ this.manifestMerger = options.manifestMerger;
}
public String getCpu() {
@@ -465,6 +509,10 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment {
return useProguardPreviousObfuscationMap;
}
+ public AndroidManifestMerger getManifestMerger() {
+ return manifestMerger;
+ }
+
@Override
public void addGlobalMakeVariables(ImmutableMap.Builder<String, String> globalMakeEnvBuilder) {
globalMakeEnvBuilder.put("ANDROID_CPU", cpu);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java
index 8630825550..964db8ae54 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java
@@ -25,7 +25,6 @@ import com.google.devtools.build.lib.analysis.config.CompilationMode;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.packages.BuildType;
-import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
import com.google.devtools.build.lib.rules.android.AndroidLibraryAarProvider.Aar;
import com.google.devtools.build.lib.rules.android.AndroidResourcesProvider.ResourceContainer;
@@ -78,7 +77,8 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory {
final ResourceApk resourceApk;
if (definesLocalResources) {
- ApplicationManifest applicationManifest = androidSemantics.getManifestForRule(ruleContext);
+ ApplicationManifest applicationManifest = androidSemantics.getManifestForRule(ruleContext)
+ .renamePackage(ruleContext, AndroidCommon.getJavaPackage(ruleContext));
resourceApk = applicationManifest.packWithDataAndResources(
null, /* resourceApk -- not needed for library */
ruleContext,
@@ -90,11 +90,8 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory {
ImmutableList.<String>of(), /* uncompressedExtensions */
false, /* crunchPng */
ImmutableList.<String>of(), /* densities */
- null /* applicationId */,
- null /* versionCode */,
- null /* versionName */,
- false,
- null /* proguardCfgOut */,
+ false, /* incremental */
+ null, /* proguardCfgOut */
null, /* mainDexProguardCfg */
ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_PROCESSED_MANIFEST),
null /* mergedResourcesOut */);
@@ -145,7 +142,8 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory {
} else {
// there are no local resources and resources attribute was not specified either
aar = null;
- ApplicationManifest applicationManifest = ApplicationManifest.generatedManifest(ruleContext);
+ ApplicationManifest applicationManifest = ApplicationManifest.generatedManifest(ruleContext)
+ .renamePackage(ruleContext, AndroidCommon.getJavaPackage(ruleContext));
String javaPackage = AndroidCommon.getJavaPackage(ruleContext);
@@ -200,7 +198,7 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory {
.add(ProguardSpecProvider.class, new ProguardSpecProvider(transitiveProguardConfigs))
.addOutputGroup(OutputGroupProvider.HIDDEN_TOP_LEVEL, transitiveProguardConfigs)
.add(AndroidLibraryAarProvider.class, new AndroidLibraryAarProvider(
- aar, transitiveAars.build()))
+ aar, transitiveAars.build()))
.build();
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
index 306a922bce..0f2008b569 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
@@ -165,11 +165,10 @@ public final class AndroidRuleClasses {
"//tools/android:incremental_stub_application";
public static final String DEFAULT_INCREMENTAL_SPLIT_STUB_APPLICATION =
"//tools/android:incremental_split_stub_application";
- public static final String DEFAULT_RESOURCES_PROCESSOR =
- "//tools/android:resources_processor";
- public static final String DEFAULT_RESOURCE_SHRINKER =
- "//tools/android:resource_shrinker";
public static final String DEFAULT_AAR_GENERATOR = "//tools/android:aar_generator";
+ public static final String DEFAULT_MANIFEST_MERGER = "//tools/android:manifest_merger";
+ public static final String DEFAULT_RESOURCES_PROCESSOR = "//tools/android:resources_processor";
+ public static final String DEFAULT_RESOURCE_SHRINKER = "//tools/android:resource_shrinker";
public static final String DEFAULT_SDK = "//tools/android:sdk";
/**
@@ -398,12 +397,14 @@ public final class AndroidRuleClasses {
@Override
public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) {
return builder
+ .add(attr("$android_aar_generator", LABEL).cfg(HOST).exec().value(
+ env.getToolsLabel(DEFAULT_AAR_GENERATOR)))
+ .add(attr("$android_manifest_merger", LABEL).cfg(HOST).exec().value(
+ env.getToolsLabel(DEFAULT_MANIFEST_MERGER)))
.add(attr("$android_resources_processor", LABEL).cfg(HOST).exec().value(
env.getToolsLabel(DEFAULT_RESOURCES_PROCESSOR)))
.add(attr("$android_resource_shrinker", LABEL).cfg(HOST).exec().value(
env.getToolsLabel(DEFAULT_RESOURCE_SHRINKER)))
- .add(attr("$android_aar_generator", LABEL).cfg(HOST).exec().value(
- env.getToolsLabel(DEFAULT_AAR_GENERATOR)))
.build();
}
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
index 296f4aff14..8992776ec2 100644
--- 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
@@ -13,8 +13,12 @@
// 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.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
@@ -26,6 +30,7 @@ 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.rules.android.AndroidConfiguration.AndroidManifestMerger;
import com.google.devtools.build.lib.rules.android.AndroidResourcesProvider.ResourceContainer;
import com.google.devtools.build.lib.rules.android.AndroidResourcesProvider.ResourceType;
import com.google.devtools.build.lib.syntax.Type;
@@ -33,6 +38,7 @@ import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.List;
import java.util.Map;
+import java.util.TreeMap;
import javax.annotation.Nullable;
@@ -44,9 +50,8 @@ public final class ApplicationManifest {
ruleContext.attributeError("manifest", "a resources or manifest attribute is mandatory.");
return null;
}
- return new ApplicationManifest(Iterables.getOnlyElement(
- resources.getDirectAndroidResources())
- .getManifest());
+ return new ApplicationManifest(
+ ruleContext, Iterables.getOnlyElement(resources.getDirectAndroidResources()).getManifest());
}
public ApplicationManifest createSplitManifest(
@@ -67,7 +72,7 @@ public final class ApplicationManifest {
.addArgument(splitName)
.addArgument(hasCode ? "--hascode" : "--nohascode");
- String overridePackage = getOverridePackage(ruleContext);
+ String overridePackage = manifestValues.get("applicationId");
if (overridePackage != null) {
builder
.addArgument("--override_package")
@@ -75,31 +80,7 @@ public final class ApplicationManifest {
}
ruleContext.registerAction(builder.build(ruleContext));
- return new ApplicationManifest(result);
- }
-
- private String getOverridePackage(RuleContext ruleContext) {
- // It seems that we sometimes rename the app for God-knows-what reason. If that is the case,
- // pass this information to the stubifier script.
- if (ruleContext.attributes().isAttributeValueExplicitlySpecified("manifest_values")) {
- Map<String, String> manifestValues =
- ruleContext.attributes().get("manifest_values", Type.STRING_DICT);
- if (manifestValues.containsKey("applicationId")) {
- return manifestValues.get("applicationId");
- }
- }
- if (ruleContext.attributes().isAttributeValueExplicitlySpecified("application_id")) {
- return ruleContext.attributes().get("application_id", Type.STRING);
- }
-
- AndroidResourcesProvider resourcesProvider = AndroidCommon.getAndroidResources(ruleContext);
- if (resourcesProvider != null) {
- ResourceContainer resourceContainer = Iterables.getOnlyElement(
- resourcesProvider.getDirectAndroidResources());
- return resourceContainer.getRenameManifestPackage();
- } else {
- return null;
- }
+ return new ApplicationManifest(ruleContext, result);
}
public ApplicationManifest addMobileInstallStubApplication(RuleContext ruleContext)
@@ -121,7 +102,7 @@ public final class ApplicationManifest {
.addOutputArgument(ruleContext.getImplicitOutputArtifact(
AndroidRuleClasses.MOBILE_INSTALL_STUB_APPLICATION_DATA));
- String overridePackage = getOverridePackage(ruleContext);
+ String overridePackage = manifestValues.get("applicationId");
if (overridePackage != null) {
builder.addArgument("--override_package");
builder.addArgument(overridePackage);
@@ -129,7 +110,7 @@ public final class ApplicationManifest {
ruleContext.registerAction(builder.build(ruleContext));
- return new ApplicationManifest(stubManifest);
+ return new ApplicationManifest(ruleContext, stubManifest);
}
public ApplicationManifest addInstantRunStubApplication(RuleContext ruleContext)
@@ -150,15 +131,17 @@ public final class ApplicationManifest {
ruleContext.registerAction(builder.build(ruleContext));
- return new ApplicationManifest(stubManifest);
+ return new ApplicationManifest(ruleContext, stubManifest);
}
-
+
public static ApplicationManifest fromRule(RuleContext ruleContext) {
- return new ApplicationManifest(ruleContext.getPrerequisiteArtifact("manifest", Mode.TARGET));
+ return new ApplicationManifest(
+ ruleContext, ruleContext.getPrerequisiteArtifact("manifest", Mode.TARGET));
}
- public static ApplicationManifest fromExplicitManifest(Artifact manifest) {
- return new ApplicationManifest(manifest);
+ public static ApplicationManifest fromExplicitManifest(
+ RuleContext ruleContext, Artifact manifest) {
+ return new ApplicationManifest(ruleContext, manifest);
}
/**
@@ -184,26 +167,78 @@ public final class ApplicationManifest {
"</manifest>");
ruleContext.getAnalysisEnvironment().registerAction(new FileWriteAction(
ruleContext.getActionOwner(), generatedManifest, contents, false /* makeExecutable */));
- return new ApplicationManifest(generatedManifest);
+ 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(Artifact manifest) {
+ private ApplicationManifest(RuleContext ruleContext, Artifact manifest) {
this.manifest = manifest;
+ this.manifestValues = getManifestValues(ruleContext);
}
- public ApplicationManifest mergeWith(RuleContext ruleContext,
- ResourceDependencies resourceDeps) {
+ public ApplicationManifest mergeWith(RuleContext ruleContext, ResourceDependencies resourceDeps) {
Iterable<Artifact> mergeeManifests = getMergeeManifests(resourceDeps.getResources());
- if (!Iterables.isEmpty(mergeeManifests)) {
- Iterable<Artifact> exportedManifests = mergeeManifests;
- Artifact outputManifest = ruleContext.getUniqueDirectoryArtifact(
- ruleContext.getRule().getName() + "_merged", "AndroidManifest.xml",
- ruleContext.getBinOrGenfilesDirectory());
- AndroidManifestMergeHelper.createMergeManifestAction(ruleContext, getManifest(),
- exportedManifests, ImmutableList.of("all"), outputManifest);
- return new ApplicationManifest(outputManifest);
+
+ 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 (!Iterables.isEmpty(mergeeManifests)) {
+ Artifact outputManifest = ruleContext.getUniqueDirectoryArtifact(
+ ruleContext.getRule().getName() + "_merged", "AndroidManifest.xml",
+ ruleContext.getBinOrGenfilesDirectory());
+ AndroidManifestMergeHelper.createMergeManifestAction(ruleContext, getManifest(),
+ mergeeManifests, ImmutableList.of("all"), outputManifest);
+ return new ApplicationManifest(ruleContext, outputManifest);
+ }
+ } else {
+ if (!Iterables.isEmpty(mergeeManifests) || !manifestValues.isEmpty()) {
+ Artifact outputManifest = ruleContext.getUniqueDirectoryArtifact(
+ ruleContext.getRule().getName() + "_merged", "AndroidManifest.xml",
+ ruleContext.getBinOrGenfilesDirectory());
+ new ManifestMergerActionBuilder(ruleContext)
+ .setManifest(getManifest())
+ .setMergeeManifests(mergeeManifests)
+ .setLibrary(false)
+ .setManifestValues(manifestValues)
+ .setCustomPackage(AndroidCommon.getJavaPackage(ruleContext))
+ .setManifestOutput(outputManifest)
+ .build(ruleContext);
+ return new ApplicationManifest(ruleContext, outputManifest);
+ }
}
return this;
}
@@ -220,6 +255,22 @@ public final class ApplicationManifest {
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(
@@ -244,14 +295,11 @@ public final class ApplicationManifest {
false, /* isLibrary */
resourceDeps,
rTxt,
- null, /* symbolsTxt */
+ null, /* Artifact symbolsTxt */
ImmutableList.<String>of(), /* configurationFilters */
ImmutableList.<String>of(), /* uncompressedExtensions */
true, /* crunchPng */
ImmutableList.<String>of(), /* densities */
- null, /* String applicationId */
- null, /* String versionCode */
- null, /* String versionName */
incremental,
data,
proguardCfg,
@@ -272,9 +320,6 @@ public final class ApplicationManifest {
List<String> uncompressedExtensions,
boolean crunchPng,
List<String> densities,
- String applicationId,
- String versionCode,
- String versionName,
boolean incremental,
Artifact proguardCfg,
@Nullable Artifact mainDexProguardCfg,
@@ -307,9 +352,6 @@ public final class ApplicationManifest {
uncompressedExtensions,
crunchPng,
densities,
- applicationId,
- versionCode,
- versionName,
incremental,
data,
proguardCfg,
@@ -329,9 +371,6 @@ public final class ApplicationManifest {
List<String> uncompressedExtensions,
boolean crunchPng,
List<String> densities,
- String applicationId,
- String versionCode,
- String versionName,
boolean incremental,
LocalResourceContainer data,
Artifact proguardCfg,
@@ -370,9 +409,9 @@ public final class ApplicationManifest {
.setDensities(densities)
.setProguardOut(proguardCfg)
.setMainDexProguardOut(mainDexProguardCfg)
- .setApplicationId(applicationId)
- .setVersionCode(versionCode)
- .setVersionName(versionName);
+ .setApplicationId(manifestValues.get("applicationId"))
+ .setVersionCode(manifestValues.get("versionCode"))
+ .setVersionName(manifestValues.get("versionName"));
if (!incremental) {
builder
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/LocalResourceContainer.java b/src/main/java/com/google/devtools/build/lib/rules/android/LocalResourceContainer.java
index acd5cf1aa3..971ccf7dfb 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/LocalResourceContainer.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/LocalResourceContainer.java
@@ -45,10 +45,7 @@ public final class LocalResourceContainer {
"assets",
"assets_dir",
"inline_constants",
- "exports_manifest",
- "application_id",
- "version_name",
- "version_code"
+ "exports_manifest"
};
/**
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ManifestMergerActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/ManifestMergerActionBuilder.java
new file mode 100644
index 0000000000..21174401a0
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/ManifestMergerActionBuilder.java
@@ -0,0 +1,138 @@
+// Copyright 2016 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 com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
+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.SpawnAction;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Builder for creating manifest merger actions.
+ */
+public class ManifestMergerActionBuilder {
+ private final RuleContext ruleContext;
+ private final SpawnAction.Builder spawnActionBuilder;
+
+ private Artifact manifest;
+ private List<Artifact> mergeeManifests;
+ private boolean isLibrary;
+ private Map<String, String> manifestValues;
+ private String customPackage;
+ private Artifact manifestOutput;
+
+ public ManifestMergerActionBuilder(RuleContext ruleContext) {
+ this.ruleContext = ruleContext;
+ this.spawnActionBuilder = new SpawnAction.Builder();
+ }
+
+ public ManifestMergerActionBuilder setManifest(Artifact manifest) {
+ this.manifest = manifest;
+ return this;
+ }
+
+ public ManifestMergerActionBuilder setMergeeManifests(Iterable<Artifact> mergeeManifests) {
+ this.mergeeManifests = ImmutableList.copyOf(mergeeManifests);
+ return this;
+ }
+
+ public ManifestMergerActionBuilder setLibrary(boolean isLibrary) {
+ this.isLibrary = isLibrary;
+ return this;
+ }
+
+ public ManifestMergerActionBuilder setManifestValues(Map<String, String> manifestValues) {
+ this.manifestValues = manifestValues;
+ return this;
+ }
+
+ public ManifestMergerActionBuilder setCustomPackage(String customPackage) {
+ this.customPackage = customPackage;
+ return this;
+ }
+
+ public ManifestMergerActionBuilder setManifestOutput(Artifact manifestOutput) {
+ this.manifestOutput = manifestOutput;
+ return this;
+ }
+
+ public void build(ActionConstructionContext context) {
+ NestedSetBuilder<Artifact> inputs = NestedSetBuilder.naiveLinkOrder();
+ ImmutableList.Builder<Artifact> outputs = ImmutableList.builder();
+ CustomCommandLine.Builder builder = new CustomCommandLine.Builder();
+
+ inputs.addAll(ruleContext.getExecutablePrerequisite("$android_manifest_merger", Mode.HOST)
+ .getRunfilesSupport()
+ .getRunfilesArtifactsWithoutMiddlemen());
+
+ builder.addExecPath("--manifest", manifest);
+ inputs.add(manifest);
+
+ if (mergeeManifests != null && !mergeeManifests.isEmpty()) {
+ builder.addJoinExecPaths("--mergeeManifests", ":", mergeeManifests);
+ inputs.addAll(mergeeManifests);
+ }
+
+ if (isLibrary) {
+ builder.add("--mergeType").add("LIBRARY");
+ }
+
+ if (manifestValues != null && !manifestValues.isEmpty()) {
+ builder.add("--manifestValues").add(mapToDictionaryString(manifestValues));
+ }
+
+ if (customPackage != null && !customPackage.isEmpty()) {
+ builder.add("--customPackage").add(customPackage);
+ }
+
+ builder.addExecPath("--manifestOutput", manifestOutput);
+ outputs.add(manifestOutput);
+
+ ruleContext.registerAction(
+ this.spawnActionBuilder
+ .addTransitiveInputs(inputs.build())
+ .addOutputs(outputs.build())
+ .setCommandLine(builder.build())
+ .setExecutable(
+ ruleContext.getExecutablePrerequisite("$android_manifest_merger", Mode.HOST))
+ .setProgressMessage("Merging manifest")
+ .setMnemonic("ManifestMerger")
+ .build(context));
+ }
+
+ private <K, V> String mapToDictionaryString(Map<K, V> map) {
+ StringBuilder sb = new StringBuilder();
+ Iterator<Entry<K, V>> iter = map.entrySet().iterator();
+ while (iter.hasNext()) {
+ Entry<K, V> entry = iter.next();
+ sb.append(entry.getKey().toString().replace(":", "\\:").replace(",", "\\,"));
+ sb.append(':');
+ sb.append(entry.getValue().toString().replace(":", "\\:").replace(",", "\\,"));
+ if (iter.hasNext()) {
+ sb.append(',');
+ }
+ }
+ return sb.toString();
+ }
+}
+