blob: d2c1c2c5448a38336c6a6a4a39a0f34e2f12ea25 [file] [log] [blame]
// Copyright 2020 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.remote.common;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.devtools.build.lib.actions.ActionInput;
import com.google.devtools.build.lib.actions.ActionInputHelper;
import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.exec.SpawnInputExpander;
import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionContext;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.IOException;
import java.util.SortedMap;
/**
* A {@link RemotePathResolver} is used to resolve input/output paths for remote execution from
* Bazel's internal path, or vice versa.
*/
public interface RemotePathResolver {
/**
* Returns the {@code workingDirectory} for a remote action. Empty string if working directory is
* the input root.
*/
String getWorkingDirectory();
/**
* Returns a {@link SortedMap} which maps from input paths for remote action to {@link
* ActionInput}.
*/
SortedMap<PathFragment, ActionInput> getInputMapping(SpawnExecutionContext context)
throws IOException, ForbiddenActionInputException;
void walkInputs(
Spawn spawn, SpawnExecutionContext context, SpawnInputExpander.InputVisitor visitor)
throws IOException, ForbiddenActionInputException;
/** Resolves the output path relative to input root for the given {@link Path}. */
String localPathToOutputPath(Path path);
/**
* Resolves the output path relative to input root for the given {@link PathFragment}.
*
* @param execPath a path fragment relative to {@code execRoot}.
*/
String localPathToOutputPath(PathFragment execPath);
/** Resolves the output path relative to input root for the {@link ActionInput}. */
default String localPathToOutputPath(ActionInput actionInput) {
return localPathToOutputPath(actionInput.getExecPath());
}
/**
* Resolves the local {@link Path} of an output file.
*
* @param outputPath the return value of {@link #localPathToOutputPath(PathFragment)}.
*/
Path outputPathToLocalPath(String outputPath);
/** Resolves the local {@link Path} for the {@link ActionInput}. */
default Path outputPathToLocalPath(ActionInput actionInput) {
String outputPath = localPathToOutputPath(actionInput.getExecPath());
return outputPathToLocalPath(outputPath);
}
/** Creates the default {@link RemotePathResolver}. */
static RemotePathResolver createDefault(Path execRoot) {
return new DefaultRemotePathResolver(execRoot);
}
/**
* The default {@link RemotePathResolver} which use {@code execRoot} as input root and do NOT set
* {@code workingDirectory} for remote actions.
*/
class DefaultRemotePathResolver implements RemotePathResolver {
private final Path execRoot;
public DefaultRemotePathResolver(Path execRoot) {
this.execRoot = execRoot;
}
@Override
public String getWorkingDirectory() {
return "";
}
@Override
public SortedMap<PathFragment, ActionInput> getInputMapping(SpawnExecutionContext context)
throws IOException, ForbiddenActionInputException {
return context.getInputMapping(PathFragment.EMPTY_FRAGMENT);
}
@Override
public void walkInputs(
Spawn spawn, SpawnExecutionContext context, SpawnInputExpander.InputVisitor visitor)
throws IOException, ForbiddenActionInputException {
context
.getSpawnInputExpander()
.walkInputs(
spawn,
context.getArtifactExpander(),
PathFragment.EMPTY_FRAGMENT,
context.getMetadataProvider(),
visitor);
}
@Override
public String localPathToOutputPath(Path path) {
return path.relativeTo(execRoot).getPathString();
}
@Override
public String localPathToOutputPath(PathFragment execPath) {
return execPath.getPathString();
}
@Override
public Path outputPathToLocalPath(String outputPath) {
return execRoot.getRelative(outputPath);
}
@Override
public Path outputPathToLocalPath(ActionInput actionInput) {
return ActionInputHelper.toInputPath(actionInput, execRoot);
}
}
/**
* A {@link RemotePathResolver} used when {@code --experimental_sibling_repository_layout} is set.
* Use parent directory of {@code execRoot} and set {@code workingDirectory} to the base name of
* {@code execRoot}.
*
* <p>The paths of outputs are relative to {@code workingDirectory} if {@code
* --incompatible_remote_output_paths_relative_to_input_root} is not set, otherwise, relative to
* input root.
*/
class SiblingRepositoryLayoutResolver implements RemotePathResolver {
private final Path execRoot;
private final boolean incompatibleRemoteOutputPathsRelativeToInputRoot;
public SiblingRepositoryLayoutResolver(Path execRoot) {
this(execRoot, /* incompatibleRemoteOutputPathsRelativeToInputRoot= */ false);
}
public SiblingRepositoryLayoutResolver(
Path execRoot, boolean incompatibleRemoteOutputPathsRelativeToInputRoot) {
this.execRoot = execRoot;
this.incompatibleRemoteOutputPathsRelativeToInputRoot =
incompatibleRemoteOutputPathsRelativeToInputRoot;
}
@Override
public String getWorkingDirectory() {
return execRoot.getBaseName();
}
@Override
public SortedMap<PathFragment, ActionInput> getInputMapping(SpawnExecutionContext context)
throws IOException, ForbiddenActionInputException {
// The "root directory" of the action from the point of view of RBE is the parent directory of
// the execroot locally. This is so that paths of artifacts in external repositories don't
// start with an uplevel reference.
return context.getInputMapping(PathFragment.create(checkNotNull(getWorkingDirectory())));
}
@Override
public void walkInputs(
Spawn spawn, SpawnExecutionContext context, SpawnInputExpander.InputVisitor visitor)
throws IOException, ForbiddenActionInputException {
context
.getSpawnInputExpander()
.walkInputs(
spawn,
context.getArtifactExpander(),
PathFragment.create(checkNotNull(getWorkingDirectory())),
context.getMetadataProvider(),
visitor);
}
private Path getBase() {
if (incompatibleRemoteOutputPathsRelativeToInputRoot) {
return execRoot.getParentDirectory();
} else {
return execRoot;
}
}
@Override
public String localPathToOutputPath(Path path) {
return path.relativeTo(getBase()).getPathString();
}
@Override
public String localPathToOutputPath(PathFragment execPath) {
return localPathToOutputPath(execRoot.getRelative(execPath));
}
@Override
public Path outputPathToLocalPath(String outputPath) {
return getBase().getRelative(outputPath);
}
@Override
public Path outputPathToLocalPath(ActionInput actionInput) {
return ActionInputHelper.toInputPath(actionInput, execRoot);
}
}
}