Skip to content

Commit c422565

Browse files
authored
[6.3.0] Support remote symlink outputs when building without the bytes. (#18476)
Fixes #13355. PiperOrigin-RevId: 534008963 Change-Id: Ia4f22f16960bcdae86c1a8820e3d47a3689876d8
1 parent 92cc903 commit c422565

File tree

2 files changed

+76
-61
lines changed

2 files changed

+76
-61
lines changed

src/main/java/com/google/devtools/build/lib/remote/RemoteExecutionService.java

Lines changed: 28 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -844,12 +844,6 @@ private void injectRemoteArtifacts(RemoteAction action, ActionResultMetadata met
844844

845845
for (Map.Entry<Path, DirectoryMetadata> entry : metadata.directories()) {
846846
DirectoryMetadata directory = entry.getValue();
847-
if (!directory.symlinks().isEmpty()) {
848-
throw new IOException(
849-
"Symlinks in action outputs are not yet supported by "
850-
+ "--experimental_remote_download_outputs=minimal");
851-
}
852-
853847
for (FileMetadata file : directory.files()) {
854848
remoteActionFileSystem.injectRemoteFile(
855849
file.path().asFragment(),
@@ -1082,7 +1076,8 @@ public InMemoryOutput downloadOutputs(RemoteAction action, RemoteActionResult re
10821076

10831077
ImmutableList.Builder<ListenableFuture<FileMetadata>> downloadsBuilder =
10841078
ImmutableList.builder();
1085-
boolean downloadOutputs = shouldDownloadOutputsFor(result, metadata);
1079+
1080+
boolean downloadOutputs = shouldDownloadOutputsFor(result);
10861081

10871082
// Download into temporary paths, then move everything at the end.
10881083
// This avoids holding the output lock while downloading, which would prevent the local branch
@@ -1102,10 +1097,6 @@ public InMemoryOutput downloadOutputs(RemoteAction action, RemoteActionResult re
11021097
checkState(
11031098
result.getExitCode() == 0,
11041099
"injecting remote metadata is only supported for successful actions (exit code 0).");
1105-
checkState(
1106-
metadata.symlinks.isEmpty(),
1107-
"Symlinks in action outputs are not yet supported by"
1108-
+ " --remote_download_outputs=minimal");
11091100
}
11101101

11111102
FileOutErr tmpOutErr = outErr.childOutErr();
@@ -1139,31 +1130,6 @@ public InMemoryOutput downloadOutputs(RemoteAction action, RemoteActionResult re
11391130

11401131
if (downloadOutputs) {
11411132
moveOutputsToFinalLocation(downloads, realToTmpPath);
1142-
1143-
List<SymlinkMetadata> symlinksInDirectories = new ArrayList<>();
1144-
for (Entry<Path, DirectoryMetadata> entry : metadata.directories()) {
1145-
for (SymlinkMetadata symlink : entry.getValue().symlinks()) {
1146-
// Symlinks should not be allowed inside directories because their semantics are unclear:
1147-
// tree artifacts are defined as a collection of regular files, and resolving the symlinks
1148-
// locally is asking for trouble. Sadly, we did start permitting relative symlinks at some
1149-
// point, so we can only ban the absolute ones.
1150-
// See https://github.com/bazelbuild/bazel/issues/16361.
1151-
if (symlink.target().isAbsolute()) {
1152-
throw new IOException(
1153-
String.format(
1154-
"Unsupported absolute symlink '%s' inside tree artifact '%s'",
1155-
symlink.path(), entry.getKey()));
1156-
}
1157-
symlinksInDirectories.add(symlink);
1158-
}
1159-
}
1160-
1161-
Iterable<SymlinkMetadata> symlinks =
1162-
Iterables.concat(metadata.symlinks(), symlinksInDirectories);
1163-
1164-
// Create the symbolic links after all downloads are finished, because dangling symlinks
1165-
// might not be supported on all platforms.
1166-
createSymlinks(symlinks);
11671133
} else {
11681134
ActionInput inMemoryOutput = null;
11691135
Digest inMemoryOutputDigest = null;
@@ -1197,6 +1163,31 @@ public InMemoryOutput downloadOutputs(RemoteAction action, RemoteActionResult re
11971163
}
11981164
}
11991165

1166+
List<SymlinkMetadata> symlinksInDirectories = new ArrayList<>();
1167+
for (Entry<Path, DirectoryMetadata> entry : metadata.directories()) {
1168+
for (SymlinkMetadata symlink : entry.getValue().symlinks()) {
1169+
// Symlinks should not be allowed inside directories because their semantics are unclear:
1170+
// tree artifacts are defined as a collection of regular files, and resolving the symlinks
1171+
// locally is asking for trouble. Sadly, we did start permitting relative symlinks at some
1172+
// point, so we can only ban the absolute ones.
1173+
// See https://github.com/bazelbuild/bazel/issues/16361.
1174+
if (symlink.target().isAbsolute()) {
1175+
throw new IOException(
1176+
String.format(
1177+
"Unsupported absolute symlink '%s' inside tree artifact '%s'",
1178+
symlink.path(), entry.getKey()));
1179+
}
1180+
symlinksInDirectories.add(symlink);
1181+
}
1182+
}
1183+
1184+
Iterable<SymlinkMetadata> symlinks =
1185+
Iterables.concat(metadata.symlinks(), symlinksInDirectories);
1186+
1187+
// Create the symbolic links after all downloads are finished, because dangling symlinks
1188+
// might not be supported on all platforms.
1189+
createSymlinks(symlinks);
1190+
12001191
if (result.success()) {
12011192
// Check that all mandatory outputs are created.
12021193
for (ActionInput output : action.getSpawn().getOutputFiles()) {
@@ -1237,8 +1228,7 @@ public InMemoryOutput downloadOutputs(RemoteAction action, RemoteActionResult re
12371228
return null;
12381229
}
12391230

1240-
private boolean shouldDownloadOutputsFor(
1241-
RemoteActionResult result, ActionResultMetadata metadata) {
1231+
private boolean shouldDownloadOutputsFor(RemoteActionResult result) {
12421232
if (remoteOptions.remoteOutputsMode.downloadAllOutputs()) {
12431233
return true;
12441234
}
@@ -1247,16 +1237,6 @@ private boolean shouldDownloadOutputsFor(
12471237
if (result.getExitCode() != 0) {
12481238
return true;
12491239
}
1250-
// Symlinks in actions output are not yet supported with BwoB.
1251-
if (!metadata.symlinks().isEmpty()) {
1252-
report(
1253-
Event.warn(
1254-
String.format(
1255-
"Symlinks in action outputs are not yet supported by --remote_download_minimal,"
1256-
+ " falling back to downloading all action outputs due to output symlink %s",
1257-
Iterables.get(metadata.symlinks(), 0).path())));
1258-
return true;
1259-
}
12601240
return false;
12611241
}
12621242

src/test/shell/bazel/remote/build_without_the_bytes_test.sh

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ EOF
360360
# Test that --remote_download_toplevel fetches inputs to symlink actions. In
361361
# particular, cc_binary links against a symlinked imported .so file, and only
362362
# the symlink is in the runfiles.
363-
function test_downloads_toplevel_symlinks() {
363+
function test_downloads_toplevel_symlink_action() {
364364
if [[ "$PLATFORM" == "darwin" ]]; then
365365
# TODO(b/37355380): This test is disabled due to RemoteWorker not supporting
366366
# setting SDKROOT and DEVELOPER_DIR appropriately, as is required of
@@ -409,25 +409,60 @@ EOF
409409
./bazel-bin/a/foo${EXE_EXT} || fail "bazel-bin/a/foo${EXE_EXT} failed to run"
410410
}
411411

412-
function test_symlink_outputs_warning_with_minimal() {
413-
mkdir -p a
414-
cat > a/input.txt <<'EOF'
415-
Input file
412+
function setup_symlink_output() {
413+
mkdir -p pkg
414+
415+
cat > pkg/defs.bzl <<EOF
416+
def _impl(ctx):
417+
sym = ctx.actions.declare_symlink(ctx.label.name)
418+
ctx.actions.run_shell(
419+
outputs = [sym],
420+
command = "ln -s {} {}".format(ctx.attr.target, sym.path),
421+
)
422+
return DefaultInfo(files = depset([sym]))
423+
424+
symlink = rule(
425+
implementation = _impl,
426+
attrs = {
427+
"target": attr.string(),
428+
},
429+
)
416430
EOF
417-
cat > a/BUILD <<'EOF'
418-
genrule(
419-
name = "foo",
420-
srcs = ["input.txt"],
421-
outs = ["output.txt", "output_symlink", "output_symlink_2"],
422-
cmd = "cp $< $(location :output.txt) && ln -s output.txt $(location output_symlink) && ln -s output.txt $(location output_symlink_2)",
431+
432+
cat > pkg/BUILD <<EOF
433+
load(":defs.bzl", "symlink")
434+
symlink(
435+
name = "sym",
436+
target = "target.txt",
423437
)
424438
EOF
439+
}
440+
441+
function test_downloads_toplevel_non_dangling_symlink_output() {
442+
setup_symlink_output
443+
touch pkg/target.txt
444+
445+
bazel build \
446+
--remote_executor=grpc://localhost:${worker_port} \
447+
--remote_download_toplevel \
448+
//pkg:sym >& $TEST_log || fail "Expected build of //pkg:sym to succeed"
449+
450+
if [[ "$(readlink bazel-bin/pkg/sym)" != "target.txt" ]]; then
451+
fail "Expected bazel-bin/pkg/sym to be a symlink pointing to target.txt"
452+
fi
453+
}
454+
455+
function test_downloads_toplevel_dangling_symlink_output() {
456+
setup_symlink_output
425457

426458
bazel build \
427459
--remote_executor=grpc://localhost:${worker_port} \
428460
--remote_download_minimal \
429-
//a:foo >& $TEST_log || fail "Expected build of //a:foo to succeed"
430-
expect_log "Symlinks in action outputs are not yet supported"
461+
//pkg:sym >& $TEST_log || fail "Expected build of //pkg:sym to succeed"
462+
463+
if [[ "$(readlink bazel-bin/pkg/sym)" != "target.txt" ]]; then
464+
fail "Expected bazel-bin/pkg/sym to be a symlink pointing to target.txt"
465+
fi
431466
}
432467

433468
# Regression test that --remote_download_toplevel does not crash when the

0 commit comments

Comments
 (0)