Skip to content

Commit

Permalink
Let RemoteOutputService ensure bazel-out/ is not a symlink
Browse files Browse the repository at this point in the history
When doing a regular build without passing in --remote_download_*,
ExecutionTool will automatically clean up any traces of an OutputService
that replaces bazel-out/ with a symlink pointing to a remote file
system.

The --remote_download_* option is implemented by installing an
OutputService of its own, meaning that the regular logic in
ExecutionTool is skipped. The rationale being that an OutputService
essentially takes ownership of bazel-out/.

This change extends RemoteOutputService to be a more complete
implementation of OutputService that ensures that bazel-out/ is set up
properly. This improves interoperability with changes such as the gRPC
based Remote Output Service protocol (see bazelbuild#12823).
  • Loading branch information
EdSchouten committed Aug 31, 2023
1 parent d6f6bd2 commit cdadd2c
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,7 @@ private ModifiedFileSet startBuildAndDetermineModifiedOutputFiles(
try (SilentCloseable c = Profiler.instance().profile("outputService.startBuild")) {
modifiedOutputFiles =
outputService.startBuild(
env.getDirectories().getOutputPath(env.getWorkspaceName()),
env.getReporter(), buildId, request.getBuildOptions().finalizeActions);
informedOutputServiceToStartTheBuild = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,16 @@
import com.google.devtools.build.lib.actions.cache.OutputMetadataStore;
import com.google.devtools.build.lib.buildtool.buildevent.ExecutionPhaseCompleteEvent;
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.server.FailureDetails.Execution;
import com.google.devtools.build.lib.server.FailureDetails.Execution.Code;
import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
import com.google.devtools.build.lib.util.AbruptExitException;
import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.vfs.BatchStat;
import com.google.devtools.build.lib.vfs.FileSystem;
import com.google.devtools.build.lib.vfs.ModifiedFileSet;
import com.google.devtools.build.lib.vfs.OutputService;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.vfs.Root;
import com.google.devtools.build.skyframe.SkyFunction.Environment;
Expand Down Expand Up @@ -117,7 +122,28 @@ public String getFilesSystemName() {

@Override
public ModifiedFileSet startBuild(
EventHandler eventHandler, UUID buildId, boolean finalizeActions) throws AbruptExitException {
Path outputPath,
EventHandler eventHandler,
UUID buildId,
boolean finalizeActions)
throws AbruptExitException {
// One of the responsibilities of OutputService.startBuild() is that
// it ensures the output path is valid. If the previous
// OutputService redirected the output path to a remote location, we
// must undo this.
if (outputPath.isSymbolicLink()) {
try {
outputPath.delete();
} catch (IOException e) {
throw new AbruptExitException(
DetailedExitCode.of(
FailureDetail.newBuilder()
.setMessage(String.format("Couldn't remove output path symlink: %s", e.getMessage()))
.setExecution(Execution.newBuilder().setCode(Code.LOCAL_OUTPUT_DIRECTORY_SYMLINK_FAILURE))
.build()),
e);
}
}
return ModifiedFileSet.EVERYTHING_MODIFIED;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,19 @@ default RemoteArtifactChecker getRemoteArtifactChecker() {
/**
* Start the build.
*
* @param outputPath absolute path pointing to the output directory
* @param buildId the UUID build identifier
* @param finalizeActions whether this build is finalizing actions so that the output service can
* track output tree modifications
* @return a ModifiedFileSet of changed output files.
* @throws BuildFailedException if build preparation failed
* @throws InterruptedException
*/
ModifiedFileSet startBuild(EventHandler eventHandler, UUID buildId, boolean finalizeActions)
ModifiedFileSet startBuild(
Path outputPath,
EventHandler eventHandler,
UUID buildId,
boolean finalizeActions)
throws BuildFailedException, AbruptExitException, InterruptedException;

/** Flush and wait for in-progress downloads. */
Expand Down
31 changes: 31 additions & 0 deletions src/test/shell/bazel/remote/build_without_the_bytes_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2049,4 +2049,35 @@ EOF
expect_log "Hello World"
}

function test_output_path_is_symlink() {
cat > BUILD << 'EOF'
genrule(
name = "foo",
outs = ["bar"],
cmd = "touch $@",
)
EOF
bazel build \
--remote_executor=grpc://localhost:${worker_port} \
--remote_download_minimal \
//:foo >& $TEST_log || fail "Failed to build //:foo"

# --remote_download_minimal and --remote_download_toplevel install an
# OutputService. One of the responsibilities of an OutputService is
# that it ensures a valid output path is present.
#
# Simulate the case where another OutputService replaced the output
# path with a symbolic link. If Bazel is rerun with
# --remote_download_minimal, it should remove the symbolic link, so
# that builds can take place on the local file system.
output_path=$(bazel info output_path)
rm -rf $output_path
ln -s /nonexistent $output_path

bazel build \
--remote_executor=grpc://localhost:${worker_port} \
--remote_download_minimal \
//:foo >& $TEST_log || fail "Failed to build //:foo"
}

run_suite "Build without the Bytes tests"

0 comments on commit cdadd2c

Please sign in to comment.