Skip to content

Commit 1f50b9a

Browse files
lfpino“Gowroji
authored and
“Gowroji
committed
Add a flag to force Bazel to download certain artifacts when using --remote_download_minimal
When using --remote_download_minimal Bazel only downloads to the client the minimum number of artifacts necessary for the build to succeed. This can be sometimes problematic when local development (i.e. IDE) requires some artifacts to do local work (e.g. syntax completion). We add a flag --experimental_remote_download_regex that allow specifying a regular expression that will force certain artifacts to be forced to downloaded. Tested locally that syntax errors disappear in IntelliJ when compiling Bazel itself with --remote_download_minimal and --experimental_remote_download_regex=".*jar$", also included an integration test with several cases. Signed-off-by: Luis Fernando Pino Duque <[email protected]> Closes bazelbuild#15638. PiperOrigin-RevId: 460672954 Change-Id: I3a4f18d046d2d91603a276f16173ad2a253480dd
1 parent 4f4303a commit 1f50b9a

File tree

2 files changed

+88
-17
lines changed

2 files changed

+88
-17
lines changed

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

+76-17
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@
145145
import java.util.concurrent.Executor;
146146
import java.util.concurrent.Phaser;
147147
import java.util.concurrent.atomic.AtomicBoolean;
148+
import java.util.function.Predicate;
149+
import java.util.regex.Matcher;
150+
import java.util.regex.Pattern;
148151
import javax.annotation.Nullable;
149152

150153
/**
@@ -172,6 +175,8 @@ public class RemoteExecutionService {
172175

173176
private final AtomicBoolean shutdown = new AtomicBoolean(false);
174177
private final AtomicBoolean buildInterrupted = new AtomicBoolean(false);
178+
private final boolean shouldForceDownloads;
179+
private final Predicate<String> shouldForceDownloadPredicate;
175180

176181
public RemoteExecutionService(
177182
Executor executor,
@@ -213,6 +218,20 @@ public RemoteExecutionService(
213218
this.captureCorruptedOutputsDir = captureCorruptedOutputsDir;
214219

215220
this.scheduler = Schedulers.from(executor, /*interruptibleWorker=*/ true);
221+
222+
String regex = remoteOptions.remoteDownloadRegex;
223+
// TODO(bazel-team): Consider adding a warning or more validation if the remoteDownloadRegex is
224+
// used without RemoteOutputsMode.MINIMAL.
225+
this.shouldForceDownloads =
226+
!regex.isEmpty()
227+
&& (remoteOptions.remoteOutputsMode == RemoteOutputsMode.MINIMAL
228+
|| remoteOptions.remoteOutputsMode == RemoteOutputsMode.TOPLEVEL);
229+
Pattern pattern = Pattern.compile(regex);
230+
this.shouldForceDownloadPredicate =
231+
path -> {
232+
Matcher m = pattern.matcher(path);
233+
return m.matches();
234+
};
216235
}
217236

