Store the hash code of the Environment in the RuleClass object. When a RuleClass is deserialized as part of a Skylark rule, the Environment is currently not present, but it is needed to detect changes to the rule.
Also precompute and store the Environment's hash code, and do a drive-by clean-up of a bunch of warnings in the Environment code.
--
MOS_MIGRATED_REVID=122838588
diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
index a9c617e..f01026b 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
@@ -102,8 +102,8 @@
*/
@Immutable
public final class RuleClass {
- public static final Function<? super Rule, Map<String, Label>> NO_EXTERNAL_BINDINGS =
- Functions.<Map<String, Label>>constant(ImmutableMap.<String, Label>of());
+ static final Function<? super Rule, Map<String, Label>> NO_EXTERNAL_BINDINGS =
+ Functions.<Map<String, Label>>constant(ImmutableMap.<String, Label>of());
public static final PathFragment THIRD_PARTY_PREFIX = new PathFragment("third_party");
@@ -481,7 +481,8 @@
private BaseFunction configuredTargetFunction = null;
private Function<? super Rule, Map<String, Label>> externalBindingsFunction =
NO_EXTERNAL_BINDINGS;
- private Environment ruleDefinitionEnvironment = null;
+ @Nullable private Environment ruleDefinitionEnvironment = null;
+ @Nullable private String ruleDefinitionEnvironmentHashCode = null;
private ConfigurationFragmentPolicy.Builder configurationFragmentPolicy =
new ConfigurationFragmentPolicy.Builder();
@@ -503,6 +504,7 @@
this.name = name;
this.skylark = skylark;
this.type = type;
+ Preconditions.checkState(skylark || type != RuleClassType.PLACEHOLDER, name);
this.documented = type != RuleClassType.ABSTRACT;
for (RuleClass parent : parents) {
if (parent.getValidityPredicate() != PredicatesWithMessage.<Rule>alwaysTrue()) {
@@ -569,12 +571,32 @@
Preconditions.checkState(skylarkExecutable == (ruleDefinitionEnvironment != null));
Preconditions.checkState(externalBindingsFunction == NO_EXTERNAL_BINDINGS);
}
- return new RuleClass(name, skylark, skylarkExecutable, documented, publicByDefault,
- binaryOutput, workspaceOnly, outputsDefaultExecutable, implicitOutputsFunction,
- configurator, configuredTargetFactory, validityPredicate, preferredDependencyPredicate,
- ImmutableSet.copyOf(advertisedProviders), canHaveAnyProvider, configuredTargetFunction,
- externalBindingsFunction, ruleDefinitionEnvironment, configurationFragmentPolicy.build(),
- supportsConstraintChecking, attributes.values().toArray(new Attribute[0]));
+ if (type == RuleClassType.PLACEHOLDER) {
+ Preconditions.checkNotNull(ruleDefinitionEnvironmentHashCode, this.name);
+ }
+ return new RuleClass(
+ name,
+ skylark,
+ skylarkExecutable,
+ documented,
+ publicByDefault,
+ binaryOutput,
+ workspaceOnly,
+ outputsDefaultExecutable,
+ implicitOutputsFunction,
+ configurator,
+ configuredTargetFactory,
+ validityPredicate,
+ preferredDependencyPredicate,
+ ImmutableSet.copyOf(advertisedProviders),
+ canHaveAnyProvider,
+ configuredTargetFunction,
+ externalBindingsFunction,
+ ruleDefinitionEnvironment,
+ ruleDefinitionEnvironmentHashCode,
+ configurationFragmentPolicy.build(),
+ supportsConstraintChecking,
+ attributes.values().toArray(new Attribute[0]));
}
/**
@@ -814,11 +836,19 @@
return this;
}
- /**
- * Sets the rule definition environment. Meant for Skylark usage.
- */
+ /** Sets the rule definition environment. Meant for Skylark usage. */
public Builder setRuleDefinitionEnvironment(Environment env) {
- this.ruleDefinitionEnvironment = env;
+ this.ruleDefinitionEnvironment = Preconditions.checkNotNull(env, this.name);
+ this.ruleDefinitionEnvironmentHashCode =
+ this.ruleDefinitionEnvironment.getTransitiveContentHashCode();
+ return this;
+ }
+
+ /** Sets the rule definition environment hash code for deserialized rule classes. */
+ Builder setRuleDefinitionEnvironmentHashCode(String hashCode) {
+ Preconditions.checkState(ruleDefinitionEnvironment == null, ruleDefinitionEnvironment);
+ Preconditions.checkState(type == RuleClassType.PLACEHOLDER, type);
+ this.ruleDefinitionEnvironmentHashCode = Preconditions.checkNotNull(hashCode, this.name);
return this;
}
@@ -900,8 +930,8 @@
}
}
- public static Builder createPlaceholderBuilder(final String name, final Location ruleLocation,
- ImmutableList<RuleClass> parents) {
+ static Builder createPlaceholderBuilder(
+ final String name, final Location ruleLocation, ImmutableList<RuleClass> parents) {
return new Builder(name, RuleClassType.PLACEHOLDER, /*skylark=*/true,
parents.toArray(new RuleClass[parents.size()])).factory(
new ConfiguredTargetFactory<Object, Object>() {
@@ -996,6 +1026,7 @@
* Null for non Skylark executable RuleClasses.
*/
@Nullable private final Environment ruleDefinitionEnvironment;
+ @Nullable private final String ruleDefinitionEnvironmentHashCode;
/**
* The set of configuration fragments which are legal for this rule's implementation to access.
@@ -1029,21 +1060,28 @@
* <p>The {@code depsAllowedRules} predicate should have a {@code toString}
* method which returns a plain English enumeration of the allowed rule class
* names, if it does not allow all rule classes.
- * @param workspaceOnly
*/
@VisibleForTesting
- RuleClass(String name,
- boolean isSkylark, boolean skylarkExecutable, boolean documented, boolean publicByDefault,
- boolean binaryOutput, boolean workspaceOnly, boolean outputsDefaultExecutable,
+ RuleClass(
+ String name,
+ boolean isSkylark,
+ boolean skylarkExecutable,
+ boolean documented,
+ boolean publicByDefault,
+ boolean binaryOutput,
+ boolean workspaceOnly,
+ boolean outputsDefaultExecutable,
ImplicitOutputsFunction implicitOutputsFunction,
Configurator<?, ?> configurator,
ConfiguredTargetFactory<?, ?> configuredTargetFactory,
- PredicateWithMessage<Rule> validityPredicate, Predicate<String> preferredDependencyPredicate,
+ PredicateWithMessage<Rule> validityPredicate,
+ Predicate<String> preferredDependencyPredicate,
ImmutableSet<Class<?>> advertisedProviders,
boolean canHaveAnyProvider,
@Nullable BaseFunction configuredTargetFunction,
Function<? super Rule, Map<String, Label>> externalBindingsFunction,
@Nullable Environment ruleDefinitionEnvironment,
+ String ruleDefinitionEnvironmentHashCode,
ConfigurationFragmentPolicy configurationFragmentPolicy,
boolean supportsConstraintChecking,
Attribute... attributes) {
@@ -1064,6 +1102,7 @@
this.configuredTargetFunction = configuredTargetFunction;
this.externalBindingsFunction = externalBindingsFunction;
this.ruleDefinitionEnvironment = ruleDefinitionEnvironment;
+ this.ruleDefinitionEnvironmentHashCode = ruleDefinitionEnvironmentHashCode;
validateNoClashInPublicNames(attributes);
this.attributes = ImmutableList.copyOf(attributes);
this.workspaceOnly = workspaceOnly;
@@ -1756,12 +1795,25 @@
}
/**
- * Returns this RuleClass's rule definition environment.
+ * Returns this RuleClass's rule definition environment. Is null for native rules' RuleClass
+ * objects and deserialized Skylark rules. Deserialized rules do provide a hash code encapsulating
+ * their behavior, available at {@link #getRuleDefinitionEnvironmentHashCode}.
*/
- @Nullable public Environment getRuleDefinitionEnvironment() {
+ @Nullable
+ public Environment getRuleDefinitionEnvironment() {
return ruleDefinitionEnvironment;
}
+ /**
+ * Returns the hash code for the RuleClass's rule definition environment. In deserialization,
+ * this RuleClass may not actually contain its environment, in which case the hash code is all
+ * that is available. Will be null for native rules' RuleClass objects.
+ */
+ @Nullable
+ public String getRuleDefinitionEnvironmentHashCode() {
+ return ruleDefinitionEnvironmentHashCode;
+ }
+
/** Returns true if this RuleClass is a skylark-defined RuleClass. */
public boolean isSkylark() {
return isSkylark;