Skip to content

Commit ca30372

Browse files
fmeumcopybara-github
authored andcommitted
Gracefully handle output symlinks with BwoB
If an action creates output symlinks, `--remote_download_minimal` now falls back to downloading all its outputs rather than failing with an exception, which most of the time led to a broken build. Closes bazelbuild#18075. PiperOrigin-RevId: 524264497 Change-Id: Id0dc77baf1f2c7c54553cf4d7f2d52ce889d1b8b
1 parent 9f36740 commit ca30372

File tree

3 files changed

+60
-16
lines changed

3 files changed

+60
-16
lines changed

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

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@
9999
import com.google.devtools.build.lib.remote.common.RemotePathResolver;
100100
import com.google.devtools.build.lib.remote.merkletree.MerkleTree;
101101
import com.google.devtools.build.lib.remote.options.RemoteOptions;
102-
import com.google.devtools.build.lib.remote.options.RemoteOutputsMode;
103102
import com.google.devtools.build.lib.remote.util.DigestUtil;
104103
import com.google.devtools.build.lib.remote.util.TempPathGenerator;
105104
import com.google.devtools.build.lib.remote.util.TracingMetadataUtils;
@@ -1136,13 +1135,7 @@ public InMemoryOutput downloadOutputs(RemoteAction action, RemoteActionResult re
11361135

11371136
ImmutableList.Builder<ListenableFuture<FileMetadata>> downloadsBuilder =
11381137
ImmutableList.builder();
1139-
RemoteOutputsMode remoteOutputsMode = remoteOptions.remoteOutputsMode;
1140-
boolean downloadOutputs =
1141-
remoteOutputsMode.downloadAllOutputs()
1142-
||
1143-
// In case the action failed, download all outputs. It might be helpful for debugging
1144-
// and there is no point in injecting output metadata of a failed action.
1145-
result.getExitCode() != 0;
1138+
boolean downloadOutputs = shouldDownloadOutputsFor(result, metadata);
11461139

11471140
// Download into temporary paths, then move everything at the end.
11481141
// This avoids holding the output lock while downloading, which would prevent the local branch
@@ -1162,12 +1155,10 @@ public InMemoryOutput downloadOutputs(RemoteAction action, RemoteActionResult re
11621155
checkState(
11631156
result.getExitCode() == 0,
11641157
"injecting remote metadata is only supported for successful actions (exit code 0).");
1165-
1166-
if (!metadata.symlinks().isEmpty()) {
1167-
throw new IOException(
1168-
"Symlinks in action outputs are not yet supported by "
1169-
+ "--experimental_remote_download_outputs=minimal");
1170-
}
1158+
checkState(
1159+
metadata.symlinks.isEmpty(),
1160+
"Symlinks in action outputs are not yet supported by"
1161+
+ " --remote_download_outputs=minimal");
11711162
}
11721163

11731164
FileOutErr tmpOutErr = outErr.childOutErr();
@@ -1300,6 +1291,29 @@ public InMemoryOutput downloadOutputs(RemoteAction action, RemoteActionResult re
13001291
return null;
13011292
}
13021293

1294+
private boolean shouldDownloadOutputsFor(
1295+
RemoteActionResult result, ActionResultMetadata metadata) {
1296+
if (remoteOptions.remoteOutputsMode.downloadAllOutputs()) {
1297+
return true;
1298+
}
1299+
// In case the action failed, download all outputs. It might be helpful for debugging and there
1300+
// is no point in injecting output metadata of a failed action.
1301+
if (result.getExitCode() != 0) {
1302+
return true;
1303+
}
1304+
// Symlinks in actions output are not yet supported with BwoB.
1305+
if (!metadata.symlinks().isEmpty()) {
1306+
report(
1307+
Event.warn(
1308+
String.format(
1309+
"Symlinks in action outputs are not yet supported by --remote_download_minimal,"
1310+
+ " falling back to downloading all action outputs due to output symlink %s",
1311+
Iterables.getOnlyElement(metadata.symlinks()).path())));
1312+
return true;
1313+
}
1314+
return false;
1315+
}
1316+
13031317
private ImmutableList<ListenableFuture<FileMetadata>> buildFilesToDownload(
13041318
RemoteActionExecutionContext context,
13051319
ProgressStatusListener progressStatusListener,

src/test/java/com/google/devtools/build/lib/remote/BuildWithoutTheBytesIntegrationTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import static com.google.common.truth.Truth.assertThat;
1818
import static com.google.devtools.build.lib.remote.util.IntegrationTestUtils.startWorker;
1919
import static org.junit.Assert.assertThrows;
20+
import static org.junit.Assume.assumeFalse;
2021

2122
import com.google.common.collect.ImmutableList;
2223
import com.google.devtools.build.lib.actions.Artifact;
@@ -32,6 +33,7 @@
3233
import com.google.devtools.build.lib.util.OS;
3334
import com.google.devtools.build.lib.vfs.FileSystemUtils;
3435
import com.google.devtools.build.lib.vfs.Path;
36+
import com.google.devtools.build.lib.vfs.Symlinks;
3537
import java.io.IOException;
3638
import org.junit.After;
3739
import org.junit.Test;
@@ -433,6 +435,34 @@ public void symlinkToNestedDirectory() throws Exception {
433435
buildTarget("//a:one_local", "//a:two_local", "//a:one_remote", "//a:two_remote");
434436
}
435437

438+
@Test
439+
public void outputSymlinkHandledGracefully() throws Exception {
440+
// Symlinks may not be supported on Windows
441+
assumeFalse(OS.getCurrent() == OS.WINDOWS);
442+
write(
443+
"a/defs.bzl",
444+
"def _impl(ctx):",
445+
" out = ctx.actions.declare_symlink(ctx.label.name)",
446+
" ctx.actions.run_shell(",
447+
" inputs = [],",
448+
" outputs = [out],",
449+
" command = 'ln -s hello $1',",
450+
" arguments = [out.path],",
451+
" )",
452+
" return DefaultInfo(files = depset([out]))",
453+
"",
454+
"my_rule = rule(",
455+
" implementation = _impl,",
456+
")");
457+
458+
write("a/BUILD", "load(':defs.bzl', 'my_rule')", "", "my_rule(name = 'hello')");
459+
460+
buildTarget("//a:hello");
461+
462+
Path outputPath = getOutputPath("a/hello");
463+
assertThat(outputPath.stat(Symlinks.NOFOLLOW).isSymbolicLink()).isTrue();
464+
}
465+
436466
@Test
437467
public void replaceOutputDirectoryWithFile() throws Exception {
438468
write(

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ 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_not_allowed_with_minimial() {
412+
function test_symlink_outputs_warning_with_minimal() {
413413
mkdir -p a
414414
cat > a/input.txt <<'EOF'
415415
Input file
@@ -426,7 +426,7 @@ EOF
426426
bazel build \
427427
--remote_executor=grpc://localhost:${worker_port} \
428428
--remote_download_minimal \
429-
//a:foo >& $TEST_log && fail "Expected failure to build //a:foo"
429+
//a:foo >& $TEST_log || fail "Expected build of //a:foo to succeed"
430430
expect_log "Symlinks in action outputs are not yet supported"
431431
}
432432

0 commit comments

Comments
 (0)