aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2016-11-30 22:40:59 +0000
committerGravatar Irina Iancu <elenairina@google.com>2016-12-01 10:17:49 +0000
commit3e8a15267e9c7fffb6a65ee63e4dd6ec10e522d5 (patch)
tree309cb0409b90107bb70a3b7f8b0f6c8d08b24a90
parent2a2be3907981d9654575493c7012d95d1241f373 (diff)
unknown commit of 5: Fixing the merge warning messages in transitive library overwrites.
Record the sources are overwritten during merging. -- MOS_MIGRATED_REVID=140654137
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/AndroidDataMerger.java63
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataAsset.java3
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataResource.java4
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java10
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataSource.java39
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataSourceTable.java48
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/DataValueFile.java17
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/MergeConflict.java15
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/ParsedAndroidData.java21
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/proto/serialize_format.proto2
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/xml/Namespaces.java10
11 files changed, 175 insertions, 57 deletions
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 3ee002a98d..34b424b521 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,9 +36,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
-/**
- * Handles the Merging of ParsedAndroidData.
- */
+/** Handles the Merging of ParsedAndroidData. */
public class AndroidDataMerger {
private static final Logger logger = Logger.getLogger(AndroidDataMerger.class.getCanonicalName());
@@ -128,7 +126,6 @@ public class AndroidDataMerger {
}
}
}
-
}
static class NoopSourceChecker implements SourceChecker {
@@ -152,8 +149,7 @@ public class AndroidDataMerger {
/** Creates a merger with a custom deduplicator and a default {@link ExecutorService}. */
public static AndroidDataMerger createWithDefaultThreadPool(SourceChecker deDuplicator) {
- return new AndroidDataMerger(deDuplicator,
- MoreExecutors.newDirectExecutorService());
+ return new AndroidDataMerger(deDuplicator, MoreExecutors.newDirectExecutorService());
}
/** Creates a merger with a custom deduplicator and an {@link ExecutorService}. */
@@ -224,18 +220,20 @@ public class AndroidDataMerger {
/**
* Merges DataResources into an UnwrittenMergedAndroidData.
- * <p>
- * This method has two basic states, library and binary. These are distinguished by
+ *
+ * <p>This method has two basic states, library and binary. These are distinguished by
* allowPrimaryOverrideAll, which allows the primary data to overwrite any value in the closure, a
* trait associated with binaries, as a binary is a leaf node. The other semantics are slightly
* more complicated: a given resource can be overwritten only if it resides in the direct
* dependencies of primary data. This forces an explicit simple priority for each resource,
* instead of the more subtle semantics of multiple layers of libraries with potential overwrites.
- * <p>
- * The UnwrittenMergedAndroidData contains only one of each DataKey in both the direct and
+ *
+ * <p>The UnwrittenMergedAndroidData contains only one of each DataKey in both the direct and
* transitive closure.
*
- * The merge semantics for overwriting resources (non id and styleable) are as follows: <pre>
+ * <p>The merge semantics for overwriting resources (non id and styleable) are as follows:
+ *
+ * <pre>
* Key:
* A(): package A
* A(foo): package A with resource symbol foo
@@ -266,22 +264,24 @@ public class AndroidDataMerger {
* A(foo),B(foo) -> C() -> D(foo) == Valid
* A() -> B(foo),C(foo) -> D(foo) == Valid
* </pre>
- * <p>
- * Combining resources are much simpler -- since a combining (id and styleable) resource does not
- * get replaced when redefined, they are simply combined: <pre>
+ *
+ * <p>Combining resources are much simpler -- since a combining (id and styleable) resource does
+ * not get replaced when redefined, they are simply combined:
+ *
+ * <pre>
* A(foo) -> B(foo) -> C(foo) == Valid
- *
+ *
* </pre>
- *
+ *
* @param transitive The transitive dependencies to merge.
* @param direct The direct dependencies to merge.
* @param primaryData The primary data to merge against.
* @param allowPrimaryOverrideAll Boolean that indicates if the primary data will be considered
- * the ultimate source of truth, provided it doesn't conflict with itself.
+ * 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.
+ * disk for aapt processing or serialized for future merge passes.
* @throws MergingException if there are merge conflicts or issues with parsing resources from
- * primaryData.
+ * primaryData.
*/
UnwrittenMergedAndroidData merge(
ParsedAndroidData transitive,
@@ -314,6 +314,8 @@ public class AndroidDataMerger {
final KeyValueConsumers primaryConsumers = primaryBuilder.consumers();
final Set<MergeConflict> conflicts = new HashSet<>();
+
+ // Find all internal conflicts.
conflicts.addAll(parsedPrimary.conflicts());
for (MergeConflict conflict : Iterables.concat(direct.conflicts(), transitive.conflicts())) {
if (allowPrimaryOverrideAll
@@ -326,7 +328,12 @@ public class AndroidDataMerger {
// overwriting resources
for (Entry<DataKey, DataResource> entry : parsedPrimary.iterateOverwritableEntries()) {
- primaryConsumers.overwritingConsumer.consume(entry.getKey(), entry.getValue());
+ if (direct.containsOverwritable(entry.getKey())) {
+ primaryConsumers.overwritingConsumer.consume(
+ entry.getKey(), entry.getValue().overwrite(direct.getOverwritable(entry.getKey())));
+ } else {
+ primaryConsumers.overwritingConsumer.consume(entry.getKey(), entry.getValue());
+ }
}
for (Map.Entry<DataKey, DataResource> entry : direct.iterateOverwritableEntries()) {
@@ -379,7 +386,12 @@ public class AndroidDataMerger {
// assets
for (Entry<DataKey, DataAsset> entry : parsedPrimary.iterateAssetEntries()) {
- primaryConsumers.assetConsumer.consume(entry.getKey(), entry.getValue());
+ if (direct.containsAsset(entry.getKey())) {
+ primaryConsumers.assetConsumer.consume(
+ entry.getKey(), entry.getValue().overwrite(direct.getAsset(entry.getKey())));
+ } else {
+ primaryConsumers.assetConsumer.consume(entry.getKey(), entry.getValue());
+ }
}
for (Map.Entry<DataKey, DataAsset> entry : direct.iterateAssetEntries()) {
@@ -412,9 +424,7 @@ public class AndroidDataMerger {
if (!conflicts.isEmpty()) {
List<String> messages = new ArrayList<>();
for (MergeConflict conflict : conflicts) {
- if (!conflict.first().equals(conflict.second())
- && !deDuplicator.checkEquality(
- conflict.first().source(), conflict.second().source())) {
+ if (conflict.isValidWith(deDuplicator)) {
messages.add(conflict.toConflictMessage());
}
}
@@ -423,11 +433,8 @@ public class AndroidDataMerger {
logger.warning(Joiner.on("").join(messages));
}
}
-
return UnwrittenMergedAndroidData.of(
- primaryManifest,
- primaryBuilder.build(),
- transitiveBuilder.build());
+ primaryManifest, primaryBuilder.build(), transitiveBuilder.build());
} catch (IOException e) {
throw MergingException.wrapException(e).build();
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/DataAsset.java b/src/tools/android/java/com/google/devtools/build/android/DataAsset.java
index cbb2f77101..b78b07fd30 100644
--- a/src/tools/android/java/com/google/devtools/build/android/DataAsset.java
+++ b/src/tools/android/java/com/google/devtools/build/android/DataAsset.java
@@ -24,4 +24,7 @@ public interface DataAsset extends DataValue {
*/
void writeAsset(RelativeAssetPath key, AndroidDataWritingVisitor mergedDataWriter)
throws IOException;
+
+ /** Overwrite another {@link DataAsset}. */
+ DataAsset overwrite(DataAsset other);
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/DataResource.java b/src/tools/android/java/com/google/devtools/build/android/DataResource.java
index d1836b8a13..b177b9e49b 100644
--- a/src/tools/android/java/com/google/devtools/build/android/DataResource.java
+++ b/src/tools/android/java/com/google/devtools/build/android/DataResource.java
@@ -14,7 +14,6 @@
package com.google.devtools.build.android;
import com.android.ide.common.res2.MergingException;
-
import java.io.IOException;
/**
@@ -42,4 +41,7 @@ public interface DataResource extends DataValue {
void writeResourceToClass(
FullyQualifiedName key,
AndroidResourceClassWriter resourceClassWriter);
+
+ /** Overwrite another {@link DataResource}. */
+ DataResource overwrite(DataResource other);
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java b/src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java
index 1fbbd8c81a..39f230dbec 100644
--- a/src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java
+++ b/src/tools/android/java/com/google/devtools/build/android/DataResourceXml.java
@@ -311,12 +311,16 @@ public class DataResourceXml implements DataResource {
}
DataResourceXml xmlResource = (DataResourceXml) resource;
return createWithNamespaces(
- combineSources(xmlResource.source),
+ source.combine(xmlResource.source),
xml.combineWith(xmlResource.xml),
namespaces.union(xmlResource.namespaces));
}
- private DataSource combineSources(DataSource otherSource) {
- return source.combine(otherSource);
+ @Override
+ public DataResource overwrite(DataResource resource) {
+ if (equals(resource)) {
+ return this;
+ }
+ return createWithNamespaces(source.overwrite(resource.source()), xml, namespaces);
}
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/DataSource.java b/src/tools/android/java/com/google/devtools/build/android/DataSource.java
index 5c51ea729a..1e4580eec9 100644
--- a/src/tools/android/java/com/google/devtools/build/android/DataSource.java
+++ b/src/tools/android/java/com/google/devtools/build/android/DataSource.java
@@ -14,7 +14,10 @@
package com.google.devtools.build.android;
import com.android.SdkConstants;
+import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.android.proto.SerializeFormat.ProtoSource;
import java.io.BufferedInputStream;
import java.io.IOException;
@@ -32,14 +35,16 @@ public class DataSource implements Comparable<DataSource> {
return of(path);
}
- public static DataSource of(Path source) {
- return new DataSource(source);
+ public static DataSource of(Path sourcePath) {
+ return new DataSource(sourcePath, ImmutableSet.<DataSource>of());
}
private final Path path;
+ private final ImmutableSet<DataSource> overrides;
- private DataSource(Path path) {
+ private DataSource(Path path, ImmutableSet<DataSource> overrides) {
this.path = path;
+ this.overrides = overrides;
}
public Path getPath() {
@@ -58,12 +63,13 @@ public class DataSource implements Comparable<DataSource> {
if (o == null || getClass() != o.getClass()) {
return false;
}
- return Objects.equal(path, ((DataSource) o).path);
+ return Objects.equal(path, ((DataSource) o).path)
+ && Objects.equal(overrides, ((DataSource) o).overrides);
}
@Override
public int hashCode() {
- return Objects.hashCode(path);
+ return Objects.hashCode(path, overrides);
}
@Override
@@ -92,7 +98,30 @@ public class DataSource implements Comparable<DataSource> {
return this;
}
+ public DataSource overwrite(DataSource... sources) {
+ ImmutableSet<DataSource> overrides =
+ ImmutableSet.<DataSource>builder().addAll(this.overrides).add(sources).build();
+ Preconditions.checkArgument(!overrides.contains(this));
+ return new DataSource(path, overrides);
+ }
+
+ public ImmutableSet<DataSource> overrides() {
+ return overrides;
+ }
+
public boolean isInValuesFolder() {
return path.getParent().getFileName().toString().startsWith(SdkConstants.FD_RES_VALUES);
}
+
+ public boolean hasOveridden(DataSource source) {
+ return overrides.contains(source);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("path", path)
+ .add("overrides", overrides)
+ .toString();
+ }
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/DataSourceTable.java b/src/tools/android/java/com/google/devtools/build/android/DataSourceTable.java
index 0a26fc092a..53be389d9f 100644
--- a/src/tools/android/java/com/google/devtools/build/android/DataSourceTable.java
+++ b/src/tools/android/java/com/google/devtools/build/android/DataSourceTable.java
@@ -13,6 +13,10 @@
// limitations under the License.
package com.google.devtools.build.android;
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.android.proto.SerializeFormat;
import com.google.devtools.build.android.proto.SerializeFormat.Header;
import com.google.devtools.build.android.proto.SerializeFormat.ProtoSource;
@@ -20,18 +24,26 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.FileSystem;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
-
/**
* Tracks mappings from resource source paths (/foo/bar/res/values/colors.xml) to an ID for a more
* compact serialization format.
*/
class DataSourceTable {
- private final Map<DataSource, Integer> sourceTable = new HashMap<>();
+ private static final Function<DataValue, DataSource> VALUE_TO_SOURCE =
+ new Function<DataValue, DataSource>() {
+ @Override
+ public DataSource apply(DataValue input) {
+ return input.source();
+ }
+ };
+ private final Map<DataSource, Integer> sourceTable = new LinkedHashMap<>();
private DataSource[] idToSource;
/**
@@ -60,17 +72,35 @@ class DataSourceTable {
private void writeSourceInfo(NavigableMap<DataKey, DataValue> map, OutputStream outStream)
throws IOException {
int sourceNumber = 0;
- for (Map.Entry<DataKey, DataValue> entry : map.entrySet()) {
- DataSource source = entry.getValue().source();
+ LinkedList<DataSource> sourceQueue =
+ new LinkedList<>(Collections2.transform(map.values(), VALUE_TO_SOURCE));
+ while (!sourceQueue.isEmpty()) {
+ DataSource source = sourceQueue.pop();
if (!sourceTable.containsKey(source)) {
sourceTable.put(source, sourceNumber);
++sourceNumber;
- ProtoSource.newBuilder()
- .setFilename(source.getPath().toString())
- .build()
- .writeDelimitedTo(outStream);
+ sourceQueue.addAll(source.overrides());
+ }
+ }
+ for (DataSource dataSource : sourceTable.keySet()) {
+ ProtoSource.newBuilder()
+ .setFilename(dataSource.getPath().toString())
+ .addAllOverwritten(sourcesToIds(dataSource.overrides()))
+ .build()
+ .writeDelimitedTo(outStream);
+ }
+ }
+
+ private List<Integer> sourcesToIds(ImmutableSet<DataSource> overrides) {
+ ImmutableList.Builder<Integer> idsBuilder = ImmutableList.builder();
+ for (DataSource dataSource : overrides) {
+ if (!sourceTable.containsKey(dataSource)) {
+ throw new IllegalArgumentException(
+ "Cannot find data source: " + dataSource.toString() + " in " + sourceTable.keySet());
}
+ idsBuilder.add(sourceTable.get(dataSource));
}
+ return idsBuilder.build();
}
/** Fill in the serialize format header information required to deserialize */
diff --git a/src/tools/android/java/com/google/devtools/build/android/DataValueFile.java b/src/tools/android/java/com/google/devtools/build/android/DataValueFile.java
index a54c8ef6da..b24d1278af 100644
--- a/src/tools/android/java/com/google/devtools/build/android/DataValueFile.java
+++ b/src/tools/android/java/com/google/devtools/build/android/DataValueFile.java
@@ -42,6 +42,7 @@ public class DataValueFile implements DataResource, DataAsset {
public static DataValueFile of(DataSource source) {
return new DataValueFile(source);
}
+
/**
* Creates a {@link DataValueFile} from a {@link SerializeFormat.DataValue}.
*/
@@ -101,6 +102,22 @@ public class DataValueFile implements DataResource, DataAsset {
}
@Override
+ public DataResource overwrite(DataResource resource) {
+ if (equals(resource)) {
+ return this;
+ }
+ return of(source.overwrite(resource.source()));
+ }
+
+ @Override
+ public DataAsset overwrite(DataAsset asset) {
+ if (equals(asset)) {
+ return this;
+ }
+ return of(source.overwrite(asset.source()));
+ }
+
+ @Override
public void writeResourceToClass(FullyQualifiedName key,
AndroidResourceClassWriter resourceClassWriter) {
resourceClassWriter.writeSimpleResource(key.type(), key.name());
diff --git a/src/tools/android/java/com/google/devtools/build/android/MergeConflict.java b/src/tools/android/java/com/google/devtools/build/android/MergeConflict.java
index cbaa642fab..215c895550 100644
--- a/src/tools/android/java/com/google/devtools/build/android/MergeConflict.java
+++ b/src/tools/android/java/com/google/devtools/build/android/MergeConflict.java
@@ -13,12 +13,12 @@
// limitations under the License.
package com.google.devtools.build.android;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-
import com.android.annotations.VisibleForTesting;
import com.android.annotations.concurrency.Immutable;
-
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import com.google.devtools.build.android.AndroidDataMerger.SourceChecker;
+import java.io.IOException;
import java.util.Objects;
/**
@@ -82,6 +82,13 @@ public class MergeConflict {
return second;
}
+ boolean isValidWith(SourceChecker checker) throws IOException {
+ return !first.equals(second)
+ && !first.source().hasOveridden(second.source())
+ && !second.source().hasOveridden(first.source())
+ && !checker.checkEquality(first.source(), second.source());
+ }
+
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
diff --git a/src/tools/android/java/com/google/devtools/build/android/ParsedAndroidData.java b/src/tools/android/java/com/google/devtools/build/android/ParsedAndroidData.java
index 2470a3f87b..e1d29d0515 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ParsedAndroidData.java
+++ b/src/tools/android/java/com/google/devtools/build/android/ParsedAndroidData.java
@@ -176,10 +176,15 @@ public class ParsedAndroidData {
@Override
public void consume(K key, V value) {
if (target.containsKey(key)) {
- conflicts.add(MergeConflict.between(key, value, target.get(key)));
+ V other = target.get(key);
+ conflicts.add(MergeConflict.between(key, value, other));
+ if (!other.source().hasOveridden(value.source())) {
+ // Only replace it if the previous value has explicitly replaced the current.
+ target.put(key, value);
+ }
+ } else {
+ target.put(key, value);
}
- // Always record the value, conflict or not, to maintain backwards compatibility.
- target.put(key, value);
}
}
@@ -463,13 +468,17 @@ public class ParsedAndroidData {
return assets;
}
- boolean containsOverwritable(DataKey name) {
+ public boolean containsOverwritable(DataKey name) {
return overwritingResources.containsKey(name);
}
public boolean containsCombineable(DataKey key) {
return combiningResources.containsKey(key);
}
+
+ public DataResource getOverwritable(DataKey name) {
+ return overwritingResources.get(name);
+ }
Iterable<Entry<DataKey, DataResource>> iterateOverwritableEntries() {
return overwritingResources.entrySet();
@@ -503,4 +512,8 @@ public class ParsedAndroidData {
ImmutableSet<MergeConflict> conflicts() {
return conflicts;
}
+
+ public DataAsset getAsset(DataKey key) {
+ return assets.get(key);
+ }
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/proto/serialize_format.proto b/src/tools/android/java/com/google/devtools/build/android/proto/serialize_format.proto
index 3c24f5f54d..122d3569da 100644
--- a/src/tools/android/java/com/google/devtools/build/android/proto/serialize_format.proto
+++ b/src/tools/android/java/com/google/devtools/build/android/proto/serialize_format.proto
@@ -58,6 +58,8 @@ message DataValue {
message ProtoSource {
// Required
optional string filename = 1;
+ // The indexes of sources this source replaces.
+ repeated uint32 overwritten = 2;
}
// The container for a serialized xml value.
diff --git a/src/tools/android/java/com/google/devtools/build/android/xml/Namespaces.java b/src/tools/android/java/com/google/devtools/build/android/xml/Namespaces.java
index cbaabea740..27947d6e01 100644
--- a/src/tools/android/java/com/google/devtools/build/android/xml/Namespaces.java
+++ b/src/tools/android/java/com/google/devtools/build/android/xml/Namespaces.java
@@ -13,6 +13,7 @@
// limitations under the License.
package com.google.devtools.build.android.xml;
+import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.android.DataResourceXml;
import com.google.devtools.build.android.XmlResourceValue;
@@ -43,9 +44,7 @@ public class Namespaces implements Iterable<Entry<String, String>> {
private static final Namespaces EMPTY_INSTANCE =
new Namespaces(ImmutableMap.<String, String>of());
- /**
- * Collects prefix and uri pairs from elements.
- */
+ /** Collects prefix and uri pairs from elements. */
public static class Collector {
private Map<String, String> prefixToUri = new HashMap<>();
@@ -149,6 +148,11 @@ public class Namespaces implements Iterable<Entry<String, String>> {
}
@Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass()).add("prefixToUri", prefixToUri).toString();
+ }
+
+ @Override
public boolean equals(Object obj) {
if (obj instanceof Namespaces) {
Namespaces other = (Namespaces) obj;