218237
static Command buildCommand(
@@ -1011,24 +1030,10 @@ public InMemoryOutput downloadOutputs(RemoteAction action, RemoteActionResult re
10111030
/* exitCode = */ result.getExitCode(),
10121031
hasFilesToDownload(action.getSpawn().getOutputFiles(), filesToDownload));
10131032

1014-
if (downloadOutputs) {
1015-
HashSet<PathFragment> queuedFilePaths = new HashSet<>();
1016-
1017-
for (FileMetadata file : metadata.files()) {
1018-
PathFragment filePath = file.path().asFragment();
1019-
if (queuedFilePaths.add(filePath)) {
1020-
downloadsBuilder.add(downloadFile(action, file));
1021-
}
1022-
}
1033+
ImmutableList<ListenableFuture<FileMetadata>> forcedDownloads = ImmutableList.of();
10231034

1024-
for (Map.Entry<Path, DirectoryMetadata> entry : metadata.directories()) {
1025-
for (FileMetadata file : entry.getValue().files()) {
1026-
PathFragment filePath = file.path().asFragment();
1027-
if (queuedFilePaths.add(filePath)) {
1028-
downloadsBuilder.add(downloadFile(action, file));
1029-
}
1030-
}
1031-
}
1035+
if (downloadOutputs) {
1036+
downloadsBuilder.addAll(buildFilesToDownload(metadata, action));
10321037
} else {
10331038
checkState(
10341039
result.getExitCode() == 0,
@@ -1039,6 +1044,10 @@ public InMemoryOutput downloadOutputs(RemoteAction action, RemoteActionResult re
10391044
"Symlinks in action outputs are not yet supported by "
10401045
+ "--experimental_remote_download_outputs=minimal");
10411046
}
1047+
if (shouldForceDownloads) {
1048+
forcedDownloads =
1049+
buildFilesToDownloadWithPredicate(metadata, action, shouldForceDownloadPredicate);
1050+
}
10421051
}
10431052

10441053
FileOutErr tmpOutErr = outErr.childOutErr();
@@ -1053,6 +1062,19 @@ public InMemoryOutput downloadOutputs(RemoteAction action, RemoteActionResult re
10531062
try (SilentCloseable c = Profiler.instance().profile("Remote.download")) {
10541063
waitForBulkTransfer(downloads, /* cancelRemainingOnInterrupt= */ true);
10551064
} catch (Exception e) {
1065+
// TODO(bazel-team): Consider adding better case-by-case exception handling instead of just
1066+
// rethrowing
1067+
captureCorruptedOutputs(e);
1068+
deletePartialDownloadedOutputs(result, tmpOutErr, e);
1069+
throw e;
1070+
}
1071+
1072+
// TODO(bazel-team): Unify this block with the equivalent block above.
1073+
try (SilentCloseable c = Profiler.instance().profile("Remote.forcedDownload")) {
1074+
waitForBulkTransfer(forcedDownloads, /* cancelRemainingOnInterrupt= */ true);
1075+
} catch (Exception e) {
1076+
// TODO(bazel-team): Consider adding better case-by-case exception handling instead of just
1077+
// rethrowing
10561078
captureCorruptedOutputs(e);
10571079
deletePartialDownloadedOutputs(result, tmpOutErr, e);
10581080
throw e;
@@ -1083,6 +1105,13 @@ public InMemoryOutput downloadOutputs(RemoteAction action, RemoteActionResult re
10831105
// might not be supported on all platforms
10841106
createSymlinks(symlinks);
10851107
} else {
1108+
// TODO(bazel-team): We should unify this if-block to rely on downloadOutputs above but, as of
1109+
// 2022-07-05, downloadOuputs' semantics isn't exactly the same as build-without-the-bytes
1110+
// which is necessary for using remoteDownloadRegex.
1111+
if (!forcedDownloads.isEmpty()) {
1112+
moveOutputsToFinalLocation(forcedDownloads);
1113+
}
1114+
10861115
ActionInput inMemoryOutput = null;
10871116
Digest inMemoryOutputDigest = null;
10881117
PathFragment inMemoryOutputPath = getInMemoryOutputPath(action.getSpawn());
@@ -1120,6 +1149,36 @@ public InMemoryOutput downloadOutputs(RemoteAction action, RemoteActionResult re
11201149
return null;
11211150
}
11221151

1152+
private ImmutableList<ListenableFuture<FileMetadata>> buildFilesToDownload(
1153+
ActionResultMetadata metadata, RemoteAction action) {
1154+
Predicate<String> alwaysTrue = unused -> true;
1155+
return buildFilesToDownloadWithPredicate(metadata, action, alwaysTrue);
1156+
}
1157+
1158+
private ImmutableList<ListenableFuture<FileMetadata>> buildFilesToDownloadWithPredicate(
1159+
ActionResultMetadata metadata, RemoteAction action, Predicate<String> predicate) {
1160+
HashSet<PathFragment> queuedFilePaths = new HashSet<>();
1161+
ImmutableList.Builder<ListenableFuture<FileMetadata>> builder = new ImmutableList.Builder<>();
1162+
1163+
for (FileMetadata file : metadata.files()) {
1164+
PathFragment filePath = file.path().asFragment();
1165+
if (queuedFilePaths.add(filePath) && predicate.test(file.path.toString())) {
1166+
builder.add(downloadFile(action, file));
1167+
}
1168+
}
1169+
1170+
for (Map.Entry<Path, DirectoryMetadata> entry : metadata.directories()) {
1171+
for (FileMetadata file : entry.getValue().files()) {
1172+
PathFragment filePath = file.path().asFragment();
1173+
if (queuedFilePaths.add(filePath) && predicate.test(file.path.toString())) {
1174+
builder.add(downloadFile(action, file));
1175+
}
1176+
}
1177+
}
1178+
1179+
return builder.build();
1180+
}
1181+
11231182
private static String prettyPrint(ActionInput actionInput) {
11241183
if (actionInput instanceof Artifact) {
11251184
return ((Artifact) actionInput).prettyPrint();

src/main/java/com/google/devtools/build/lib/remote/options/RemoteOptions.java

+12
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,18 @@ public RemoteOutputsStrategyConverter() {
579579
+ "`all` to print always.")
580580
public ExecutionMessagePrintMode remotePrintExecutionMessages;
581581

582+
@Option(
583+
name = "experimental_remote_download_regex",
584+
defaultValue = "",
585+
documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
586+
effectTags = {OptionEffectTag.AFFECTS_OUTPUTS},
587+
help =
588+
"Force Bazel to download the artifacts that match the given regexp. To be used in"
589+
+ " conjunction with --remote_download_minimal or --remote_download_toplevel to allow"
590+
+ " the client to request certain artifacts that might be needed locally (e.g. IDE"
591+
+ " support)")
592+
public String remoteDownloadRegex;
593+
582594
// The below options are not configurable by users, only tests.
583595
// This is part of the effort to reduce the overall number of flags.
584596

0 commit comments

Comments
 (0)