bazel analysis: move analysis-related classes here
This change moves RuleErrorConsumer and AnalysisIssues from lib.packages,
where they *obviously* do not belong, to lib.analysis.
RuleClass.checkAttributesNonEmpty, which again doesn't belong, because
of its dependency on RuleErrorConsumer, was inlined to its sole caller
(RuleContext).
PiperOrigin-RevId: 325437765
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AnalysisIssues.java b/src/main/java/com/google/devtools/build/lib/analysis/AnalysisIssues.java
new file mode 100644
index 0000000..ca591bd
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/AnalysisIssues.java
@@ -0,0 +1,134 @@
+// Copyright 2014 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.analysis;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.Nullable;
+
+/**
+ * Checked exception for analysis-time errors, which can store the errors for later reporting.
+ *
+ * <p>It's more robust for a method to throw this exception than expecting a
+ * {@link RuleErrorConsumer} object (which may be null).
+ */
+public final class AnalysisIssues extends Exception {
+
+ /**
+ * An error entry.
+ *
+ * <p>{@link AnalysisIssues} can accumulate multiple of these, and report all of them at once.
+ */
+ public static final class Entry {
+ private final String attribute;
+ private final String messageTemplate;
+ private final Object[] arguments;
+
+ private Entry(@Nullable String attribute, String messageTemplate, Object... arguments) {
+ this.attribute = attribute;
+ this.messageTemplate = messageTemplate;
+ this.arguments = arguments;
+ }
+
+ private void reportTo(RuleErrorConsumer errors) {
+ String msg = String.format(messageTemplate, arguments);
+ if (attribute == null) {
+ errors.ruleError(msg);
+ } else {
+ errors.attributeError(attribute, msg);
+ }
+ }
+
+ private void reportTo(StringBuilder sb) {
+ String msg = String.format(messageTemplate, arguments);
+ if (attribute == null) {
+ sb.append("ERROR: ").append(msg);
+ } else {
+ sb.append("ERROR: in attribute \"").append(attribute).append("\": ").append(msg);
+ }
+ }
+
+ @Override
+ public String toString() {
+ if (attribute == null) {
+ return String.format("ERROR: " + messageTemplate, arguments);
+ } else {
+ List<Object> args = new ArrayList<>();
+ args.add(attribute);
+ Collections.addAll(args, arguments);
+ return String.format("ERROR in '%s': " + messageTemplate, args.toArray());
+ }
+ }
+ }
+
+ private final ImmutableList<Entry> entries;
+
+ public AnalysisIssues(Entry entry) {
+ this.entries = ImmutableList.of(Preconditions.checkNotNull(entry));
+ }
+
+ public AnalysisIssues(Collection<Entry> entries) {
+ this.entries = ImmutableList.copyOf(Preconditions.checkNotNull(entries));
+ }
+
+ /**
+ * Creates a attribute error entry that will be added to a {@link AnalysisIssues} later.
+ */
+ public static Entry attributeError(String attribute, String messageTemplate,
+ Object... arguments) {
+ return new Entry(attribute, messageTemplate, arguments);
+ }
+
+ public static Entry ruleError(String messageTemplate, Object... arguments) {
+ return new Entry(null, messageTemplate, arguments);
+ }
+
+ /**
+ * Report all accumulated errors and warnings to the given consumer object.
+ */
+ public void reportTo(RuleErrorConsumer errors) {
+ Preconditions.checkNotNull(errors);
+ for (Entry e : entries) {
+ e.reportTo(errors);
+ }
+ }
+
+ @Nullable
+ private String asString() {
+ if (entries == null) {
+ return null;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (Entry e : entries) {
+ e.reportTo(sb);
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public String getMessage() {
+ return asString();
+ }
+
+ @Override
+ public String toString() {
+ String s = asString();
+ return s == null ? "" : s;
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BUILD b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
index cb29326..d3c509a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
@@ -169,6 +169,7 @@
"Allowlist.java",
"AnalysisEnvironment.java",
"AnalysisFailureEvent.java",
+ "AnalysisIssues.java",
"AnalysisResult.java",
"AnalysisRootCauseEvent.java",
"AnalysisUtils.java",
@@ -205,6 +206,7 @@
"RuleConfiguredTargetFactory.java",
"RuleContext.java",
"RuleDefinition.java",
+ "RuleErrorConsumer.java",
"Runfiles.java",
"RunfilesProvider.java",
"RunfilesSupplierImpl.java",
@@ -2110,6 +2112,7 @@
name = "starlark/starlark_error_reporter",
srcs = ["starlark/StarlarkErrorReporter.java"],
deps = [
+ ":analysis_cluster",
"//src/main/java/com/google/devtools/build/lib/packages",
"//src/main/java/com/google/devtools/build/lib/syntax:evaluator",
],
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/CommonPrerequisiteValidator.java b/src/main/java/com/google/devtools/build/lib/analysis/CommonPrerequisiteValidator.java
index d6f90dc..ffd5d5d 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/CommonPrerequisiteValidator.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/CommonPrerequisiteValidator.java
@@ -25,7 +25,6 @@
import com.google.devtools.build.lib.packages.PackageGroup;
import com.google.devtools.build.lib.packages.RawAttributeMapper;
import com.google.devtools.build.lib.packages.Rule;
-import com.google.devtools.build.lib.packages.RuleErrorConsumer;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.packages.Type;
import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData;
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/EventHandlingErrorReporter.java b/src/main/java/com/google/devtools/build/lib/analysis/EventHandlingErrorReporter.java
index c06ba04..f138abc 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/EventHandlingErrorReporter.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/EventHandlingErrorReporter.java
@@ -17,12 +17,11 @@
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.packages.Attribute;
-import com.google.devtools.build.lib.packages.RuleErrorConsumer;
import com.google.devtools.build.lib.syntax.Location;
/**
* Base class for implementations of {@link
- * com.google.devtools.build.lib.packages.RuleErrorConsumer}.
+ * com.google.devtools.build.lib.analysis.RuleErrorConsumer}.
*
* <p>Do not create new implementations of this class - instead, use {@link RuleContext} in Native
* rule definitions, and {@link StarlarkErrorReporter} in Starlark API definitions. For use in
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/LocationExpander.java b/src/main/java/com/google/devtools/build/lib/analysis/LocationExpander.java
index fafc011..e5a1c95 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/LocationExpander.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/LocationExpander.java
@@ -31,7 +31,6 @@
import com.google.devtools.build.lib.cmdline.RepositoryName;
import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.packages.OutputFile;
-import com.google.devtools.build.lib.packages.RuleErrorConsumer;
import com.google.devtools.build.lib.util.ShellEscaper;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
index 72edea4..002e1fd 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
@@ -85,7 +85,6 @@
import com.google.devtools.build.lib.packages.RequiredProviders;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.RuleClass;
-import com.google.devtools.build.lib.packages.RuleErrorConsumer;
import com.google.devtools.build.lib.packages.SymbolGenerator;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.packages.TargetUtils;
@@ -1813,7 +1812,7 @@
Preconditions.checkNotNull(constraintSemantics);
AttributeMap attributes =
ConfiguredAttributeMapper.of(target.getAssociatedRule(), configConditions);
- validateAttributes(attributes);
+ checkAttributesNonEmpty(attributes);
ListMultimap<String, ConfiguredTargetAndData> targetMap = createTargetMap();
ListMultimap<String, ConfiguredFilesetEntry> filesetEntryMap =
createFilesetEntryMap(target.getAssociatedRule(), configConditions);
@@ -1840,11 +1839,25 @@
requiredConfigFragments);
}
- private void validateAttributes(AttributeMap attributes) {
- target
- .getAssociatedRule()
- .getRuleClassObject()
- .checkAttributesNonEmpty(reporter, attributes);
+ private void checkAttributesNonEmpty(AttributeMap attributes) {
+ for (String attributeName : attributes.getAttributeNames()) {
+ Attribute attr = attributes.getAttributeDefinition(attributeName);
+ if (!attr.isNonEmpty()) {
+ continue;
+ }
+ Object attributeValue = attributes.get(attributeName, attr.getType());
+
+ // TODO(adonovan): define in terms of Starlark.len?
+ boolean isEmpty = false;
+ if (attributeValue instanceof List<?>) {
+ isEmpty = ((List) attributeValue).isEmpty();
+ } else if (attributeValue instanceof Map<?, ?>) {
+ isEmpty = ((Map) attributeValue).isEmpty();
+ }
+ if (isEmpty) {
+ reporter.attributeError(attr.getName(), "attribute must be non empty");
+ }
+ }
}
public Builder setVisibility(NestedSet<PackageGroupContents> visibility) {
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleErrorConsumer.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleErrorConsumer.java
new file mode 100644
index 0000000..cf4ae88
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleErrorConsumer.java
@@ -0,0 +1,104 @@
+// Copyright 2014 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.analysis;
+
+import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
+
+/**
+ * A thin interface exposing only the warning and error reporting functionality
+ * of a rule.
+ *
+ * <p>When a class or a method needs only this functionality but not the whole
+ * {@code RuleContext}, it can use this thin interface instead.
+ *
+ * <p>This interface should only be implemented by {@code RuleContext}.
+ */
+public interface RuleErrorConsumer {
+
+ /**
+ * Consume a non-attribute-specific warning in a rule.
+ */
+ void ruleWarning(String message);
+
+ /**
+ * Consume a non-attribute-specific error in a rule.
+ */
+ void ruleError(String message);
+
+ /**
+ * Consume an attribute-specific warning in a rule.
+ */
+ void attributeWarning(String attrName, String message);
+
+ /**
+ * Consume an attribute-specific error in a rule.
+ */
+ void attributeError(String attrName, String message);
+
+ /**
+ * Convenience function to report non-attribute-specific errors in the current rule and then throw
+ * a {@link RuleErrorException}, immediately exiting the current rule, and shutting down the
+ * invocation in a no-keep-going build. If multiple errors are present, invoke {@link #ruleError}
+ * to collect additional error information before calling this method.
+ */
+ default RuleErrorException throwWithRuleError(String message) throws RuleErrorException {
+ ruleError(message);
+ throw new RuleErrorException(message);
+ }
+
+ /** See {@link #throwWithRuleError(String)}. */
+ default RuleErrorException throwWithRuleError(Throwable cause) throws RuleErrorException {
+ ruleError(cause.getMessage());
+ throw new RuleErrorException(cause);
+ }
+
+ /** See {@link #throwWithRuleError(String)}. */
+ default RuleErrorException throwWithRuleError(String message, Throwable cause)
+ throws RuleErrorException {
+ ruleError(message);
+ throw new RuleErrorException(message, cause);
+ }
+
+ /**
+ * Convenience function to report attribute-specific errors in the current rule, and then throw a
+ * {@link RuleErrorException}, immediately exiting the build invocation. Alternatively, invoke
+ * {@link #attributeError} instead to collect additional error information before ending the
+ * invocation.
+ *
+ * <p>If the name of the attribute starts with <code>$</code>
+ * it is replaced with a string <code>(an implicit dependency)</code>.
+ */
+ default RuleErrorException throwWithAttributeError(String attrName, String message)
+ throws RuleErrorException {
+ attributeError(attrName, message);
+ throw new RuleErrorException(message);
+ }
+
+ /**
+ * Returns whether this instance is known to have errors at this point during analysis. Do not
+ * call this method after the initializationHook has returned.
+ */
+ boolean hasErrors();
+
+ /**
+ * No-op if {@link #hasErrors} is false, throws {@link RuleErrorException} if it is true.
+ * This provides a convenience to early-exit of configured target creation if there are errors.
+ */
+ default void assertNoErrors() throws RuleErrorException {
+ if (hasErrors()) {
+ throw new RuleErrorException();
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/actions/ActionConstructionContext.java b/src/main/java/com/google/devtools/build/lib/analysis/actions/ActionConstructionContext.java
index eb8fc9a..59d14ea 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/actions/ActionConstructionContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/actions/ActionConstructionContext.java
@@ -20,10 +20,10 @@
import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact;
import com.google.devtools.build.lib.actions.ArtifactRoot;
import com.google.devtools.build.lib.analysis.AnalysisEnvironment;
+import com.google.devtools.build.lib.analysis.RuleErrorConsumer;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
-import com.google.devtools.build.lib.packages.RuleErrorConsumer;
import com.google.devtools.build.lib.vfs.PathFragment;
import javax.annotation.Nullable;
@@ -180,7 +180,7 @@
PlatformInfo getExecutionPlatform(String execGroup);
/**
- * Returns the {@link com.google.devtools.build.lib.packages.RuleErrorConsumer} for reporting rule
+ * Returns the {@link com.google.devtools.build.lib.analysis.RuleErrorConsumer} for reporting rule
* errors.
*/
RuleErrorConsumer getRuleErrorConsumer();
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkErrorReporter.java b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkErrorReporter.java
index 79abec0..80d70a3 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkErrorReporter.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkErrorReporter.java
@@ -13,8 +13,8 @@
// limitations under the License
package com.google.devtools.build.lib.analysis.starlark;
+import com.google.devtools.build.lib.analysis.RuleErrorConsumer;
import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
-import com.google.devtools.build.lib.packages.RuleErrorConsumer;
import com.google.devtools.build.lib.syntax.EvalException;
/**