blob: bf6e3a00997bd1a8b4fd78b3eb466182e0dbdc83 [file] [log] [blame]
// 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.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.skyframe.LegacySkyKey;
import com.google.devtools.build.skyframe.SkyFunctionName;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
/**
* Base class for all values which can provide the generating action of an artifact. The primary
* instance of such lookup values is {@code ConfiguredTargetValue}. Values that hold the generating
* actions of target completion values and build info artifacts also fall into this category.
*/
public class ActionLookupValue implements SkyValue {
protected final List<ActionAnalysisMetadata> actions;
private final ImmutableMap<Artifact, Integer> generatingActionIndex;
private static Actions.GeneratingActions filterSharedActionsAndThrowRuntimeIfConflict(
List<ActionAnalysisMetadata> actions) {
try {
return Actions.filterSharedActionsAndThrowActionConflict(actions);
} catch (ActionConflictException e) {
// Programming bug.
throw new IllegalStateException(e);
}
}
@VisibleForTesting
public ActionLookupValue(
List<ActionAnalysisMetadata> actions, boolean removeActionsAfterEvaluation) {
this(filterSharedActionsAndThrowRuntimeIfConflict(actions), removeActionsAfterEvaluation);
}
protected ActionLookupValue(ActionAnalysisMetadata action, boolean removeActionAfterEvaluation) {
this(ImmutableList.of(action), removeActionAfterEvaluation);
}
protected ActionLookupValue(
Actions.GeneratingActions generatingActions, boolean removeActionsAfterEvaluation) {
if (removeActionsAfterEvaluation) {
this.actions = new ArrayList<>(generatingActions.getActions());
} else {
this.actions = ImmutableList.copyOf(generatingActions.getActions());
}
this.generatingActionIndex = generatingActions.getGeneratingActionIndex();
}
/**
* Returns the action that generates {@code artifact}, if known to this value, or null. This
* method should be avoided. Call it only when the action is really needed, and it is known to be
* present, either because the execution phase has not started, or because {@link
* Action#canRemoveAfterExecution} is known to be false for the action being requested.
*/
@Nullable
public ActionAnalysisMetadata getGeneratingActionDangerousReadJavadoc(Artifact artifact) {
Integer actionIndex = getGeneratingActionIndex(artifact);
if (actionIndex == null) {
return null;
}
return getActionAnalysisMetadata(actionIndex);
}
/**
* Returns the index of the action that generates {@code artifact} in this value, or null if this
* value does not have a generating action for this artifact. The index together with the key for
* this {@link ActionLookupValue} uniquely identifies the action.
*
* <p>Unlike {@link #getAction}, this may be called after action execution.
*/
@Nullable
public Integer getGeneratingActionIndex(Artifact artifact) {
return generatingActionIndex.get(artifact);
}
/**
* Returns the {@link Action} with index {@code index} in this value. Never null. Should only be
* called during action execution by {@code ArtifactFunction} and {@code ActionExecutionFunction}
* -- after an action has executed, calling this with its index may crash.
*/
@SuppressWarnings("unchecked") // We test to make sure it's an Action.
public Action getAction(int index) {
ActionAnalysisMetadata result = getActionAnalysisMetadata(index);
Preconditions.checkState(result instanceof Action, "Not action: %s %s %s", result, index, this);
return (Action) result;
}
private ActionAnalysisMetadata getActionAnalysisMetadata(int index) {
return Preconditions.checkNotNull(actions.get(index), "null action: %s %s", index, this);
}
/**
* Returns the {@link ActionAnalysisMetadata} at index {@code index} if it is present and
* <i>not</i> an {@link Action}. Tree artifacts need their {@code ActionTemplate}s in order to
* generate the correct actions, but in general most actions are not needed after they are
* executed and may not even be available.
*/
public ActionAnalysisMetadata getIfPresentAndNotAction(int index) {
ActionAnalysisMetadata actionAnalysisMetadata = actions.get(index);
if (!(actionAnalysisMetadata instanceof Action)) {
return actionAnalysisMetadata;
}
return null;
}
/** To be used only when checking consistency of the action graph -- not by other values. */
public Map<Artifact, ActionAnalysisMetadata> getMapForConsistencyCheck() {
return getMapForConsistencyCheck(generatingActionIndex, actions);
}
protected ToStringHelper getStringHelper() {
return MoreObjects.toStringHelper(this)
.add("actions", actions)
.add("generatingActionIndex", generatingActionIndex);
}
@Override
public String toString() {
return getStringHelper().toString();
}
@VisibleForTesting
public static SkyKey key(ActionLookupKey ownerKey) {
return ownerKey.getSkyKey();
}
public int getNumActions() {
return actions.size();
}
public static Map<Artifact, ActionAnalysisMetadata> getMapForConsistencyCheck(
Map<Artifact, Integer> generatingActionIndex,
final List<? extends ActionAnalysisMetadata> actions) {
return Maps.transformValues(generatingActionIndex, actions::get);
}
/**
* If this object was initialized with {@code removeActionsAfterEvaluation} and {@link
* Action#canRemoveAfterExecution()} is true for {@code action}, then remove this action from this
* object's index as a memory-saving measure. The {@code artifact -> index} mapping remains
* intact, so this action's execution value can still be addressed by its inputs.
*/
@ThreadSafe
public void actionEvaluated(int actionIndex, Action action) {
if (!action.canRemoveAfterExecution()) {
return;
}
if (actions instanceof ArrayList) {
// This method may concurrently mutate an ArrayList, which is unsafe on its face. However,
// ArrayList mutation on different indices that does not affect the size of the ArrayList is
// safe, and that is what does this code does.
ArrayList<ActionAnalysisMetadata> actionArrayList =
(ArrayList<ActionAnalysisMetadata>) actions;
ActionAnalysisMetadata oldAction = actionArrayList.set(actionIndex, null);
Preconditions.checkState(
action.equals(oldAction), "Not same: %s %s %s %s", action, oldAction, this, actionIndex);
}
}
/**
* ArtifactOwner is not a SkyKey, but we wish to convert any ArtifactOwner into a SkyKey as simply
* as possible. To that end, all subclasses of ActionLookupValue "own" artifacts with
* ArtifactOwners that are subclasses of ActionLookupKey. This allows callers to easily find the
* value key, while remaining agnostic to what ActionLookupValues actually exist.
*
* <p>The methods of this class should only be called by {@link ActionLookupValue#key}.
*/
public abstract static class ActionLookupKey implements ArtifactOwner {
@Override
public Label getLabel() {
return null;
}
/**
* Subclasses must override this to specify their specific value type, unless they override
* {@link #getSkyKey}, in which case they are free not to implement this method.
*/
protected abstract SkyFunctionName getType();
protected SkyKey getSkyKeyInternal() {
return LegacySkyKey.create(getType(), this);
}
/**
* Prefer {@link ActionLookupValue#key} to calling this method directly.
*
* <p>Subclasses may override {@link #getSkyKeyInternal} if the {@link SkyKey} argument should
* not be this {@link ActionLookupKey} itself.
*/
public final SkyKey getSkyKey() {
SkyKey result = getSkyKeyInternal();
Preconditions.checkState(
result.argument() instanceof ActionLookupKey,
"Not ActionLookupKey for %s: %s",
this,
result);
return result;
}
}
}