aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2017-07-13 19:50:00 +0200
committerGravatar László Csomor <laszlocsomor@google.com>2017-07-14 10:52:06 +0200
commit5abf4ed4dc9fc134e47f9b56e3b65ba26d0ba9f0 (patch)
treea046d265ae8c46d59150f849c15fc87fcc94f182
parentfba07bb72570245d26bd8795709c5a004fc9026a (diff)
Add flag to turn Android resource merge conflicts from warnings into errors
RELNOTES: none PiperOrigin-RevId: 161831232
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AarGeneratorBuilder.java10
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java15
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java4
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java11
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java11
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java10
-rw-r--r--src/test/java/com/google/devtools/build/android/AndroidDataMergerTest.java106
-rw-r--r--src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java18
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java16
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidDataMerger.java50
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceMerger.java32
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceMergingAction.java15
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java15
13 files changed, 248 insertions, 65 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AarGeneratorBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/AarGeneratorBuilder.java
index b0ffa9e5b5..c311d82f0f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AarGeneratorBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AarGeneratorBuilder.java
@@ -40,6 +40,7 @@ public class AarGeneratorBuilder {
private Artifact classes;
private Artifact aarOut;
+ private boolean throwOnResourceConflict;
private final RuleContext ruleContext;
private final SpawnAction.Builder builder;
@@ -79,6 +80,11 @@ public class AarGeneratorBuilder {
return this;
}
+ public AarGeneratorBuilder setThrowOnResourceConflict(boolean throwOnResourceConflict) {
+ this.throwOnResourceConflict = throwOnResourceConflict;
+ return this;
+ }
+
public void build(ActionConstructionContext context) {
List<Artifact> outs = new ArrayList<>();
List<Artifact> ins = new ArrayList<>();
@@ -115,6 +121,10 @@ public class AarGeneratorBuilder {
args.add(aarOut.getExecPathString());
outs.add(aarOut);
+ if (throwOnResourceConflict) {
+ args.add("--throwOnResourceConflict");
+ }
+
ruleContext.registerAction(
this.builder
.addInputs(ImmutableList.<Artifact>copyOf(ins))
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 4820e03710..03878f5bfe 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
@@ -648,6 +648,15 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment {
)
public boolean generateRobolectricRClass;
+ @Option(
+ name = "experimental_android_throw_on_resource_conflict",
+ defaultValue = "false",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "If passed, resource merge conflicts will be treated as errors instead of warnings"
+ )
+ public boolean throwOnResourceConflict;
+
@Override
public FragmentOptions getHost(boolean fallback) {
Options host = (Options) super.getHost(fallback);
@@ -725,6 +734,7 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment {
private final boolean exportsManifestDefault;
private final AndroidAaptVersion androidAaptVersion;
private final boolean generateRobolectricRClass;
+ private final boolean throwOnResourceConflict;
private final boolean useParallelDex2Oat;
AndroidConfiguration(Options options) throws InvalidConfigurationException {
@@ -760,6 +770,7 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment {
this.exportsManifestDefault = options.exportsManifestDefault;
this.androidAaptVersion = options.androidAaptVersion;
this.generateRobolectricRClass = options.generateRobolectricRClass;
+ this.throwOnResourceConflict = options.throwOnResourceConflict;
this.useParallelDex2Oat = options.useParallelDex2Oat;
if (!dexoptsSupportedInIncrementalDexing.contains("--no-locals")) {
@@ -889,6 +900,10 @@ public class AndroidConfiguration extends BuildConfiguration.Fragment {
return generateRobolectricRClass;
}
+ boolean throwOnResourceConflict() {
+ return throwOnResourceConflict;
+ }
+
@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 98fcb45946..83cc88ec6c 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
@@ -180,6 +180,8 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory {
.withPrimary(resourceContainer)
.withDependencies(resourceApk.getResourceDependencies())
.setDebug(ruleContext.getConfiguration().getCompilationMode() != CompilationMode.OPT)
+ .setThrowOnResourceConflict(
+ ruleContext.getFragment(AndroidConfiguration.class).throwOnResourceConflict())
.build(ruleContext);
}
@@ -189,6 +191,8 @@ public abstract class AndroidLibrary implements RuleConfiguredTargetFactory {
.withRtxt(primaryResources.getRTxt())
.withClasses(classesJar)
.setAAROut(aarOut)
+ .setThrowOnResourceConflict(
+ ruleContext.getFragment(AndroidConfiguration.class).throwOnResourceConflict())
.build(ruleContext);
RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(ruleContext);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java
index e86cae2441..5b63f24af6 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceMergingActionBuilder.java
@@ -65,6 +65,7 @@ public class AndroidResourceMergingActionBuilder {
// Flags
private String customJavaPackage;
+ private boolean throwOnResourceConflict;
/** @param ruleContext The RuleContext that was used to create the SpawnAction.Builder. */
public AndroidResourceMergingActionBuilder(RuleContext ruleContext) {
@@ -116,6 +117,12 @@ public class AndroidResourceMergingActionBuilder {
return this;
}
+ public AndroidResourceMergingActionBuilder setThrowOnResourceConflict(
+ boolean throwOnResourceConflict) {
+ this.throwOnResourceConflict = throwOnResourceConflict;
+ return this;
+ }
+
public ResourceContainer build(ActionConstructionContext context) {
CustomCommandLine.Builder builder = new CustomCommandLine.Builder();
@@ -170,6 +177,10 @@ public class AndroidResourceMergingActionBuilder {
outs.add(dataBindingInfoZip);
}
+ if (throwOnResourceConflict) {
+ builder.add("--throwOnResourceConflict");
+ }
+
SpawnAction.Builder spawnActionBuilder = new SpawnAction.Builder();
// Create the spawn action.
ruleContext.registerAction(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java
index b61bee6269..fd827b6962 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java
@@ -110,6 +110,7 @@ public class AndroidResourcesProcessorBuilder {
private Artifact featureOf;
private Artifact featureAfter;
private AndroidAaptVersion aaptVersion;
+ private boolean throwOnResourceConflict;
/**
* @param ruleContext The RuleContext that was used to create the SpawnAction.Builder.
@@ -232,6 +233,12 @@ public class AndroidResourcesProcessorBuilder {
return this;
}
+ public AndroidResourcesProcessorBuilder setThrowOnResourceConflict(
+ boolean throwOnResourceConflict) {
+ this.throwOnResourceConflict = throwOnResourceConflict;
+ return this;
+ }
+
public ResourceContainer build(ActionConstructionContext context) {
if (aaptVersion == AndroidAaptVersion.AAPT2) {
return createAapt2ApkAction(context);
@@ -467,5 +474,9 @@ public class AndroidResourcesProcessorBuilder {
builder.addExecPath("--featureAfter", featureAfter);
inputs.add(featureAfter);
}
+
+ if (throwOnResourceConflict) {
+ builder.add("--throwOnResourceConflict");
+ }
}
}
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 a9d096ee2a..1ef2406f50 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
@@ -603,7 +603,10 @@ public final class ApplicationManifest {
.setMergedResourcesOut(mergedResources)
.setManifestOut(manifestOut)
.setClassJarOut(rJavaClassJar)
- .setDataBindingInfoZip(dataBindingInfoZip);
+ .setDataBindingInfoZip(dataBindingInfoZip)
+ .setThrowOnResourceConflict(
+ ruleContext.getConfiguration()
+ .getFragment(AndroidConfiguration.class).throwOnResourceConflict());
ResourceContainer merged = resourcesMergerBuilder.build(ruleContext);
processed =
@@ -642,7 +645,10 @@ public final class ApplicationManifest {
.setVersionCode(manifestValues.get("versionCode"))
.setVersionName(manifestValues.get("versionName"))
.setFeatureOf(featureOf)
- .setFeatureAfter(featureAfter);
+ .setFeatureAfter(featureAfter)
+ .setThrowOnResourceConflict(
+ ruleContext.getConfiguration()
+ .getFragment(AndroidConfiguration.class).throwOnResourceConflict());
if (!incremental) {
builder
.targetAaptVersion(AndroidAaptVersion.AAPT)
diff --git a/src/test/java/com/google/devtools/build/android/AndroidDataMergerTest.java b/src/test/java/com/google/devtools/build/android/AndroidDataMergerTest.java
index 1444b83fd6..e28d65ceb3 100644
--- a/src/test/java/com/google/devtools/build/android/AndroidDataMergerTest.java
+++ b/src/test/java/com/google/devtools/build/android/AndroidDataMergerTest.java
@@ -25,6 +25,7 @@ import com.google.common.jimfs.Jimfs;
import com.google.common.truth.FailureStrategy;
import com.google.common.truth.SubjectFactory;
import com.google.devtools.build.android.AndroidDataBuilder.ResourceType;
+import com.google.devtools.build.android.AndroidDataMerger.MergeConflictException;
import com.google.devtools.build.android.AndroidDataMerger.SourceChecker;
import com.google.devtools.build.android.xml.IdXmlResourceValue;
import com.google.devtools.build.android.xml.PublicXmlResourceValue;
@@ -102,7 +103,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
UnwrittenMergedAndroidData data =
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, true);
UnwrittenMergedAndroidData expected =
UnwrittenMergedAndroidData.of(
@@ -161,7 +162,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
UnwrittenMergedAndroidData data =
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, true);
UnwrittenMergedAndroidData expected =
UnwrittenMergedAndroidData.of(
@@ -226,7 +227,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
UnwrittenMergedAndroidData transitive =
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, true);
UnwrittenMergedAndroidData expected =
UnwrittenMergedAndroidData.of(
@@ -276,7 +277,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, false);
assertThat(loggingHandler.warnings)
.containsExactly(
@@ -325,7 +326,7 @@ public class AndroidDataMergerTest {
});
assertAbout(unwrittenMergedAndroidData)
- .that(merger.merge(transitiveDependency, directDependency, primary, false))
+ .that(merger.merge(transitiveDependency, directDependency, primary, false, true))
.isEqualTo(
UnwrittenMergedAndroidData.of(
primary.getManifest(),
@@ -367,7 +368,7 @@ public class AndroidDataMergerTest {
.buildUnvalidated();
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, true);
assertThat(loggingHandler.warnings).isEmpty();
}
@@ -402,7 +403,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
UnwrittenMergedAndroidData data =
- merger.merge(transitiveDependency, directDependency, primary, true);
+ merger.merge(transitiveDependency, directDependency, primary, true, true);
UnwrittenMergedAndroidData expected =
UnwrittenMergedAndroidData.of(
primary.getManifest(),
@@ -442,7 +443,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, false);
assertThat(loggingHandler.warnings)
.containsExactly(
@@ -489,7 +490,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
UnwrittenMergedAndroidData data =
- merger.merge(transitiveDependency, directDependency, primary, true);
+ merger.merge(transitiveDependency, directDependency, primary, true, true);
UnwrittenMergedAndroidData expected =
UnwrittenMergedAndroidData.of(
primary.getManifest(),
@@ -533,7 +534,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, false);
assertThat(loggingHandler.warnings)
.containsExactly(
MergeConflict.of(
@@ -578,23 +579,62 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, false);
FullyQualifiedName fullyQualifiedName = fqnFactory.parse("string/exit");
assertThat(loggingHandler.warnings)
.containsExactly(
MergeConflict.of(
- fullyQualifiedName,
- DataResourceXml.createWithNoNamespace(
- directRoot.resolve("res/values/strings.xml"),
- SimpleXmlResourceValue.createWithValue(Type.STRING, "no way out")),
- DataResourceXml.createWithNoNamespace(
- transitiveRoot.resolve("res/values/strings.xml"),
- SimpleXmlResourceValue.createWithValue(Type.STRING, "wrong way out")))
+ fullyQualifiedName,
+ DataResourceXml.createWithNoNamespace(
+ directRoot.resolve("res/values/strings.xml"),
+ SimpleXmlResourceValue.createWithValue(Type.STRING, "no way out")),
+ DataResourceXml.createWithNoNamespace(
+ transitiveRoot.resolve("res/values/strings.xml"),
+ SimpleXmlResourceValue.createWithValue(Type.STRING, "wrong way out")))
.toConflictMessage());
}
@Test
+ public void mergeDirectTransitivePrimaryConflictWithThrowOnConflict() throws Exception {
+ Path primaryRoot = fileSystem.getPath("primary");
+ Path directRoot = fileSystem.getPath("direct");
+ Path transitiveRoot = fileSystem.getPath("transitive");
+
+ ParsedAndroidData transitiveDependency =
+ ParsedAndroidDataBuilder.buildOn(transitiveRoot, fqnFactory)
+ .overwritable(
+ xml("string/exit")
+ .source("values/strings.xml")
+ .value(SimpleXmlResourceValue.createWithValue(Type.STRING, "no way out")))
+ .build();
+
+ ParsedAndroidData directDependency =
+ ParsedAndroidDataBuilder.buildOn(directRoot, fqnFactory)
+ .overwritable(
+ xml("string/exit")
+ .source("values/strings.xml")
+ .value(SimpleXmlResourceValue.createWithValue(Type.STRING, "wrong way out")))
+ .build();
+
+ UnvalidatedAndroidData primary =
+ AndroidDataBuilder.of(primaryRoot)
+ .createManifest("AndroidManifest.xml", "com.google.mergetest")
+ .addResource(
+ "values/strings.xml", ResourceType.VALUE, "<string name='exit'>way out</string>")
+ .buildUnvalidated();
+
+ AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
+
+ try {
+ merger.merge(transitiveDependency, directDependency, primary, false, true);
+ throw new Exception("Expected a MergeConflictException!");
+ } catch (MergeConflictException e) {
+ return;
+ }
+ }
+
+ @Test
public void mergeDirectTransitivePrimaryConflictWithPrimaryOverride() throws Exception {
Path primaryRoot = fileSystem.getPath("primary");
Path directRoot = fileSystem.getPath("direct");
@@ -629,7 +669,7 @@ public class AndroidDataMergerTest {
UnwrittenMergedAndroidData data =
AndroidDataMerger.createWithDefaults()
- .merge(transitiveDependency, directDependency, primary, true);
+ .merge(transitiveDependency, directDependency, primary, true, true);
UnwrittenMergedAndroidData expected =
UnwrittenMergedAndroidData.of(
@@ -667,7 +707,7 @@ public class AndroidDataMergerTest {
.buildUnvalidated();
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, false);
assertThat(loggingHandler.warnings)
.containsExactly(
@@ -699,7 +739,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
UnwrittenMergedAndroidData data =
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, true);
UnwrittenMergedAndroidData expected =
UnwrittenMergedAndroidData.of(
@@ -753,7 +793,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
UnwrittenMergedAndroidData data =
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, true);
UnwrittenMergedAndroidData expected =
UnwrittenMergedAndroidData.of(
@@ -816,7 +856,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
UnwrittenMergedAndroidData data =
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, true);
UnwrittenMergedAndroidData expected =
UnwrittenMergedAndroidData.of(
@@ -885,7 +925,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
UnwrittenMergedAndroidData data =
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, true);
UnwrittenMergedAndroidData expected =
UnwrittenMergedAndroidData.of(
@@ -948,7 +988,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
UnwrittenMergedAndroidData data =
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, true);
UnwrittenMergedAndroidData expected =
UnwrittenMergedAndroidData.of(
@@ -996,7 +1036,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
UnwrittenMergedAndroidData data =
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, true);
UnwrittenMergedAndroidData expected =
UnwrittenMergedAndroidData.of(
@@ -1034,7 +1074,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, false);
assertThat(loggingHandler.warnings)
.containsExactly(
MergeConflict.of(
@@ -1073,7 +1113,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
UnwrittenMergedAndroidData data =
- merger.merge(transitiveDependency, directDependency, primary, true);
+ merger.merge(transitiveDependency, directDependency, primary, true, true);
UnwrittenMergedAndroidData expected =
UnwrittenMergedAndroidData.of(
primary.getManifest(),
@@ -1109,7 +1149,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, false);
assertThat(loggingHandler.warnings)
.containsExactly(
MergeConflict.of(
@@ -1140,7 +1180,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
UnwrittenMergedAndroidData data =
- merger.merge(transitiveDependency, directDependency, primary, true);
+ merger.merge(transitiveDependency, directDependency, primary, true, true);
UnwrittenMergedAndroidData expected =
UnwrittenMergedAndroidData.of(
primary.getManifest(),
@@ -1174,7 +1214,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, false);
assertThat(loggingHandler.warnings)
.containsExactly(
MergeConflict.of(
@@ -1209,7 +1249,7 @@ public class AndroidDataMergerTest {
AndroidDataMerger merger = AndroidDataMerger.createWithDefaults();
- merger.merge(transitiveDependency, directDependency, primary, false);
+ merger.merge(transitiveDependency, directDependency, primary, false, false);
assertThat(loggingHandler.warnings)
.containsExactly(
MergeConflict.of(
@@ -1248,7 +1288,7 @@ public class AndroidDataMergerTest {
UnwrittenMergedAndroidData data =
AndroidDataMerger.createWithDefaults()
- .merge(transitiveDependency, directDependency, primary, true);
+ .merge(transitiveDependency, directDependency, primary, true, true);
UnwrittenMergedAndroidData expected =
UnwrittenMergedAndroidData.of(
diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
index 9be5e5ca7f..704d6a4ee9 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
@@ -1569,6 +1569,24 @@ public class AndroidBinaryTest extends AndroidBuildViewTestCase {
}
}
+ @Test
+ public void testThrowOnResourceConflictFlagGetsPropagated() throws Exception {
+ scratch.file(
+ "java/r/android/BUILD",
+ "android_binary(",
+ " name = 'r',",
+ " manifest = 'AndroidManifest.xml',",
+ " resource_files = ['res/values/foo.xml'],",
+ ")");
+ useConfiguration("--experimental_android_throw_on_resource_conflict");
+ ConfiguredTarget binary = getConfiguredTarget("//java/r/android:r");
+
+ List<String> resourceProcessingArgs =
+ resourceGeneratingAction(getResourceContainer(binary)).getArguments();
+
+ assertThat(resourceProcessingArgs).contains("--throwOnResourceConflict");
+ }
+
/**
* Gets the paths of matching artifacts contained within a resource container
*
diff --git a/src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java b/src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java
index 7a6947d698..16091348c1 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AarGeneratorAction.java
@@ -19,6 +19,7 @@ import com.google.common.base.Joiner;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
+import com.google.devtools.build.android.AndroidDataMerger.MergeConflictException;
import com.google.devtools.build.android.AndroidResourceMerger.MergingException;
import com.google.devtools.build.android.Converters.ExistingPathConverter;
import com.google.devtools.build.android.Converters.PathConverter;
@@ -124,6 +125,14 @@ public class AarGeneratorAction {
help = "Path to write the archive."
)
public Path aarOutput;
+
+ @Option(name = "throwOnResourceConflict",
+ defaultValue = "false",
+ category = "config",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "If passed, resource merge conflicts will be treated as errors instead of warnings")
+ public boolean throwOnResourceConflict;
}
public static void main(String[] args) {
@@ -153,12 +162,17 @@ public class AarGeneratorAction {
null,
VariantType.LIBRARY,
null,
- /* filteredResources= */ ImmutableList.<String>of());
+ /* filteredResources= */ ImmutableList.<String>of(),
+ options.throwOnResourceConflict
+ );
logger.fine(String.format("Merging finished at %dms", timer.elapsed(TimeUnit.MILLISECONDS)));
writeAar(options.aarOutput, mergedData, options.manifest, options.rtxt, options.classes);
logger.fine(
String.format("Packaging finished at %dms", timer.elapsed(TimeUnit.MILLISECONDS)));
+ } catch (MergeConflictException e) {
+ logger.log(Level.SEVERE, e.getMessage());
+ System.exit(1);
} catch (IOException | MergingException e) {
logger.log(Level.SEVERE, "Error during merging resources", e);
System.exit(1);
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidDataMerger.java b/src/tools/android/java/com/google/devtools/build/android/AndroidDataMerger.java
index 44967168b9..f5b0b0040e 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidDataMerger.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidDataMerger.java
@@ -36,6 +36,17 @@ import java.util.logging.Logger;
/** Handles the Merging of ParsedAndroidData. */
class AndroidDataMerger {
+ public static class MergeConflictException extends RuntimeException {
+
+ private MergeConflictException(String message) {
+ super(message);
+ }
+
+ static MergeConflictException withMessage(String message) {
+ return new MergeConflictException(message);
+ }
+ }
+
private static final Logger logger = Logger.getLogger(AndroidDataMerger.class.getCanonicalName());
/** Interface for comparing paths. */
@@ -70,7 +81,7 @@ class AndroidDataMerger {
// getFileSize did not return correct size.
logger.severe(
String.format(
- "Filesystem size of %s (%s) or %s (%s) is inconsistant with bytes read %s.",
+ "Filesystem size of %s (%s) or %s (%s) is inconsistent with bytes read %s.",
one, one.getFileSize(), two, two.getFileSize(), bytesRead));
return false;
}
@@ -131,15 +142,15 @@ class AndroidDataMerger {
* ParsedAndroidData}.
*
* @see AndroidDataMerger#merge(ParsedAndroidData, ParsedAndroidData, UnvalidatedAndroidData,
- * boolean) for details.
+ * boolean, boolean) for details.
*/
UnwrittenMergedAndroidData loadAndMerge(
List<? extends SerializedAndroidData> transitive,
List<? extends SerializedAndroidData> direct,
ParsedAndroidData primary,
Path primaryManifest,
- boolean allowPrimaryOverrideAll)
- throws MergingException {
+ boolean allowPrimaryOverrideAll,
+ boolean throwOnResourceConflict) {
Stopwatch timer = Stopwatch.createStarted();
try {
logger.fine(
@@ -150,7 +161,8 @@ class AndroidDataMerger {
ParsedAndroidData.loadedFrom(direct, executorService, deserializer),
primary,
primaryManifest,
- allowPrimaryOverrideAll);
+ allowPrimaryOverrideAll,
+ throwOnResourceConflict);
} finally {
logger.fine(String.format("Resources merged in %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
}
@@ -218,20 +230,26 @@ class AndroidDataMerger {
* the ultimate source of truth, provided it doesn't conflict with itself.
* @return An UnwrittenMergedAndroidData, containing DataResource objects that can be written to
* disk for aapt processing or serialized for future merge passes.
- * @throws MergingException if there are merge conflicts or issues with parsing resources from
+ * @throws MergingException if there are issues with parsing resources from
* primaryData.
+ * @throws MergeConflictException if there are merge conflicts
*/
UnwrittenMergedAndroidData merge(
ParsedAndroidData transitive,
ParsedAndroidData direct,
UnvalidatedAndroidData primaryData,
- boolean allowPrimaryOverrideAll)
- throws MergingException {
+ boolean allowPrimaryOverrideAll,
+ boolean throwOnResourceConflict) {
try {
// Extract the primary resources.
ParsedAndroidData parsedPrimary = ParsedAndroidData.from(primaryData);
return doMerge(
- transitive, direct, parsedPrimary, primaryData.getManifest(), allowPrimaryOverrideAll);
+ transitive,
+ direct,
+ parsedPrimary,
+ primaryData.getManifest(),
+ allowPrimaryOverrideAll,
+ throwOnResourceConflict);
} catch (IOException e) {
throw MergingException.wrapException(e);
}
@@ -242,15 +260,14 @@ class AndroidDataMerger {
ParsedAndroidData direct,
ParsedAndroidData parsedPrimary,
Path primaryManifest,
- boolean allowPrimaryOverrideAll)
- throws MergingException {
+ boolean allowPrimaryOverrideAll,
+ boolean throwOnResourceConflict) {
try {
// Create the builders for the final parsed data.
final ParsedAndroidData.Builder primaryBuilder = ParsedAndroidData.Builder.newBuilder();
final ParsedAndroidData.Builder transitiveBuilder = ParsedAndroidData.Builder.newBuilder();
final KeyValueConsumers transitiveConsumers = transitiveBuilder.consumers();
final KeyValueConsumers primaryConsumers = primaryBuilder.consumers();
-
final Set<MergeConflict> conflicts = new HashSet<>();
// Find all internal conflicts.
@@ -286,7 +303,7 @@ class AndroidDataMerger {
if (allowPrimaryOverrideAll && parsedPrimary.containsOverwritable(entry.getKey())) {
continue;
}
- // If a transitive value is in the direct map report a conflict, as it is commonly
+ // If a transitive value is in the direct map, report a conflict, as it is commonly
// unintentional.
if (direct.containsOverwritable(entry.getKey())) {
conflicts.add(direct.foundResourceConflict(entry.getKey(), entry.getValue()));
@@ -367,8 +384,11 @@ class AndroidDataMerger {
}
}
if (!messages.isEmpty()) {
- // TODO(corysmith): Turn these into errors.
- logger.warning(Joiner.on("").join(messages));
+ String conflictMessage = Joiner.on("").join(messages);
+ if (throwOnResourceConflict) {
+ throw MergeConflictException.withMessage(conflictMessage);
+ }
+ logger.warning(conflictMessage);
}
}
return UnwrittenMergedAndroidData.of(
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMerger.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMerger.java
index 8fc6075382..eb7fd70711 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMerger.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMerger.java
@@ -64,8 +64,8 @@ public class AndroidResourceMerger {
final VariantType type,
@Nullable final Path symbolsOut,
@Nullable AndroidResourceClassWriter rclassWriter,
- AndroidDataDeserializer deserializer)
- throws MergingException {
+ AndroidDataDeserializer deserializer,
+ boolean throwOnResourceConflict) {
Stopwatch timer = Stopwatch.createStarted();
final ListeningExecutorService executorService =
MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(15));
@@ -78,7 +78,8 @@ public class AndroidResourceMerger {
primary,
primaryManifest,
type != VariantType.LIBRARY,
- deserializer);
+ deserializer,
+ throwOnResourceConflict);
timer.reset().start();
if (symbolsOut != null) {
AndroidDataSerializer serializer = AndroidDataSerializer.create();
@@ -114,14 +115,19 @@ public class AndroidResourceMerger {
ParsedAndroidData primary,
Path primaryManifest,
boolean allowPrimaryOverrideAll,
- AndroidDataDeserializer deserializer)
- throws MergingException {
+ AndroidDataDeserializer deserializer,
+ boolean throwOnResourceConflict) {
Stopwatch timer = Stopwatch.createStarted();
try {
AndroidDataMerger merger =
AndroidDataMerger.createWithPathDeduplictor(executorService, deserializer);
return merger.loadAndMerge(
- transitive, direct, primary, primaryManifest, allowPrimaryOverrideAll);
+ transitive,
+ direct,
+ primary,
+ primaryManifest,
+ allowPrimaryOverrideAll,
+ throwOnResourceConflict);
} finally {
logger.fine(String.format("merge finished in %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
}
@@ -140,8 +146,8 @@ public class AndroidResourceMerger {
@Nullable final PngCruncher cruncher,
final VariantType type,
@Nullable final Path symbolsOut,
- final List<String> filteredResources)
- throws MergingException {
+ final List<String> filteredResources,
+ boolean throwOnResourceConflict) {
try {
final ParsedAndroidData parsedPrimary = ParsedAndroidData.from(primary);
return mergeData(
@@ -155,7 +161,8 @@ public class AndroidResourceMerger {
type,
symbolsOut,
null /* rclassWriter */,
- AndroidDataDeserializer.withFilteredResources(filteredResources));
+ AndroidDataDeserializer.withFilteredResources(filteredResources),
+ throwOnResourceConflict);
} catch (IOException e) {
throw MergingException.wrapException(e);
}
@@ -175,8 +182,8 @@ public class AndroidResourceMerger {
@Nullable final PngCruncher cruncher,
final VariantType type,
@Nullable final Path symbolsOut,
- @Nullable final AndroidResourceClassWriter rclassWriter)
- throws MergingException {
+ @Nullable final AndroidResourceClassWriter rclassWriter,
+ boolean throwOnResourceConflict) {
final ParsedAndroidData.Builder primaryBuilder = ParsedAndroidData.Builder.newBuilder();
final AndroidDataDeserializer deserializer = AndroidDataDeserializer.create();
primary.deserialize(deserializer, primaryBuilder.consumers());
@@ -192,7 +199,8 @@ public class AndroidResourceMerger {
type,
symbolsOut,
rclassWriter,
- deserializer);
+ deserializer,
+ throwOnResourceConflict);
}
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMergingAction.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMergingAction.java
index 9459ae6d99..4506fae73e 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMergingAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceMergingAction.java
@@ -21,6 +21,7 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import com.google.common.base.Strings;
import com.google.common.io.Files;
+import com.google.devtools.build.android.AndroidDataMerger.MergeConflictException;
import com.google.devtools.build.android.AndroidResourceMerger.MergingException;
import com.google.devtools.build.android.AndroidResourceProcessor.AaptConfigOptions;
import com.google.devtools.build.android.Converters.ExistingPathConverter;
@@ -180,6 +181,14 @@ public class AndroidResourceMergingAction {
help = "Path to where data binding's layout info output should be written."
)
public Path dataBindingInfoOut;
+
+ @Option(name = "throwOnResourceConflict",
+ defaultValue = "false",
+ category = "config",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "If passed, resource merge conflicts will be treated as errors instead of warnings")
+ public boolean throwOnResourceConflict;
}
public static void main(String[] args) throws Exception {
@@ -223,7 +232,8 @@ public class AndroidResourceMergingAction {
new StubPngCruncher(),
packageType,
options.symbolsBinOut,
- resourceClassWriter);
+ resourceClassWriter,
+ options.throwOnResourceConflict);
logger.fine(String.format("Merging finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
@@ -268,6 +278,9 @@ public class AndroidResourceMergingAction {
String.format(
"Create resources.zip finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
}
+ } catch (MergeConflictException e) {
+ logger.log(Level.SEVERE, e.getMessage());
+ System.exit(1);
} catch (MergingException e) {
logger.log(Level.SEVERE, "Error during merging resources", e);
throw e;
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
index 13eb0f5c90..0dfd21e5f5 100644
--- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
+++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceProcessingAction.java
@@ -22,6 +22,7 @@ import com.android.ide.common.process.LoggedProcessOutputHandler;
import com.android.utils.StdLogger;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.android.AndroidDataMerger.MergeConflictException;
import com.google.devtools.build.android.AndroidResourceMerger.MergingException;
import com.google.devtools.build.android.AndroidResourceProcessor.AaptConfigOptions;
import com.google.devtools.build.android.AndroidResourceProcessor.FlagAaptOptions;
@@ -295,6 +296,14 @@ public class AndroidResourceProcessingAction {
help = "A list of resources that were filtered out in analysis."
)
public List<String> prefilteredResources;
+
+ @Option(name = "throwOnResourceConflict",
+ defaultValue = "false",
+ category = "config",
+ documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help = "If passed, resource merge conflicts will be treated as errors instead of warnings")
+ public boolean throwOnResourceConflict;
}
private static AaptConfigOptions aaptConfigOptions;
@@ -346,7 +355,8 @@ public class AndroidResourceProcessingAction {
selectPngCruncher(),
options.packageType,
options.symbolsOut,
- options.prefilteredResources);
+ options.prefilteredResources,
+ options.throwOnResourceConflict);
logger.fine(String.format("Merging finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
@@ -429,6 +439,9 @@ public class AndroidResourceProcessingAction {
}
logger.fine(
String.format("Packaging finished at %sms", timer.elapsed(TimeUnit.MILLISECONDS)));
+ } catch (MergeConflictException e) {
+ logger.severe(e.getMessage());
+ System.exit(1);
} catch (MergingException e) {
logger.log(java.util.logging.Level.SEVERE, "Error during merging resources", e);
throw e;