aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar janakr <janakr@google.com>2018-03-06 14:06:56 -0800
committerGravatar Copybara-Service <copybara-piper@google.com>2018-03-06 14:10:04 -0800
commita03a9c3b76f3d43757f2dc3b9e262ed7ac671b15 (patch)
tree49d26752783b8e9760bf2148c65d98d0895cc6c3 /src
parentfcb67e94b54b8ee55563bc75b5ae2d21295d7260 (diff)
Add proper serialization constructor and equals/hashCode for EnvironmentLabels.
PiperOrigin-RevId: 188078054
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/constraints/EnvironmentCollection.java10
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/EnvironmentGroup.java34
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/EnvironmentLabels.java119
3 files changed, 140 insertions, 23 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/constraints/EnvironmentCollection.java b/src/main/java/com/google/devtools/build/lib/analysis/constraints/EnvironmentCollection.java
index c1ce9f0a1a..430078eb62 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/constraints/EnvironmentCollection.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/constraints/EnvironmentCollection.java
@@ -15,6 +15,7 @@
package com.google.devtools.build.lib.analysis.constraints;
import com.google.auto.value.AutoValue;
+import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
@@ -89,6 +90,15 @@ public class EnvironmentCollection {
/** An empty collection. */
static final EnvironmentCollection EMPTY = new EnvironmentCollection(ImmutableMultimap.of());
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("size", map.size())
+ .add("hashCode", map.hashCode())
+ .add("map", map)
+ .toString();
+ }
+
/**
* Builder for {@link EnvironmentCollection}.
*/
diff --git a/src/main/java/com/google/devtools/build/lib/packages/EnvironmentGroup.java b/src/main/java/com/google/devtools/build/lib/packages/EnvironmentGroup.java
index a0e8b23ece..34090af5e1 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/EnvironmentGroup.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/EnvironmentGroup.java
@@ -15,7 +15,6 @@
package com.google.devtools.build.lib.packages;
import com.google.common.base.Predicate;
-import com.google.common.base.Verify;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
@@ -29,28 +28,26 @@ import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.Location;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Model for the "environment_group' rule: the piece of Bazel's rule constraint system that binds
- * thematically related environments together and determines which environments a rule supports
- * by default. See {@link com.google.devtools.build.lib.analysis.constraints.ConstraintSemantics}
- * for precise semantic details of how this information is used.
+ * thematically related environments together and determines which environments a rule supports by
+ * default. See {@link com.google.devtools.build.lib.analysis.constraints.ConstraintSemantics} for
+ * precise semantic details of how this information is used.
*
* <p>Note that "environment_group" is implemented as a loading-time function, not a rule. This is
- * to support proper discovery of defaults: Say rule A has no explicit constraints and depends
- * on rule B, which is explicitly constrained to environment ":bar". Since A declares nothing
- * explicitly, it's implicitly constrained to DEFAULTS (whatever that is). Therefore, the
- * dependency is only allowed if DEFAULTS doesn't include environments beyond ":bar". To figure
- * that out, we need to be able to look up the environment group for ":bar", which is what this
- * class provides.
+ * to support proper discovery of defaults: Say rule A has no explicit constraints and depends on
+ * rule B, which is explicitly constrained to environment ":bar". Since A declares nothing
+ * explicitly, it's implicitly constrained to DEFAULTS (whatever that is). Therefore, the dependency
+ * is only allowed if DEFAULTS doesn't include environments beyond ":bar". To figure that out, we
+ * need to be able to look up the environment group for ":bar", which is what this class provides.
*
- * <p>If we implemented this as a rule, we'd have to provide that lookup via rule dependencies,
- * e.g. something like:
- *
- * <code>
+ * <p>If we implemented this as a rule, we'd have to provide that lookup via rule dependencies, e.g.
+ * something like: <code>
* environment(
* name = 'bar',
* group = [':sample_environments'],
@@ -62,7 +59,7 @@ import java.util.Set;
* to determine what other environments belong to the group is to have the group somehow reference
* them. That would produce circular dependencies in the build graph, which is no good.
*/
-@Immutable
+@Immutable // This is a lie, but this object is only mutable until its containing package is loaded.
public class EnvironmentGroup implements Target {
private final EnvironmentLabels environmentLabels;
private final Location location;
@@ -106,6 +103,7 @@ public class EnvironmentGroup implements Target {
}
public EnvironmentLabels getEnvironmentLabels() {
+ environmentLabels.checkInitialized();
return environmentLabels;
}
@@ -167,15 +165,17 @@ public class EnvironmentGroup implements Target {
}
}
+ Map<Label, NestedSet<Label>> fulfillersMap = new HashMap<>();
// Now that we know which environments directly fulfill each other, compute which environments
// transitively fulfill each other. We could alternatively compute this on-demand, but since
// we don't expect these chains to be very large we opt toward computing them once at package
// load time.
- Verify.verify(environmentLabels.fulfillersMap.isEmpty());
+ environmentLabels.assertNotInitialized();
for (Label envName : environmentLabels.environments) {
- setTransitiveFulfillers(envName, directFulfillers, environmentLabels.fulfillersMap);
+ setTransitiveFulfillers(envName, directFulfillers, fulfillersMap);
}
+ environmentLabels.setFulfillersMap(fulfillersMap);
return events;
}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/EnvironmentLabels.java b/src/main/java/com/google/devtools/build/lib/packages/EnvironmentLabels.java
index e756a8e2a9..c375c7a229 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/EnvironmentLabels.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/EnvironmentLabels.java
@@ -14,19 +14,26 @@
package com.google.devtools.build.lib.packages;
-import com.google.common.base.Verify;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import java.util.Collection;
-import java.util.HashMap;
+import java.util.Collections;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/**
* Parts of an {@link EnvironmentGroup} that are needed for analysis. Since {@link EnvironmentGroup}
* keeps a reference to a {@link Package} object, it is too heavyweight to store in analysis.
+ *
+ * <p>Constructor should only be called by {@link EnvironmentGroup}, and this object must never be
+ * accessed externally until after {@link EnvironmentGroup#processMemberEnvironments} is called. The
+ * mutability of fulfillersMap means that we must take care to wait until it is set before doing
+ * anything with this class.
*/
@AutoCodec
public class EnvironmentLabels {
@@ -35,22 +42,52 @@ public class EnvironmentLabels {
final ImmutableSet<Label> defaults;
/**
* Maps a member environment to the set of environments that directly fulfill it. Note that we
- * can't populate this map until all Target instances for member environments have been
- * initialized, which may occur after group instantiation (this makes the class mutable).
+ * can't set this map until all Target instances for member environments have been initialized,
+ * which occurs after group instantiation (this makes the class mutable).
*/
- final Map<Label, NestedSet<Label>> fulfillersMap = new HashMap<>();
+ private Map<Label, NestedSet<Label>> fulfillersMap = null;
EnvironmentLabels(Label label, Collection<Label> environments, Collection<Label> defaults) {
+ this(label, environments, defaults, null);
+ }
+
+ /**
+ * Only for use by serialization: the mutable fulfillersMap object is not properly initialized
+ * otherwise during deserialization.
+ */
+ @AutoCodec.VisibleForSerialization
+ @AutoCodec.Instantiator
+ EnvironmentLabels(
+ Label label,
+ Collection<Label> environments,
+ Collection<Label> defaults,
+ Map<Label, NestedSet<Label>> fulfillersMap) {
this.label = label;
this.environments = ImmutableSet.copyOf(environments);
this.defaults = ImmutableSet.copyOf(defaults);
+ this.fulfillersMap = fulfillersMap;
+ }
+
+ void assertNotInitialized() {
+ Preconditions.checkState(fulfillersMap == null, this);
+ }
+
+ void checkInitialized() {
+ Preconditions.checkNotNull(fulfillersMap, this);
+ }
+
+ void setFulfillersMap(Map<Label, NestedSet<Label>> fulfillersMap) {
+ Preconditions.checkState(this.fulfillersMap == null, this);
+ this.fulfillersMap = Collections.unmodifiableMap(fulfillersMap);
}
public Set<Label> getEnvironments() {
+ checkInitialized();
return environments;
}
public Set<Label> getDefaults() {
+ checkInitialized();
return defaults;
}
@@ -59,6 +96,7 @@ public class EnvironmentLabels {
* belong to this group.
*/
public boolean isDefault(Label environment) {
+ checkInitialized();
return defaults.contains(environment);
}
@@ -73,10 +111,79 @@ public class EnvironmentLabels {
* <p>If no environments fulfill the input, returns an empty set.
*/
public Iterable<Label> getFulfillers(Label environment) {
- return Verify.verifyNotNull(fulfillersMap.get(environment));
+ checkInitialized();
+ return fulfillersMap.get(environment);
}
public Label getLabel() {
+ checkInitialized();
return label;
}
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("label", label)
+ .add("sizes", environments.size() + ", " + defaults.size() + ", " + fulfillersMap.size())
+ .add("environments", environments)
+ .add("defaults", defaults)
+ .add("fulfillersMap", fulfillersMap)
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ checkInitialized();
+ return Objects.hash(label, environments, defaults, fulfillersMap.keySet());
+ }
+
+ /**
+ * Compares {@code map1} and {@code map2} using deep equality for their values. Should be feasible
+ * because comparison will usually only happen between == objects, so this is hit rarely. If
+ * objects are equal, but have been deserialized separately so not ==, this should still be ok
+ * because these nested sets are not particularly big, and there are very few EnvironmentGroups
+ * (and therefore EnvironmentLabels) in any given build.
+ *
+ * <p>This will have to be revisited if it turns out to be noticeably expensive. It should be
+ * sound to not compare the values of the fulfillerMaps at all, since they are determined from the
+ * package each EnvironmentLabel is associated with, and so as long as EnvironmentLabels from
+ * different source states but the same package are not compared, the values shouldn't be
+ * necessary.
+ */
+ private static boolean fulfillerMapsEqual(
+ Map<Label, NestedSet<Label>> map1, Map<Label, NestedSet<Label>> map2) {
+ if (map1 == map2) {
+ return true;
+ }
+ if (map1.size() != map2.size()) {
+ return false;
+ }
+ for (Map.Entry<Label, NestedSet<Label>> entry : map1.entrySet()) {
+ NestedSet<Label> secondValue = map2.get(entry.getKey());
+ // Do shallowEquals check first for speed.
+ if (secondValue == null
+ || (!entry.getValue().shallowEquals(secondValue)
+ && !entry.getValue().toCollection().equals(secondValue.toCollection()))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ checkInitialized();
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ EnvironmentLabels that = (EnvironmentLabels) o;
+ that.checkInitialized();
+ return label.equals(that.label)
+ && environments.equals(that.environments)
+ && defaults.equals(that.defaults)
+ && fulfillerMapsEqual(this.fulfillersMap, that.fulfillersMap);
+ }
}