| // 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.ImmutableMap; |
| import com.google.devtools.build.lib.actions.extra.ExtraActionInfo; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSet; |
| import com.google.devtools.build.lib.concurrent.ThreadSafety.ConditionallyThreadCompatible; |
| import com.google.devtools.build.lib.vfs.BulkDeleter; |
| import com.google.devtools.build.lib.vfs.Path; |
| import java.io.IOException; |
| import java.util.Map; |
| import javax.annotation.Nullable; |
| |
| /** |
| * An Action represents a function from Artifacts to Artifacts executed as an atomic build step. |
| * Examples include compilation of a single C++ source file, or linking a single library. |
| * |
| * <p>All subclasses of Action need to follow a strict set of invariants to ensure correctness on |
| * incremental builds. In our experience, getting this wrong is a lot more expensive than any |
| * benefits it might entail. |
| * |
| * <p>Use {@link com.google.devtools.build.lib.analysis.actions.SpawnAction} or {@link |
| * com.google.devtools.build.lib.analysis.actions.FileWriteAction} where possible, and avoid writing |
| * a new custom subclass. |
| * |
| * <p>These are the most important requirements for subclasses: |
| * |
| * <ul> |
| * <li>Actions must be generally immutable; we currently make an exception for C++, and that has |
| * been a constant source of correctness issues; there are still ongoing incremental |
| * correctness issues for C++ compilations, despite several rounds of fixes and even though |
| * this is the oldest part of the code base. |
| * <li>Actions should be as lazy as possible - storing full lists of inputs or full command lines |
| * in every action generally results in quadratic memory consumption. Use {@link |
| * com.google.devtools.build.lib.collect.nestedset.NestedSet} for inputs, and {@link |
| * com.google.devtools.build.lib.analysis.actions.CustomCommandLine} for command lines where |
| * possible to share as much data between the different actions and their owning configured |
| * targets. |
| * <li>However, actions must not reference configured targets or rule contexts themselves; only |
| * reference the necessary underlying artifacts or strings, preferably as nested sets. Bazel |
| * may attempt to garbage collect configured targets and rule contexts before execution to |
| * keep memory consumption down, and referencing them prevents that. |
| * <li>In particular, avoid anonymous inner classes - when created in a non-static method, they |
| * implicitly keep a reference to their enclosing class, even if that reference is unnecessary |
| * for correct operation. Not doing so has caused significant increases in memory consumption |
| * in the past. |
| * <li>Correct cache key computation in {@link #getKey} is critical for the correctness of |
| * incremental builds; you may be tempted to intentionally exclude data from the cache key, |
| * but be aware that every time we've done that, it later resulted in expensive debugging |
| * sessions and bug fixes. |
| * <li>As much as possible, make the cache key computation obvious - fully hash every field |
| * (except input contents, but including input and output names if they appear in the command |
| * line) in the class, and avoid referencing anything that isn't needed for action execution, |
| * such as {@link com.google.devtools.build.lib.analysis.config.BuildConfigurationValue} |
| * objects or even fragments thereof; if the action has a command line, err on the side of |
| * hashing the entire command line, even if that seems expensive. It's always safe to hash too |
| * much - the negative effect on incremental build times is usually negligible. |
| * <li>Add test coverage for the cache key computation; use {@link |
| * com.google.devtools.build.lib.analysis.util.ActionTester} to generate as many combinations |
| * of field values as possible; add test coverage every time you add another field. |
| * </ul> |
| * |
| * <p>These constraints are not easily enforced or tested for (e.g., ActionTester only checks that a |
| * known set of fields is covered, not that all fields are covered), so carefully check all changes |
| * to action subclasses. |
| */ |
| public interface Action extends ActionExecutionMetadata { |
| /** |
| * Prepares for executing this action; called by the Builder prior to executing the Action itself. |
| * This method should prepare the file system, so that the execution of the Action can write the |
| * output files. At a minimum any pre-existing and write protected output files should be removed |
| * or the permissions should be changed, so that they can be safely overwritten by the action. |
| * |
| * @throws IOException if there is an error deleting the outputs. |
| * @throws InterruptedException if the execution is interrupted |
| */ |
| void prepare( |
| Path execRoot, |
| ArtifactPathResolver pathResolver, |
| @Nullable BulkDeleter bulkDeleter, |
| boolean cleanupArchivedArtifacts) |
| throws IOException, InterruptedException; |
| |
| /** |
| * Executes this action. This method <i>unconditionally does the work of the Action</i>, although |
| * it may delegate some of that work to {@link ActionContext} instances obtained from the {@link |
| * ActionExecutionContext}, which may in turn perform caching at smaller granularity than an |
| * entire action. |
| * |
| * <p>This method may not be invoked if an equivalent action (as determined by the hashes of the |
| * input files, the list of output files, and the action cache key) has been previously executed, |
| * possibly on another machine. |
| * |
| * <p>The framework guarantees that: |
| * |
| * <ul> |
| * <li>all declared inputs have already been successfully created, |
| * <li>the output directory for each file in <code>getOutputs()</code> has already been created, |
| * <li>this method is only called by at most one thread at a time, but subsequent calls may be |
| * made from different threads, |
| * <li>for shared actions, at most one instance is executed per build. |
| * </ul> |
| * |
| * <p>Multiple instances of the same action implementation may be called in parallel. |
| * Implementations must therefore be thread-compatible. Also see the class documentation for |
| * additional invariants. |
| * |
| * <p>Implementations should attempt to detect interrupts, and exit quickly with an {@link |
| * InterruptedException}. |
| * |
| * @param actionExecutionContext services in the scope of the action, like the output and error |
| * streams to use for messages arising during action execution |
| * @return returns an ActionResult containing action execution metadata |
| * @throws ActionExecutionException if execution fails for any reason |
| * @throws InterruptedException if the execution is interrupted |
| */ |
| @ConditionallyThreadCompatible |
| default ActionResult execute(ActionExecutionContext actionExecutionContext) |
| throws ActionExecutionException, InterruptedException { |
| ActionContinuationOrResult continuation = beginExecution(actionExecutionContext); |
| while (!continuation.isDone()) { |
| continuation = continuation.execute(); |
| } |
| return continuation.get(); |
| } |
| |
| /** |
| * Actions that want to support async execution can use this interface to do so. While this is |
| * still disabled by default, we want to eventually deprecate the {@link #execute} method in favor |
| * of this new interface. |
| * |
| * <p>If the relevant command-line flag is enabled, Skyframe will call this method rather than |
| * {@link #execute}. As such, actions implementing both should exhibit identical behavior, and all |
| * requirements from the {@link #execute} documentation apply. |
| * |
| * <p>This method allows an action to return a continuation representing future work to be done, |
| * in combination with a listenable future representing concurrent ongoing work in another thread |
| * pool or even on another machine. When the concurrent work finishes, the listenable future must |
| * be completed to notify Skyframe of this fact. |
| * |
| * <p>Once the listenable future is completed, Skyframe will re-execute the corresponding Skyframe |
| * node representing this action, which will eventually call into the continuation returned here. |
| * |
| * <p>Actions implementing this method are not required to run asynchronously, although we expect |
| * the majority of actions to do so eventually. They can block the current thread for any amount |
| * of time as long as they return eventually, and also honor interrupt signals. |
| * |
| * @param actionExecutionContext services in the scope of the action, like the output and error |
| * streams to use for messages arising during action execution |
| * @return returns an ActionResult containing action execution metadata |
| * @throws ActionExecutionException if execution fails for any reason |
| * @throws InterruptedException if the execution is interrupted |
| */ |
| default ActionContinuationOrResult beginExecution(ActionExecutionContext actionExecutionContext) |
| throws ActionExecutionException, InterruptedException { |
| return ActionContinuationOrResult.of(execute(actionExecutionContext)); |
| } |
| |
| /** |
| * Returns true iff action must be executed regardless of its current state. |
| * Default implementation can be overridden by some actions that might be |
| * executed unconditionally under certain circumstances - e.g., if caching of |
| * test results is not requested, this method could be used to force test |
| * execution even if all dependencies are up-to-date. |
| * |
| * <p>Note, it is <b>very</b> important not to abuse this method, since it |
| * completely overrides dependency checking. Any use of this method must |
| * be carefully reviewed and proved to be necessary. |
| * |
| * <p>Note that the definition of {@link #isVolatile} depends on the |
| * definition of this method, so be sure to consider both methods together |
| * when making changes. |
| */ |
| boolean executeUnconditionally(); |
| |
| /** |
| * Returns true if it's ever possible that {@link #executeUnconditionally} |
| * could evaluate to true during the lifetime of this instance, false |
| * otherwise. |
| */ |
| boolean isVolatile(); |
| |
| /** |
| * Method used to find inputs before execution for an action that {@link |
| * ActionExecutionMetadata#discoversInputs}. Returns the set of discovered inputs (may be the |
| * empty set) or null if this action declared additional Skyframe dependencies that must be |
| * computed before it can make a decision. |
| */ |
| @Nullable |
| NestedSet<Artifact> discoverInputs(ActionExecutionContext actionExecutionContext) |
| throws ActionExecutionException, InterruptedException; |
| |
| /** Prepare for input discovery, called before the first call to {@link #discoverInputs}. */ |
| default void prepareInputDiscovery() {} |
| |
| /** |
| * Resets this action's inputs to a pre {@linkplain #discoverInputs input discovery} state. |
| * |
| * <p>This may be called on input-discovering actions during non-incremental builds, when it is |
| * not worthwhile to retain the discovered inputs after the action completes execution. It may |
| * still be necessary to rewind the action, so it must retain state necessary for re-execution. |
| */ |
| void resetDiscoveredInputs(); |
| |
| /** |
| * Returns the set of artifacts that can possibly be inputs. It will be called iff {@link |
| * #inputsDiscovered()} is false for the given action instance and there is a related cache entry |
| * in the action cache. |
| * |
| * <p>Method must be redefined for any action for which {@link #inputsDiscovered()} may return |
| * false. |
| * |
| * <p>The method is allowed to return source artifacts. They are useless, though, since exec paths |
| * in the action cache referring to source artifacts are always resolved. |
| */ |
| NestedSet<Artifact> getAllowedDerivedInputs(); |
| |
| /** |
| * Informs the action that its inputs are {@code inputs}, and that its inputs are now known. Can |
| * only be called for actions that discover inputs. After this method is called, {@link |
| * ActionExecutionMetadata#inputsDiscovered} should return true. |
| */ |
| void updateInputs(NestedSet<Artifact> inputs); |
| |
| /** |
| * Returns true if the output should bypass output filtering. This is used for test actions. |
| */ |
| boolean showsOutputUnconditionally(); |
| |
| /** |
| * Called by {@link com.google.devtools.build.lib.analysis.extra.ExtraAction} at execution time to |
| * extract information from this action into a protocol buffer to be used by extra_action rules. |
| * |
| * <p>As this method is called from the ExtraAction, make sure it is ok to call this method from a |
| * different thread than the one this action is executed on. |
| */ |
| ExtraActionInfo.Builder getExtraActionInfo(ActionKeyContext actionKeyContext) |
| throws CommandLineExpansionException, InterruptedException; |
| |
| /** |
| * Called by {@link com.google.devtools.build.lib.analysis.actions.StarlarkAction} in {@link |
| * #beginExecution} to use its shadowed action, if any, complete list of environment variables in |
| * the Starlark action Spawn. |
| * |
| * <p>As this method is called from the StarlarkAction, make sure it is ok to call it from a |
| * different thread than the one this action is executed on. By definition, the method should not |
| * mutate any of the called action data but if necessary, its implementation must synchronize any |
| * accesses to mutable data. |
| */ |
| ImmutableMap<String, String> getEffectiveEnvironment(Map<String, String> clientEnv) |
| throws CommandLineExpansionException; |
| } |