blob: 9ccfd15c105d66b325b107943be40921c1f06647 [file] [log] [blame]
// Copyright 2023 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.actions;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.AbstractAction;
import com.google.devtools.build.lib.actions.Action;
import com.google.devtools.build.lib.actions.ActionKeyContext;
import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
import com.google.devtools.build.lib.actions.CommandLineLimits;
import com.google.devtools.build.lib.actions.ExecutionRequirements;
import com.google.devtools.build.lib.actions.PathMapper;
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue;
import com.google.devtools.build.lib.analysis.config.CoreOptions;
import com.google.devtools.build.lib.analysis.config.CoreOptions.OutputPathsMode;
import com.google.devtools.build.lib.util.Fingerprint;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.Map;
import javax.annotation.Nullable;
/**
* Utility methods that are the canonical way for actions to support path mapping (see {@link
* PathMapper}).
*/
public final class PathMappers {
// TODO: Remove actions from this list by adding ExecutionRequirements.SUPPORTS_PATH_MAPPING to
// their execution info instead.
private static final ImmutableSet<String> SUPPORTED_MNEMONICS =
ImmutableSet.of(
"AndroidLint",
"CompileAndroidResources",
"DeJetify",
"DejetifySrcs",
"Desugar",
"DexBuilder",
"Jetify",
"JetifySrcs",
"LinkAndroidResources",
"MergeAndroidAssets",
"MergeManifests",
"ParseAndroidResources",
"StarlarkAARGenerator",
"StarlarkMergeCompiledAndroidResources",
"StarlarkRClassGenerator",
"Mock action");
/**
* Actions that support path mapping should call this method from {@link
* Action#getKey(ActionKeyContext, ArtifactExpander)}.
*
* <p>Compared to {@link #create(AbstractAction, OutputPathsMode)}, this method does not flatten
* nested sets and thus can't result in memory regressions.
*
* @param mnemonic the mnemonic of the action
* @param executionInfo the execution info of the action
* @param outputPathsMode the value of {@link CoreOptions#outputPathsMode}
* @param fingerprint the fingerprint to add to
*/
public static void addToFingerprint(
String mnemonic,
Map<String, String> executionInfo,
OutputPathsMode outputPathsMode,
Fingerprint fingerprint) {
// Creating a new PathMapper instance can be expensive, but isn't needed here: Whether and
// how path mapping applies to the action only depends on the output paths mode and the action
// inputs, which are already part of the action key.
OutputPathsMode effectiveOutputPathsMode =
getEffectiveOutputPathsMode(outputPathsMode, mnemonic, executionInfo);
if (effectiveOutputPathsMode == OutputPathsMode.STRIP) {
fingerprint.addString(StrippingPathMapper.GUID);
}
}
/**
* Actions that support path mapping should call this method when creating their {@link Spawn}.
*
* <p>The returned {@link PathMapper} has to be passed to {@link
* com.google.devtools.build.lib.actions.CommandLine#arguments(ArtifactExpander, PathMapper)},
* {@link com.google.devtools.build.lib.actions.CommandLines#expand(ArtifactExpander,
* PathFragment, PathMapper, CommandLineLimits)} )} or any other variants of these functions. The
* same instance should also be passed to the {@link Spawn} constructor so that the executor can
* obtain it via {@link Spawn#getPathMapper()}.
*
* <p>Note: This method flattens nested sets and should thus not be called from methods that are
* executed in the analysis phase.
*
* <p>Actions calling this method should also call {@link #addToFingerprint(String, Map,
* OutputPathsMode, Fingerprint)} from {@link Action#getKey(ActionKeyContext, ArtifactExpander)}
* to ensure correct incremental builds.
*
* @param action the {@link AbstractAction} for which a {@link Spawn} is to be created
* @param outputPathsMode the value of {@link CoreOptions#outputPathsMode}
* @return a {@link PathMapper} that maps paths of the action's inputs and outputs. May be {@link
* PathMapper#NOOP} if path mapping is not applicable to the action.
*/
public static PathMapper create(AbstractAction action, OutputPathsMode outputPathsMode) {
if (getEffectiveOutputPathsMode(
outputPathsMode, action.getMnemonic(), action.getExecutionInfo())
!= OutputPathsMode.STRIP) {
return PathMapper.NOOP;
}
return StrippingPathMapper.tryCreate(action).orElse(PathMapper.NOOP);
}
/**
* Helper method to simplify calling {@link #create(SpawnAction, OutputPathsMode)} for actions
* that store the configuration directly.
*
* @param configuration the configuration
* @return the value of
*/
public static OutputPathsMode getOutputPathsMode(
@Nullable BuildConfigurationValue configuration) {
if (configuration == null) {
return OutputPathsMode.OFF;
}
return configuration.getOptions().get(CoreOptions.class).outputPathsMode;
}
private static OutputPathsMode getEffectiveOutputPathsMode(
OutputPathsMode outputPathsMode, String mnemonic, Map<String, String> executionInfo) {
if (outputPathsMode == OutputPathsMode.STRIP
&& (SUPPORTED_MNEMONICS.contains(mnemonic)
|| executionInfo.containsKey(ExecutionRequirements.SUPPORTS_PATH_MAPPING))) {
return OutputPathsMode.STRIP;
}
return OutputPathsMode.OFF;
}
private PathMappers() {}
}