Make explicit the contract of ConfiguredTarget builders returning null when there are rule errors.
RELNOTES: None.
PiperOrigin-RevId: 206652580
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
index 786dba9..ddc1ad6 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
@@ -49,6 +49,7 @@
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
+import javax.annotation.Nullable;
/**
* Builder class for analyzed rule instances.
@@ -79,7 +80,11 @@
add(VisibilityProvider.class, new VisibilityProviderImpl(ruleContext.getVisibility()));
}
- /** Constructs the RuleConfiguredTarget instance based on the values set for this Builder. */
+ /**
+ * Constructs the RuleConfiguredTarget instance based on the values set for this Builder.
+ * Returns null if there were rule errors reported.
+ */
+ @Nullable
public ConfiguredTarget build() throws ActionConflictException {
if (ruleContext.getConfiguration().enforceConstraints()) {
checkConstraints();
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleConfiguredTargetUtil.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleConfiguredTargetUtil.java
index 1f2a184..3b150c2 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleConfiguredTargetUtil.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleConfiguredTargetUtil.java
@@ -74,7 +74,11 @@
private static final ImmutableSet<String> DEFAULT_PROVIDER_FIELDS =
ImmutableSet.of("files", "runfiles", "data_runfiles", "default_runfiles", "executable");
- /** Create a Rule Configured Target from the ruleContext and the ruleImplementation. */
+ /**
+ * Create a Rule Configured Target from the ruleContext and the ruleImplementation.
+ * Returns null if there were errors during target creation.
+ */
+ @Nullable
public static ConfiguredTarget buildRule(
RuleContext ruleContext,
AdvertisedProviderSet advertisedProviders,
@@ -115,8 +119,12 @@
return null;
}
ConfiguredTarget configuredTarget = createTarget(skylarkRuleContext, target);
- SkylarkProviderValidationUtil.validateArtifacts(ruleContext);
- checkDeclaredProviders(configuredTarget, advertisedProviders, location);
+ if (configuredTarget != null) {
+ // If there was error creating the ConfiguredTarget, no further validation is needed.
+ // Null will be returned and the errors thus reported.
+ SkylarkProviderValidationUtil.validateArtifacts(ruleContext);
+ checkDeclaredProviders(configuredTarget, advertisedProviders, location);
+ }
return configuredTarget;
} catch (EvalException e) {
addRuleToStackTrace(e, ruleContext.getRule(), ruleImplementation);
@@ -172,6 +180,7 @@
return ex.getMessage();
}
+ @Nullable
private static ConfiguredTarget createTarget(SkylarkRuleContext context, Object target)
throws EvalException, RuleErrorException, ActionConflictException {
RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
index 75d7929..1834735 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
@@ -1731,6 +1731,39 @@
assertContainsEvent("foo/bar/baz");
}
+ @Test
+ public void testEnvironmentConstraintsFromSkylarkRule() throws Exception {
+ scratch.file(
+ "buildenv/foo/BUILD",
+ "environment_group(name = 'env_group',",
+ " defaults = [':default'],",
+ " environments = ['default', 'other'])",
+ "environment(name = 'default')",
+ "environment(name = 'other')");
+ // The example skylark rule explicitly provides the MyProvider provider as a regression test
+ // for a bug where a skylark rule with unsatisfied constraints but explicit providers would
+ // result in Bazel throwing a null pointer exception.
+ scratch.file(
+ "test/skylark/extension.bzl",
+ "MyProvider = provider()",
+ "",
+ "def _impl(ctx):",
+ " return struct(providers = [MyProvider(foo = 'bar')])",
+ "my_rule = rule(implementation = _impl,",
+ " attrs = { 'deps' : attr.label_list() },",
+ " provides = [MyProvider])");
+ scratch.file(
+ "test/skylark/BUILD",
+ "load('//test/skylark:extension.bzl', 'my_rule')",
+ "java_library(name = 'dep', srcs = ['a.java'], restricted_to = ['//buildenv/foo:other'])",
+ "my_rule(name='my', deps = [':dep'])");
+
+ reporter.removeHandler(failFastHandler);
+ assertThat(getConfiguredTarget("//test/skylark:my")).isNull();
+ assertContainsEvent(
+ "//test/skylark:dep doesn't support expected environment: //buildenv/foo:default");
+ }
+
/**
* Skylark integration test that forces inlining.
*/