blob: cddc4349a2fdf463bdc927d1ef0f17fff40d7958 [file] [log] [blame]
// Copyright 2018 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.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.RuleErrorConsumer;
/**
* Base class for implementations of {@link
* com.google.devtools.build.lib.packages.RuleErrorConsumer}.
*
* <p>Do not create new implementations of this class - instead, use {@link RuleContext} in Native
* rule definitions, and {@link com.google.devtools.build.lib.analysis.skylark.SkylarkErrorReporter}
* in Skylark API definitions. For use in testing, implement {@link RuleErrorConsumer} instead.
*/
public abstract class EventHandlingErrorReporter implements RuleErrorConsumer {
private final String ruleClassNameForLogging;
private final AnalysisEnvironment env;
protected EventHandlingErrorReporter(String ruleClassNameForLogging, AnalysisEnvironment env) {
this.ruleClassNameForLogging = ruleClassNameForLogging;
this.env = env;
}
private void reportError(Location location, String message) {
// TODO(ulfjack): Consider generating the error message from the root cause event rather than
// the other way round.
if (!hasErrors()) {
// We must not report duplicate events, so we only report the first one for now.
env.getEventHandler()
.post(new AnalysisRootCauseEvent(getConfiguration(), getLabel(), message));
}
env.getEventHandler().handle(Event.error(location, message));
}
@Override
public void ruleError(String message) {
reportError(getRuleLocation(), prefixRuleMessage(message));
}
@Override
public void attributeError(String attrName, String message) {
reportError(getRuleLocation(), completeAttributeMessage(attrName, message));
}
@Override
public boolean hasErrors() {
return env.hasErrors();
}
public void reportWarning(Location location, String message) {
env.getEventHandler().handle(Event.warn(location, message));
}
@Override
public void ruleWarning(String message) {
env.getEventHandler().handle(Event.warn(getRuleLocation(), prefixRuleMessage(message)));
}
@Override
public void attributeWarning(String attrName, String message) {
reportWarning(getRuleLocation(), completeAttributeMessage(attrName, message));
}
private String prefixRuleMessage(String message) {
return String.format("in %s rule %s: %s", ruleClassNameForLogging, getLabel(), message);
}
private String maskInternalAttributeNames(String name) {
return Attribute.isImplicit(name) ? "(an implicit dependency)" : name;
}
/**
* Prefixes the given message with details about the rule and appends details about the macro that
* created this rule, if applicable.
*/
private String completeAttributeMessage(String attrName, String message) {
// Appends a note to the given message if the offending rule was created by a macro.
return String.format(
"in %s attribute of %s rule %s: %s%s",
maskInternalAttributeNames(attrName),
ruleClassNameForLogging,
getLabel(),
message,
getMacroMessageAppendix(attrName));
}
/** Returns a string describing the macro that created this rule, or an empty string. */
protected abstract String getMacroMessageAppendix(String attrName);
protected abstract Label getLabel();
protected abstract BuildConfiguration getConfiguration();
protected abstract Location getRuleLocation();
}