// 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.actions; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.google.common.collect.Sets.SetView; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventHandler; import java.util.Set; /** * A mutable action graph. Implementations of this interface must be thread-safe. */ public interface MutableActionGraph extends ActionGraph { /** * Attempts to register the action. If any of the action's outputs already has a generating * action, and the two actions are not compatible, then an {@link ActionConflictException} is * thrown. The internal data structure may be partially modified when that happens; it is not * guaranteed that all potential conflicts are detected, but at least one of them is. * *
For example, take three actions A, B, and C, where A creates outputs a and b, B creates just * b, and C creates c and b. There are two potential conflicts in this case, between A and B, and * between B and C. Depending on the ordering of calls to this method and the ordering of outputs * in the action output lists, either one or two conflicts are detected: if B is registered first, * then both conflicts are detected; if either A or C is registered first, then only one conflict * is detected. */ void registerAction(ActionAnalysisMetadata action) throws ActionConflictException; /** * Removes an action from this action graph if it is present. * *
Throws {@link IllegalStateException} if one of the outputs of the action is in fact
* generated by a different {@link Action} instance (even if they are sharable).
*/
void unregisterAction(ActionAnalysisMetadata action);
/**
* Clear the action graph.
*/
void clear();
/**
* This exception is thrown when a conflict between actions is detected. It contains information
* about the artifact for which the conflict is found, and data about the two conflicting actions
* and their owners.
*/
public static final class ActionConflictException extends Exception {
private final Artifact artifact;
private final ActionAnalysisMetadata previousAction;
private final ActionAnalysisMetadata attemptedAction;
private static final int MAX_DIFF_ARTIFACTS_TO_REPORT = 5;
public ActionConflictException(Artifact artifact, ActionAnalysisMetadata previousAction,
ActionAnalysisMetadata attemptedAction) {
super(
String.format(
"for %s, previous action: %s, attempted action: %s",
artifact.prettyPrint(),
previousAction.prettyPrint(),
attemptedAction.prettyPrint()));
this.artifact = artifact;
this.previousAction = previousAction;
this.attemptedAction = attemptedAction;
}
public Artifact getArtifact() {
return artifact;
}
public void reportTo(EventHandler eventListener) {
String msg =
"file '"
+ artifact.prettyPrint()
+ "' is generated by these conflicting actions:\n"
+ suffix(attemptedAction, previousAction);
eventListener.handle(Event.error(msg));
}
private static void addStringDetail(
StringBuilder sb, String key, String valueA, String valueB) {
valueA = valueA != null ? valueA : "(null)";
valueB = valueB != null ? valueB : "(null)";
sb.append(key).append(": ").append(valueA);
if (!valueA.equals(valueB)) {
sb.append(", ").append(valueB);
}
sb.append("\n");
}
private static void addListDetail(
StringBuilder sb, String key, Iterable