ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 1 | // Copyright 2017 The Bazel Authors. All rights reserved. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | package com.google.devtools.build.lib.remote; |
| 16 | |
Jakob Buchgraber | 4b3c2eb | 2019-04-04 02:01:48 -0700 | [diff] [blame] | 17 | import static com.google.devtools.build.lib.profiler.ProfilerTask.REMOTE_DOWNLOAD; |
| 18 | import static com.google.devtools.build.lib.profiler.ProfilerTask.REMOTE_EXECUTION; |
| 19 | import static com.google.devtools.build.lib.profiler.ProfilerTask.UPLOAD_TIME; |
buchgr | d480c5f | 2019-04-03 00:53:34 -0700 | [diff] [blame] | 20 | import static com.google.devtools.build.lib.remote.util.Utils.createSpawnResult; |
buchgr | ff008f4 | 2018-06-02 14:13:43 -0700 | [diff] [blame] | 21 | import static com.google.devtools.build.lib.remote.util.Utils.getFromFuture; |
buchgr | d480c5f | 2019-04-03 00:53:34 -0700 | [diff] [blame] | 22 | import static com.google.devtools.build.lib.remote.util.Utils.getInMemoryOutputPath; |
Jakob Buchgraber | 50c1004 | 2019-04-11 02:11:19 -0700 | [diff] [blame] | 23 | import static com.google.devtools.build.lib.remote.util.Utils.hasTopLevelOutputs; |
| 24 | import static com.google.devtools.build.lib.remote.util.Utils.shouldDownloadAllSpawnOutputs; |
buchgr | ff008f4 | 2018-06-02 14:13:43 -0700 | [diff] [blame] | 25 | |
olaola | f0aa55d | 2018-08-16 08:51:06 -0700 | [diff] [blame] | 26 | import build.bazel.remote.execution.v2.Action; |
| 27 | import build.bazel.remote.execution.v2.ActionResult; |
| 28 | import build.bazel.remote.execution.v2.Command; |
| 29 | import build.bazel.remote.execution.v2.Digest; |
| 30 | import build.bazel.remote.execution.v2.ExecuteRequest; |
| 31 | import build.bazel.remote.execution.v2.ExecuteResponse; |
| 32 | import build.bazel.remote.execution.v2.LogFile; |
| 33 | import build.bazel.remote.execution.v2.Platform; |
Jakob Buchgraber | 562fcf9 | 2017-07-27 12:51:13 +0200 | [diff] [blame] | 34 | import com.google.common.annotations.VisibleForTesting; |
Jakob Buchgraber | 716e4e3 | 2019-01-21 07:59:23 -0800 | [diff] [blame] | 35 | import com.google.common.base.Preconditions; |
olaola | 1cbba98 | 2017-07-27 02:52:37 +0200 | [diff] [blame] | 36 | import com.google.common.base.Throwables; |
Benjamin Peterson | dd3ddb0 | 2018-05-03 09:20:08 -0700 | [diff] [blame] | 37 | import com.google.common.collect.ImmutableList; |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 38 | import com.google.common.collect.ImmutableMap; |
Jakob Buchgraber | 50c1004 | 2019-04-11 02:11:19 -0700 | [diff] [blame] | 39 | import com.google.common.collect.ImmutableSet; |
buchgr | 7f72544 | 2019-03-08 03:20:25 -0800 | [diff] [blame] | 40 | import com.google.common.collect.Maps; |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 41 | import com.google.devtools.build.lib.actions.ActionInput; |
olaola | 2838dd9 | 2018-03-08 09:56:12 -0800 | [diff] [blame] | 42 | import com.google.devtools.build.lib.actions.Artifact; |
tomlu | 09fe062 | 2018-06-19 12:55:39 -0700 | [diff] [blame] | 43 | import com.google.devtools.build.lib.actions.CommandLines.ParamFileActionInput; |
olaola | 460a105 | 2017-06-09 04:33:25 +0200 | [diff] [blame] | 44 | import com.google.devtools.build.lib.actions.ExecException; |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 45 | import com.google.devtools.build.lib.actions.Spawn; |
ruperts | da40fbf | 2017-09-22 05:59:42 +0200 | [diff] [blame] | 46 | import com.google.devtools.build.lib.actions.SpawnResult; |
| 47 | import com.google.devtools.build.lib.actions.SpawnResult.Status; |
olaola | 460a105 | 2017-06-09 04:33:25 +0200 | [diff] [blame] | 48 | import com.google.devtools.build.lib.actions.Spawns; |
Ola Rozenfeld | ccbe40a | 2017-07-17 10:05:59 +0200 | [diff] [blame] | 49 | import com.google.devtools.build.lib.actions.cache.VirtualActionInput; |
Googler | 77ef8d6 | 2019-07-23 11:55:53 -0700 | [diff] [blame] | 50 | import com.google.devtools.build.lib.analysis.platform.PlatformUtils; |
ulfjack | 72143fe | 2017-04-05 14:04:30 +0000 | [diff] [blame] | 51 | import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; |
Benjamin Peterson | 3ff87f7 | 2017-08-21 18:41:45 +0200 | [diff] [blame] | 52 | import com.google.devtools.build.lib.events.Event; |
| 53 | import com.google.devtools.build.lib.events.Reporter; |
tomlu | 09fe062 | 2018-06-19 12:55:39 -0700 | [diff] [blame] | 54 | import com.google.devtools.build.lib.exec.ExecutionOptions; |
ulfjack | 2ce304a | 2017-04-04 08:04:01 +0000 | [diff] [blame] | 55 | import com.google.devtools.build.lib.exec.SpawnRunner; |
ulfjack | 278a84b | 2018-11-05 08:44:22 -0800 | [diff] [blame] | 56 | import com.google.devtools.build.lib.profiler.Profiler; |
Jakob Buchgraber | 4b3c2eb | 2019-04-04 02:01:48 -0700 | [diff] [blame] | 57 | import com.google.devtools.build.lib.profiler.ProfilerTask; |
ulfjack | 278a84b | 2018-11-05 08:44:22 -0800 | [diff] [blame] | 58 | import com.google.devtools.build.lib.profiler.SilentCloseable; |
buchgr | 7f72544 | 2019-03-08 03:20:25 -0800 | [diff] [blame] | 59 | import com.google.devtools.build.lib.remote.merkletree.MerkleTree; |
Jakob Buchgraber | 75b7ed4 | 2019-03-27 10:27:13 -0700 | [diff] [blame] | 60 | import com.google.devtools.build.lib.remote.options.RemoteOptions; |
buchgr | d480c5f | 2019-04-03 00:53:34 -0700 | [diff] [blame] | 61 | import com.google.devtools.build.lib.remote.options.RemoteOutputsMode; |
Googler | 922d1e6 | 2018-03-05 14:49:00 -0800 | [diff] [blame] | 62 | import com.google.devtools.build.lib.remote.util.DigestUtil; |
| 63 | import com.google.devtools.build.lib.remote.util.DigestUtil.ActionKey; |
| 64 | import com.google.devtools.build.lib.remote.util.TracingMetadataUtils; |
Jakob Buchgraber | a79a4b6 | 2019-06-23 02:06:20 -0700 | [diff] [blame] | 65 | import com.google.devtools.build.lib.remote.util.Utils; |
buchgr | d480c5f | 2019-04-03 00:53:34 -0700 | [diff] [blame] | 66 | import com.google.devtools.build.lib.remote.util.Utils.InMemoryOutput; |
buchgr | a659135 | 2017-09-01 13:14:19 +0200 | [diff] [blame] | 67 | import com.google.devtools.build.lib.util.ExitCode; |
buchgr | 9626bb4 | 2017-08-11 13:54:04 +0200 | [diff] [blame] | 68 | import com.google.devtools.build.lib.util.io.FileOutErr; |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 69 | import com.google.devtools.build.lib.vfs.Path; |
| 70 | import com.google.devtools.build.lib.vfs.PathFragment; |
buchgr | 7f72544 | 2019-03-08 03:20:25 -0800 | [diff] [blame] | 71 | import com.google.protobuf.Message; |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 72 | import io.grpc.Context; |
olaola | 1cbba98 | 2017-07-27 02:52:37 +0200 | [diff] [blame] | 73 | import io.grpc.Status.Code; |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 74 | import java.io.IOException; |
tomlu | 09fe062 | 2018-06-19 12:55:39 -0700 | [diff] [blame] | 75 | import java.io.OutputStream; |
ulfjack | deab0cf | 2017-08-08 13:08:24 +0200 | [diff] [blame] | 76 | import java.time.Duration; |
ulfjack | 2936047 | 2017-07-07 09:32:53 -0400 | [diff] [blame] | 77 | import java.util.ArrayList; |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 78 | import java.util.Collection; |
olaola | 0d31014 | 2017-07-14 10:07:50 +0200 | [diff] [blame] | 79 | import java.util.Collections; |
Ola Rozenfeld | ccbe40a | 2017-07-17 10:05:59 +0200 | [diff] [blame] | 80 | import java.util.HashMap; |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 81 | import java.util.List; |
Ola Rozenfeld | ccbe40a | 2017-07-17 10:05:59 +0200 | [diff] [blame] | 82 | import java.util.Map; |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 83 | import java.util.SortedMap; |
| 84 | import java.util.TreeSet; |
Benjamin Peterson | 3ff87f7 | 2017-08-21 18:41:45 +0200 | [diff] [blame] | 85 | import java.util.concurrent.atomic.AtomicBoolean; |
philwo | d6ce264 | 2018-07-30 05:24:50 -0700 | [diff] [blame] | 86 | import java.util.concurrent.atomic.AtomicReference; |
ulfjack | 3fbd7c4 | 2017-07-10 13:19:19 +0200 | [diff] [blame] | 87 | import javax.annotation.Nullable; |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 88 | |
olaola | 460a105 | 2017-06-09 04:33:25 +0200 | [diff] [blame] | 89 | /** A client for the remote execution service. */ |
ulfjack | 72143fe | 2017-04-05 14:04:30 +0000 | [diff] [blame] | 90 | @ThreadSafe |
buchgr | d480c5f | 2019-04-03 00:53:34 -0700 | [diff] [blame] | 91 | public class RemoteSpawnRunner implements SpawnRunner { |
| 92 | private static final int POSIX_TIMEOUT_EXIT_CODE = /*SIGNAL_BASE=*/ 128 + /*SIGALRM=*/ 14; |
ulfjack | 32e7a1c | 2017-11-28 01:14:34 -0800 | [diff] [blame] | 93 | |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 94 | private final Path execRoot; |
tomlu | 09fe062 | 2018-06-19 12:55:39 -0700 | [diff] [blame] | 95 | private final RemoteOptions remoteOptions; |
| 96 | private final ExecutionOptions executionOptions; |
philwo | d6ce264 | 2018-07-30 05:24:50 -0700 | [diff] [blame] | 97 | private final AtomicReference<SpawnRunner> fallbackRunner; |
olaola | 1cbba98 | 2017-07-27 02:52:37 +0200 | [diff] [blame] | 98 | private final boolean verboseFailures; |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 99 | |
Benjamin Peterson | 3ff87f7 | 2017-08-21 18:41:45 +0200 | [diff] [blame] | 100 | @Nullable private final Reporter cmdlineReporter; |
Jakob Buchgraber | 716e4e3 | 2019-01-21 07:59:23 -0800 | [diff] [blame] | 101 | private final GrpcRemoteCache remoteCache; |
ulfjack | 3fbd7c4 | 2017-07-10 13:19:19 +0200 | [diff] [blame] | 102 | @Nullable private final GrpcRemoteExecutor remoteExecutor; |
George Gensure | f600b69 | 2018-07-10 14:13:50 -0700 | [diff] [blame] | 103 | @Nullable private final RemoteRetrier retrier; |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 104 | private final String buildRequestId; |
| 105 | private final String commandId; |
buchgr | 559a07d | 2017-11-30 11:09:35 -0800 | [diff] [blame] | 106 | private final DigestUtil digestUtil; |
olaola | bf326fa | 2018-03-21 11:22:12 -0700 | [diff] [blame] | 107 | private final Path logDir; |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 108 | |
Jakob Buchgraber | 50c1004 | 2019-04-11 02:11:19 -0700 | [diff] [blame] | 109 | /** |
| 110 | * Set of artifacts that are top level outputs |
| 111 | * |
| 112 | * <p>This set is empty unless {@link RemoteOutputsMode#TOPLEVEL} is specified. If so, this set is |
| 113 | * used to decide whether to download an output. |
| 114 | */ |
Jakob Buchgraber | 34784c3 | 2019-07-22 07:40:14 -0700 | [diff] [blame] | 115 | private final ImmutableSet<ActionInput> topLevelOutputs; |
Jakob Buchgraber | 50c1004 | 2019-04-11 02:11:19 -0700 | [diff] [blame] | 116 | |
Benjamin Peterson | 3ff87f7 | 2017-08-21 18:41:45 +0200 | [diff] [blame] | 117 | // Used to ensure that a warning is reported only once. |
| 118 | private final AtomicBoolean warningReported = new AtomicBoolean(); |
| 119 | |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 120 | RemoteSpawnRunner( |
| 121 | Path execRoot, |
tomlu | 09fe062 | 2018-06-19 12:55:39 -0700 | [diff] [blame] | 122 | RemoteOptions remoteOptions, |
| 123 | ExecutionOptions executionOptions, |
philwo | d6ce264 | 2018-07-30 05:24:50 -0700 | [diff] [blame] | 124 | AtomicReference<SpawnRunner> fallbackRunner, |
olaola | 1cbba98 | 2017-07-27 02:52:37 +0200 | [diff] [blame] | 125 | boolean verboseFailures, |
Benjamin Peterson | 3ff87f7 | 2017-08-21 18:41:45 +0200 | [diff] [blame] | 126 | @Nullable Reporter cmdlineReporter, |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 127 | String buildRequestId, |
| 128 | String commandId, |
Jakob Buchgraber | 716e4e3 | 2019-01-21 07:59:23 -0800 | [diff] [blame] | 129 | GrpcRemoteCache remoteCache, |
buchgr | 559a07d | 2017-11-30 11:09:35 -0800 | [diff] [blame] | 130 | @Nullable GrpcRemoteExecutor remoteExecutor, |
George Gensure | f600b69 | 2018-07-10 14:13:50 -0700 | [diff] [blame] | 131 | @Nullable RemoteRetrier retrier, |
olaola | bf326fa | 2018-03-21 11:22:12 -0700 | [diff] [blame] | 132 | DigestUtil digestUtil, |
Jakob Buchgraber | 50c1004 | 2019-04-11 02:11:19 -0700 | [diff] [blame] | 133 | Path logDir, |
Jakob Buchgraber | 34784c3 | 2019-07-22 07:40:14 -0700 | [diff] [blame] | 134 | ImmutableSet<ActionInput> topLevelOutputs) { |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 135 | this.execRoot = execRoot; |
tomlu | 09fe062 | 2018-06-19 12:55:39 -0700 | [diff] [blame] | 136 | this.remoteOptions = remoteOptions; |
| 137 | this.executionOptions = executionOptions; |
ulfjack | 2936047 | 2017-07-07 09:32:53 -0400 | [diff] [blame] | 138 | this.fallbackRunner = fallbackRunner; |
Jakob Buchgraber | 716e4e3 | 2019-01-21 07:59:23 -0800 | [diff] [blame] | 139 | this.remoteCache = Preconditions.checkNotNull(remoteCache, "remoteCache"); |
ulfjack | 2936047 | 2017-07-07 09:32:53 -0400 | [diff] [blame] | 140 | this.remoteExecutor = remoteExecutor; |
olaola | 1cbba98 | 2017-07-27 02:52:37 +0200 | [diff] [blame] | 141 | this.verboseFailures = verboseFailures; |
Benjamin Peterson | 3ff87f7 | 2017-08-21 18:41:45 +0200 | [diff] [blame] | 142 | this.cmdlineReporter = cmdlineReporter; |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 143 | this.buildRequestId = buildRequestId; |
| 144 | this.commandId = commandId; |
George Gensure | f600b69 | 2018-07-10 14:13:50 -0700 | [diff] [blame] | 145 | this.retrier = retrier; |
buchgr | 559a07d | 2017-11-30 11:09:35 -0800 | [diff] [blame] | 146 | this.digestUtil = digestUtil; |
olaola | bf326fa | 2018-03-21 11:22:12 -0700 | [diff] [blame] | 147 | this.logDir = logDir; |
Jakob Buchgraber | 50c1004 | 2019-04-11 02:11:19 -0700 | [diff] [blame] | 148 | this.topLevelOutputs = Preconditions.checkNotNull(topLevelOutputs, "topLevelOutputs"); |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 149 | } |
| 150 | |
ulfjack | 2ce304a | 2017-04-04 08:04:01 +0000 | [diff] [blame] | 151 | @Override |
olaola | ec08553 | 2018-02-22 10:33:28 -0800 | [diff] [blame] | 152 | public String getName() { |
| 153 | return "remote"; |
| 154 | } |
| 155 | |
| 156 | @Override |
tomlu | 29e306d | 2018-04-19 05:41:44 -0700 | [diff] [blame] | 157 | public SpawnResult exec(Spawn spawn, SpawnExecutionContext context) |
olaola | 460a105 | 2017-06-09 04:33:25 +0200 | [diff] [blame] | 158 | throws ExecException, InterruptedException, IOException { |
Sergio Rodriguez Orellana | 8860c3e | 2019-07-25 01:12:58 -0700 | [diff] [blame] | 159 | |
| 160 | boolean spawnCacheableRemotely = Spawns.mayBeCachedRemotely(spawn); |
| 161 | boolean uploadLocalResults = remoteOptions.remoteUploadLocalResults && spawnCacheableRemotely; |
| 162 | boolean acceptCachedResult = remoteOptions.remoteAcceptCached && spawnCacheableRemotely; |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 163 | |
tomlu | 29e306d | 2018-04-19 05:41:44 -0700 | [diff] [blame] | 164 | context.report(ProgressStatus.EXECUTING, getName()); |
buchgr | d480c5f | 2019-04-03 00:53:34 -0700 | [diff] [blame] | 165 | RemoteOutputsMode remoteOutputsMode = remoteOptions.remoteOutputsMode; |
philwo | 9ed9d8a | 2018-09-03 07:30:26 -0700 | [diff] [blame] | 166 | SortedMap<PathFragment, ActionInput> inputMap = context.getInputMapping(true); |
buchgr | 7f72544 | 2019-03-08 03:20:25 -0800 | [diff] [blame] | 167 | final MerkleTree merkleTree = |
| 168 | MerkleTree.build(inputMap, context.getMetadataProvider(), execRoot, digestUtil); |
tomlu | 09fe062 | 2018-06-19 12:55:39 -0700 | [diff] [blame] | 169 | maybeWriteParamFilesLocally(spawn); |
John Cater | 3afb7b0 | 2019-01-18 11:44:51 -0800 | [diff] [blame] | 170 | |
| 171 | // Get the remote platform properties. |
Googler | 77ef8d6 | 2019-07-23 11:55:53 -0700 | [diff] [blame] | 172 | Platform platform = PlatformUtils.getPlatformProto(spawn.getExecutionPlatform(), remoteOptions); |
John Cater | 3afb7b0 | 2019-01-18 11:44:51 -0800 | [diff] [blame] | 173 | |
Jakob Buchgraber | 54d7d61 | 2019-03-25 08:30:17 -0700 | [diff] [blame] | 174 | Command command = |
| 175 | buildCommand( |
| 176 | spawn.getOutputFiles(), spawn.getArguments(), spawn.getEnvironment(), platform); |
| 177 | Digest commandHash = digestUtil.compute(command); |
| 178 | Action action = |
| 179 | buildAction( |
Sergio Rodriguez Orellana | 8860c3e | 2019-07-25 01:12:58 -0700 | [diff] [blame] | 180 | commandHash, merkleTree.getRootDigest(), context.getTimeout(), spawnCacheableRemotely); |
Jakob Buchgraber | 54d7d61 | 2019-03-25 08:30:17 -0700 | [diff] [blame] | 181 | ActionKey actionKey = digestUtil.computeActionKey(action); |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 182 | |
Sergio Rodriguez Orellana | 8860c3e | 2019-07-25 01:12:58 -0700 | [diff] [blame] | 183 | if (!Spawns.mayBeExecutedRemotely(spawn)) { |
| 184 | return execLocallyAndUpload( |
| 185 | spawn, context, inputMap, actionKey, action, command, uploadLocalResults); |
| 186 | } |
| 187 | |
ulfjack | 2936047 | 2017-07-07 09:32:53 -0400 | [diff] [blame] | 188 | // Look up action cache, and reuse the action output if it is found. |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 189 | Context withMetadata = |
| 190 | TracingMetadataUtils.contextWithMetadata(buildRequestId, commandId, actionKey); |
| 191 | Context previous = withMetadata.attach(); |
Jakob Buchgraber | 4b3c2eb | 2019-04-04 02:01:48 -0700 | [diff] [blame] | 192 | Profiler prof = Profiler.instance(); |
ulfjack | 2936047 | 2017-07-07 09:32:53 -0400 | [diff] [blame] | 193 | try { |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 194 | try { |
| 195 | // Try to lookup the action in the action cache. |
ulfjack | 278a84b | 2018-11-05 08:44:22 -0800 | [diff] [blame] | 196 | ActionResult cachedResult; |
Jakob Buchgraber | 4b3c2eb | 2019-04-04 02:01:48 -0700 | [diff] [blame] | 197 | try (SilentCloseable c = prof.profile(ProfilerTask.REMOTE_CACHE_CHECK, "check cache hit")) { |
ulfjack | 278a84b | 2018-11-05 08:44:22 -0800 | [diff] [blame] | 198 | cachedResult = acceptCachedResult ? remoteCache.getCachedActionResult(actionKey) : null; |
| 199 | } |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 200 | if (cachedResult != null) { |
| 201 | if (cachedResult.getExitCode() != 0) { |
ishikhman | 62f5458 | 2019-03-18 03:42:42 -0700 | [diff] [blame] | 202 | // Failed actions are treated as a cache miss mostly in order to avoid caching flaky |
| 203 | // actions (tests). |
| 204 | // Set acceptCachedResult to false in order to force the action re-execution |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 205 | acceptCachedResult = false; |
ishikhman | 62f5458 | 2019-03-18 03:42:42 -0700 | [diff] [blame] | 206 | } else { |
buchgr | d480c5f | 2019-04-03 00:53:34 -0700 | [diff] [blame] | 207 | try { |
| 208 | return downloadAndFinalizeSpawnResult( |
| 209 | cachedResult, /* cacheHit= */ true, spawn, context, remoteOutputsMode); |
ishikhman | 62f5458 | 2019-03-18 03:42:42 -0700 | [diff] [blame] | 210 | } catch (CacheNotFoundException e) { |
| 211 | // No cache hit, so we fall through to local or remote execution. |
| 212 | // We set acceptCachedResult to false in order to force the action re-execution. |
| 213 | acceptCachedResult = false; |
| 214 | } |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 215 | } |
buchgr | 2efea9d | 2017-08-11 16:19:00 +0200 | [diff] [blame] | 216 | } |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 217 | } catch (IOException e) { |
olaola | f0aa55d | 2018-08-16 08:51:06 -0700 | [diff] [blame] | 218 | return execLocallyAndUploadOrFail( |
| 219 | spawn, context, inputMap, actionKey, action, command, uploadLocalResults, e); |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 220 | } |
| 221 | |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 222 | if (remoteExecutor == null) { |
| 223 | // Remote execution is disabled and so execute the spawn on the local machine. |
olaola | f0aa55d | 2018-08-16 08:51:06 -0700 | [diff] [blame] | 224 | return execLocallyAndUpload( |
Sergio Rodriguez Orellana | 8860c3e | 2019-07-25 01:12:58 -0700 | [diff] [blame] | 225 | spawn, context, inputMap, actionKey, action, command, uploadLocalResults); |
George Gensure | 7131af4 | 2018-12-11 09:45:41 -0800 | [diff] [blame] | 226 | } |
ulfjack | 2936047 | 2017-07-07 09:32:53 -0400 | [diff] [blame] | 227 | |
olaola | fea6ab4 | 2018-12-20 08:47:00 -0800 | [diff] [blame] | 228 | ExecuteRequest.Builder requestBuilder = |
George Gensure | f600b69 | 2018-07-10 14:13:50 -0700 | [diff] [blame] | 229 | ExecuteRequest.newBuilder() |
| 230 | .setInstanceName(remoteOptions.remoteInstanceName) |
olaola | f0aa55d | 2018-08-16 08:51:06 -0700 | [diff] [blame] | 231 | .setActionDigest(actionKey.getDigest()) |
olaola | fea6ab4 | 2018-12-20 08:47:00 -0800 | [diff] [blame] | 232 | .setSkipCacheLookup(!acceptCachedResult); |
| 233 | if (remoteOptions.remoteResultCachePriority != 0) { |
| 234 | requestBuilder |
| 235 | .getResultsCachePolicyBuilder() |
| 236 | .setPriority(remoteOptions.remoteResultCachePriority); |
| 237 | } |
olaola | 5c97dcb | 2018-12-21 10:40:43 -0800 | [diff] [blame] | 238 | if (remoteOptions.remoteExecutionPriority != 0) { |
| 239 | requestBuilder |
| 240 | .getExecutionPolicyBuilder() |
| 241 | .setPriority(remoteOptions.remoteExecutionPriority); |
| 242 | } |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 243 | try { |
George Gensure | f600b69 | 2018-07-10 14:13:50 -0700 | [diff] [blame] | 244 | return retrier.execute( |
| 245 | () -> { |
George Gensure | da146d2 | 2019-03-26 03:17:32 -0700 | [diff] [blame] | 246 | ExecuteRequest request = requestBuilder.build(); |
| 247 | |
Sergio Rodriguez Orellana | 8860c3e | 2019-07-25 01:12:58 -0700 | [diff] [blame] | 248 | // Upload the command and all the inputs into the remote cache, if remote caching |
| 249 | // is enabled and not disabled using tags, {@see Spawns#mayBeCachedRemotely} |
| 250 | if (spawnCacheableRemotely) { |
| 251 | try (SilentCloseable c = prof.profile(UPLOAD_TIME, "upload missing inputs")) { |
| 252 | Map<Digest, Message> additionalInputs = Maps.newHashMapWithExpectedSize(2); |
| 253 | additionalInputs.put(actionKey.getDigest(), action); |
| 254 | additionalInputs.put(commandHash, command); |
| 255 | remoteCache.ensureInputsPresent(merkleTree, additionalInputs, execRoot); |
| 256 | } |
ulfjack | 278a84b | 2018-11-05 08:44:22 -0800 | [diff] [blame] | 257 | } |
Sergio Rodriguez Orellana | 8860c3e | 2019-07-25 01:12:58 -0700 | [diff] [blame] | 258 | |
ulfjack | 278a84b | 2018-11-05 08:44:22 -0800 | [diff] [blame] | 259 | ExecuteResponse reply; |
Jakob Buchgraber | 4b3c2eb | 2019-04-04 02:01:48 -0700 | [diff] [blame] | 260 | try (SilentCloseable c = prof.profile(REMOTE_EXECUTION, "execute remotely")) { |
ulfjack | 278a84b | 2018-11-05 08:44:22 -0800 | [diff] [blame] | 261 | reply = remoteExecutor.executeRemotely(request); |
| 262 | } |
Ed Schouten | e213269 | 2019-01-24 04:03:08 -0800 | [diff] [blame] | 263 | |
| 264 | FileOutErr outErr = context.getFileOutErr(); |
| 265 | String message = reply.getMessage(); |
Jakob Buchgraber | 1b8cf02 | 2019-03-21 09:22:29 -0700 | [diff] [blame] | 266 | ActionResult actionResult = reply.getResult(); |
| 267 | if ((actionResult.getExitCode() != 0 |
Ed Schouten | e213269 | 2019-01-24 04:03:08 -0800 | [diff] [blame] | 268 | || reply.getStatus().getCode() != Code.OK.value()) |
| 269 | && !message.isEmpty()) { |
| 270 | outErr.printErr(message + "\n"); |
| 271 | } |
| 272 | |
Jakob Buchgraber | 4b3c2eb | 2019-04-04 02:01:48 -0700 | [diff] [blame] | 273 | try (SilentCloseable c = prof.profile(REMOTE_DOWNLOAD, "download server logs")) { |
ulfjack | 278a84b | 2018-11-05 08:44:22 -0800 | [diff] [blame] | 274 | maybeDownloadServerLogs(reply, actionKey); |
| 275 | } |
buchgr | 9626bb4 | 2017-08-11 13:54:04 +0200 | [diff] [blame] | 276 | |
buchgr | d480c5f | 2019-04-03 00:53:34 -0700 | [diff] [blame] | 277 | try { |
| 278 | return downloadAndFinalizeSpawnResult( |
| 279 | actionResult, reply.getCachedResult(), spawn, context, remoteOutputsMode); |
George Gensure | da146d2 | 2019-03-26 03:17:32 -0700 | [diff] [blame] | 280 | } catch (CacheNotFoundException e) { |
| 281 | // No cache hit, so if we retry this execution, we must no longer accept |
| 282 | // cached results, it must be reexecuted |
| 283 | requestBuilder.setSkipCacheLookup(true); |
| 284 | throw e; |
ulfjack | 278a84b | 2018-11-05 08:44:22 -0800 | [diff] [blame] | 285 | } |
George Gensure | f600b69 | 2018-07-10 14:13:50 -0700 | [diff] [blame] | 286 | }); |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 287 | } catch (IOException e) { |
olaola | f0aa55d | 2018-08-16 08:51:06 -0700 | [diff] [blame] | 288 | return execLocallyAndUploadOrFail( |
| 289 | spawn, context, inputMap, actionKey, action, command, uploadLocalResults, e); |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 290 | } |
| 291 | } finally { |
| 292 | withMetadata.detach(previous); |
buchgr | 9626bb4 | 2017-08-11 13:54:04 +0200 | [diff] [blame] | 293 | } |
| 294 | } |
| 295 | |
buchgr | d480c5f | 2019-04-03 00:53:34 -0700 | [diff] [blame] | 296 | private SpawnResult downloadAndFinalizeSpawnResult( |
| 297 | ActionResult actionResult, |
| 298 | boolean cacheHit, |
| 299 | Spawn spawn, |
| 300 | SpawnExecutionContext context, |
| 301 | RemoteOutputsMode remoteOutputsMode) |
| 302 | throws ExecException, IOException, InterruptedException { |
Jakob Buchgraber | 50c1004 | 2019-04-11 02:11:19 -0700 | [diff] [blame] | 303 | boolean downloadOutputs = |
| 304 | shouldDownloadAllSpawnOutputs( |
| 305 | remoteOutputsMode, |
| 306 | /* exitCode = */ actionResult.getExitCode(), |
| 307 | hasTopLevelOutputs(spawn.getOutputFiles(), topLevelOutputs)); |
buchgr | d480c5f | 2019-04-03 00:53:34 -0700 | [diff] [blame] | 308 | InMemoryOutput inMemoryOutput = null; |
Jakob Buchgraber | 50c1004 | 2019-04-11 02:11:19 -0700 | [diff] [blame] | 309 | if (downloadOutputs) { |
| 310 | try (SilentCloseable c = Profiler.instance().profile(REMOTE_DOWNLOAD, "download outputs")) { |
Jakob Buchgraber | d75b6cf | 2019-06-19 08:12:49 -0700 | [diff] [blame] | 311 | remoteCache.download( |
| 312 | actionResult, execRoot, context.getFileOutErr(), context::lockOutputFiles); |
Jakob Buchgraber | 50c1004 | 2019-04-11 02:11:19 -0700 | [diff] [blame] | 313 | } |
| 314 | } else { |
| 315 | PathFragment inMemoryOutputPath = getInMemoryOutputPath(spawn); |
| 316 | try (SilentCloseable c = |
| 317 | Profiler.instance().profile(REMOTE_DOWNLOAD, "download outputs minimal")) { |
| 318 | inMemoryOutput = |
| 319 | remoteCache.downloadMinimal( |
| 320 | actionResult, |
| 321 | spawn.getOutputFiles(), |
| 322 | inMemoryOutputPath, |
| 323 | context.getFileOutErr(), |
| 324 | execRoot, |
Jakob Buchgraber | d75b6cf | 2019-06-19 08:12:49 -0700 | [diff] [blame] | 325 | context.getMetadataInjector(), |
| 326 | context::lockOutputFiles); |
Jakob Buchgraber | 50c1004 | 2019-04-11 02:11:19 -0700 | [diff] [blame] | 327 | } |
buchgr | d480c5f | 2019-04-03 00:53:34 -0700 | [diff] [blame] | 328 | } |
| 329 | return createSpawnResult(actionResult.getExitCode(), cacheHit, getName(), inMemoryOutput); |
| 330 | } |
| 331 | |
philwo | 849113c | 2019-02-20 15:09:30 -0800 | [diff] [blame] | 332 | @Override |
| 333 | public boolean canExec(Spawn spawn) { |
| 334 | return Spawns.mayBeExecutedRemotely(spawn); |
| 335 | } |
| 336 | |
tomlu | 09fe062 | 2018-06-19 12:55:39 -0700 | [diff] [blame] | 337 | private void maybeWriteParamFilesLocally(Spawn spawn) throws IOException { |
tomlu | b5a727a | 2019-08-03 23:06:35 -0700 | [diff] [blame^] | 338 | if (!executionOptions.shouldMaterializeParamFiles()) { |
tomlu | 09fe062 | 2018-06-19 12:55:39 -0700 | [diff] [blame] | 339 | return; |
| 340 | } |
| 341 | for (ActionInput actionInput : spawn.getInputFiles()) { |
| 342 | if (actionInput instanceof ParamFileActionInput) { |
| 343 | ParamFileActionInput paramFileActionInput = (ParamFileActionInput) actionInput; |
| 344 | Path outputPath = execRoot.getRelative(paramFileActionInput.getExecPath()); |
| 345 | if (outputPath.exists()) { |
| 346 | outputPath.delete(); |
| 347 | } |
| 348 | outputPath.getParentDirectory().createDirectoryAndParents(); |
| 349 | try (OutputStream out = outputPath.getOutputStream()) { |
| 350 | paramFileActionInput.writeTo(out); |
| 351 | } |
| 352 | } |
| 353 | } |
| 354 | } |
| 355 | |
olaola | bf326fa | 2018-03-21 11:22:12 -0700 | [diff] [blame] | 356 | private void maybeDownloadServerLogs(ExecuteResponse resp, ActionKey actionKey) |
| 357 | throws InterruptedException { |
| 358 | ActionResult result = resp.getResult(); |
| 359 | if (resp.getServerLogsCount() > 0 |
| 360 | && (result.getExitCode() != 0 || resp.getStatus().getCode() != Code.OK.value())) { |
| 361 | Path parent = logDir.getRelative(actionKey.getDigest().getHash()); |
| 362 | Path logPath = null; |
| 363 | int logCount = 0; |
| 364 | for (Map.Entry<String, LogFile> e : resp.getServerLogsMap().entrySet()) { |
| 365 | if (e.getValue().getHumanReadable()) { |
| 366 | logPath = parent.getRelative(e.getKey()); |
| 367 | logCount++; |
| 368 | try { |
olaola | f0aa55d | 2018-08-16 08:51:06 -0700 | [diff] [blame] | 369 | getFromFuture(remoteCache.downloadFile(logPath, e.getValue().getDigest())); |
olaola | bf326fa | 2018-03-21 11:22:12 -0700 | [diff] [blame] | 370 | } catch (IOException ex) { |
| 371 | reportOnce(Event.warn("Failed downloading server logs from the remote cache.")); |
| 372 | } |
| 373 | } |
| 374 | } |
| 375 | if (logCount > 0 && verboseFailures) { |
| 376 | report( |
| 377 | Event.info("Server logs of failing action:\n " + (logCount > 1 ? parent : logPath))); |
| 378 | } |
| 379 | } |
| 380 | } |
| 381 | |
olaola | f0aa55d | 2018-08-16 08:51:06 -0700 | [diff] [blame] | 382 | private SpawnResult execLocally(Spawn spawn, SpawnExecutionContext context) |
| 383 | throws ExecException, InterruptedException, IOException { |
| 384 | return fallbackRunner.get().exec(spawn, context); |
| 385 | } |
| 386 | |
| 387 | private SpawnResult execLocallyAndUploadOrFail( |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 388 | Spawn spawn, |
tomlu | 29e306d | 2018-04-19 05:41:44 -0700 | [diff] [blame] | 389 | SpawnExecutionContext context, |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 390 | SortedMap<PathFragment, ActionInput> inputMap, |
| 391 | ActionKey actionKey, |
olaola | f0aa55d | 2018-08-16 08:51:06 -0700 | [diff] [blame] | 392 | Action action, |
| 393 | Command command, |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 394 | boolean uploadLocalResults, |
| 395 | IOException cause) |
buchgr | 9626bb4 | 2017-08-11 13:54:04 +0200 | [diff] [blame] | 396 | throws ExecException, InterruptedException, IOException { |
olaola | e3cc481 | 2018-03-06 14:15:38 -0800 | [diff] [blame] | 397 | // Regardless of cause, if we are interrupted, we should stop without displaying a user-visible |
| 398 | // failure/stack trace. |
| 399 | if (Thread.currentThread().isInterrupted()) { |
| 400 | throw new InterruptedException(); |
| 401 | } |
Benjamin Peterson | 1532df0 | 2019-01-24 08:45:44 -0800 | [diff] [blame] | 402 | if (remoteOptions.remoteLocalFallback && !RemoteRetrierUtils.causedByExecTimeout(cause)) { |
olaola | f0aa55d | 2018-08-16 08:51:06 -0700 | [diff] [blame] | 403 | return execLocallyAndUpload( |
Sergio Rodriguez Orellana | 8860c3e | 2019-07-25 01:12:58 -0700 | [diff] [blame] | 404 | spawn, context, inputMap, actionKey, action, command, uploadLocalResults); |
buchgr | 9626bb4 | 2017-08-11 13:54:04 +0200 | [diff] [blame] | 405 | } |
Jakob Buchgraber | d75b6cf | 2019-06-19 08:12:49 -0700 | [diff] [blame] | 406 | return handleError(cause, context.getFileOutErr(), actionKey, context); |
buchgr | a659135 | 2017-09-01 13:14:19 +0200 | [diff] [blame] | 407 | } |
| 408 | |
Jakob Buchgraber | d75b6cf | 2019-06-19 08:12:49 -0700 | [diff] [blame] | 409 | private SpawnResult handleError( |
| 410 | IOException exception, FileOutErr outErr, ActionKey actionKey, SpawnExecutionContext context) |
olaola | 2732df0 | 2018-03-16 08:48:13 -0700 | [diff] [blame] | 411 | throws ExecException, InterruptedException, IOException { |
Benjamin Peterson | 1532df0 | 2019-01-24 08:45:44 -0800 | [diff] [blame] | 412 | if (exception.getCause() instanceof ExecutionStatusException) { |
| 413 | ExecutionStatusException e = (ExecutionStatusException) exception.getCause(); |
olaola | 2732df0 | 2018-03-16 08:48:13 -0700 | [diff] [blame] | 414 | if (e.getResponse() != null) { |
| 415 | ExecuteResponse resp = e.getResponse(); |
olaola | bf326fa | 2018-03-21 11:22:12 -0700 | [diff] [blame] | 416 | maybeDownloadServerLogs(resp, actionKey); |
olaola | 2732df0 | 2018-03-16 08:48:13 -0700 | [diff] [blame] | 417 | if (resp.hasResult()) { |
| 418 | // We try to download all (partial) results even on server error, for debuggability. |
Jakob Buchgraber | d75b6cf | 2019-06-19 08:12:49 -0700 | [diff] [blame] | 419 | remoteCache.download(resp.getResult(), execRoot, outErr, context::lockOutputFiles); |
olaola | 2732df0 | 2018-03-16 08:48:13 -0700 | [diff] [blame] | 420 | } |
buchgr | a659135 | 2017-09-01 13:14:19 +0200 | [diff] [blame] | 421 | } |
olaola | 2732df0 | 2018-03-16 08:48:13 -0700 | [diff] [blame] | 422 | if (e.isExecutionTimeout()) { |
| 423 | return new SpawnResult.Builder() |
| 424 | .setRunnerName(getName()) |
| 425 | .setStatus(Status.TIMEOUT) |
| 426 | .setExitCode(POSIX_TIMEOUT_EXIT_CODE) |
| 427 | .build(); |
| 428 | } |
ulfjack | 32e7a1c | 2017-11-28 01:14:34 -0800 | [diff] [blame] | 429 | } |
| 430 | final Status status; |
Benjamin Peterson | 1532df0 | 2019-01-24 08:45:44 -0800 | [diff] [blame] | 431 | if (RemoteRetrierUtils.causedByStatus(exception, Code.UNAVAILABLE)) { |
ulfjack | 32e7a1c | 2017-11-28 01:14:34 -0800 | [diff] [blame] | 432 | status = Status.EXECUTION_FAILED_CATASTROPHICALLY; |
Benjamin Peterson | 1532df0 | 2019-01-24 08:45:44 -0800 | [diff] [blame] | 433 | } else if (exception instanceof CacheNotFoundException) { |
buchgr | a659135 | 2017-09-01 13:14:19 +0200 | [diff] [blame] | 434 | status = Status.REMOTE_CACHE_FAILED; |
buchgr | 9626bb4 | 2017-08-11 13:54:04 +0200 | [diff] [blame] | 435 | } else { |
buchgr | a659135 | 2017-09-01 13:14:19 +0200 | [diff] [blame] | 436 | status = Status.EXECUTION_FAILED; |
buchgr | 9626bb4 | 2017-08-11 13:54:04 +0200 | [diff] [blame] | 437 | } |
Jakob Buchgraber | a79a4b6 | 2019-06-23 02:06:20 -0700 | [diff] [blame] | 438 | |
| 439 | final String errorMessage; |
| 440 | if (!verboseFailures) { |
| 441 | errorMessage = Utils.grpcAwareErrorMessage(exception); |
| 442 | } else { |
| 443 | // On --verbose_failures print the whole stack trace |
| 444 | errorMessage = Throwables.getStackTraceAsString(exception); |
| 445 | } |
| 446 | |
| 447 | return new SpawnResult.Builder() |
| 448 | .setRunnerName(getName()) |
| 449 | .setStatus(status) |
| 450 | .setExitCode(ExitCode.REMOTE_ERROR.getNumericExitCode()) |
| 451 | .setFailureMessage(errorMessage) |
| 452 | .build(); |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 453 | } |
| 454 | |
Jakob Buchgraber | a79a4b6 | 2019-06-23 02:06:20 -0700 | [diff] [blame] | 455 | |
buchgr | d480c5f | 2019-04-03 00:53:34 -0700 | [diff] [blame] | 456 | static Action buildAction(Digest command, Digest inputRoot, Duration timeout, boolean cacheable) { |
John Cater | 38ba193 | 2018-01-10 19:01:14 -0800 | [diff] [blame] | 457 | |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 458 | Action.Builder action = Action.newBuilder(); |
| 459 | action.setCommandDigest(command); |
| 460 | action.setInputRootDigest(inputRoot); |
ulfjack | deab0cf | 2017-08-08 13:08:24 +0200 | [diff] [blame] | 461 | if (!timeout.isZero()) { |
| 462 | action.setTimeout(com.google.protobuf.Duration.newBuilder().setSeconds(timeout.getSeconds())); |
olaola | b259cc4 | 2017-07-04 09:58:53 -0400 | [diff] [blame] | 463 | } |
olaola | a22d0e9 | 2017-12-11 07:53:15 -0800 | [diff] [blame] | 464 | if (!cacheable) { |
| 465 | action.setDoNotCache(true); |
| 466 | } |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 467 | return action.build(); |
| 468 | } |
| 469 | |
olaola | f0aa55d | 2018-08-16 08:51:06 -0700 | [diff] [blame] | 470 | static Command buildCommand( |
| 471 | Collection<? extends ActionInput> outputs, |
| 472 | List<String> arguments, |
| 473 | ImmutableMap<String, String> env, |
John Cater | 3afb7b0 | 2019-01-18 11:44:51 -0800 | [diff] [blame] | 474 | @Nullable Platform platform) { |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 475 | Command.Builder command = Command.newBuilder(); |
olaola | f0aa55d | 2018-08-16 08:51:06 -0700 | [diff] [blame] | 476 | ArrayList<String> outputFiles = new ArrayList<>(); |
| 477 | ArrayList<String> outputDirectories = new ArrayList<>(); |
| 478 | for (ActionInput output : outputs) { |
| 479 | String pathString = output.getExecPathString(); |
| 480 | if (output instanceof Artifact && ((Artifact) output).isTreeArtifact()) { |
| 481 | outputDirectories.add(pathString); |
| 482 | } else { |
| 483 | outputFiles.add(pathString); |
| 484 | } |
| 485 | } |
| 486 | Collections.sort(outputFiles); |
| 487 | Collections.sort(outputDirectories); |
| 488 | command.addAllOutputFiles(outputFiles); |
| 489 | command.addAllOutputDirectories(outputDirectories); |
| 490 | |
John Cater | 3afb7b0 | 2019-01-18 11:44:51 -0800 | [diff] [blame] | 491 | if (platform != null) { |
olaola | f0aa55d | 2018-08-16 08:51:06 -0700 | [diff] [blame] | 492 | command.setPlatform(platform); |
| 493 | } |
olaola | 460a105 | 2017-06-09 04:33:25 +0200 | [diff] [blame] | 494 | command.addAllArguments(arguments); |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 495 | // Sorting the environment pairs by variable name. |
ulfjack | 85c533c | 2017-08-11 12:23:48 +0200 | [diff] [blame] | 496 | TreeSet<String> variables = new TreeSet<>(env.keySet()); |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 497 | for (String var : variables) { |
ulfjack | 85c533c | 2017-08-11 12:23:48 +0200 | [diff] [blame] | 498 | command.addEnvironmentVariablesBuilder().setName(var).setValue(env.get(var)); |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 499 | } |
| 500 | return command.build(); |
| 501 | } |
ulfjack | 2936047 | 2017-07-07 09:32:53 -0400 | [diff] [blame] | 502 | |
Jakob Buchgraber | 562fcf9 | 2017-07-27 12:51:13 +0200 | [diff] [blame] | 503 | private Map<Path, Long> getInputCtimes(SortedMap<PathFragment, ActionInput> inputMap) { |
buchgr | d480c5f | 2019-04-03 00:53:34 -0700 | [diff] [blame] | 504 | HashMap<Path, Long> ctimes = new HashMap<>(); |
Ola Rozenfeld | ccbe40a | 2017-07-17 10:05:59 +0200 | [diff] [blame] | 505 | for (Map.Entry<PathFragment, ActionInput> e : inputMap.entrySet()) { |
| 506 | ActionInput input = e.getValue(); |
ulfjack | 3e28868 | 2018-01-08 09:31:17 -0800 | [diff] [blame] | 507 | if (input instanceof VirtualActionInput) { |
Ola Rozenfeld | ccbe40a | 2017-07-17 10:05:59 +0200 | [diff] [blame] | 508 | continue; |
| 509 | } |
| 510 | Path path = execRoot.getRelative(input.getExecPathString()); |
| 511 | try { |
| 512 | ctimes.put(path, path.stat().getLastChangeTime()); |
| 513 | } catch (IOException ex) { |
| 514 | // Put a token value indicating an exception; this is used so that if the exception |
| 515 | // is raised both before and after the execution, it is ignored, but if it is raised only |
| 516 | // one of the times, it triggers a remote cache upload skip. |
| 517 | ctimes.put(path, -1L); |
| 518 | } |
| 519 | } |
| 520 | return ctimes; |
| 521 | } |
| 522 | |
Jakob Buchgraber | 562fcf9 | 2017-07-27 12:51:13 +0200 | [diff] [blame] | 523 | @VisibleForTesting |
| 524 | SpawnResult execLocallyAndUpload( |
| 525 | Spawn spawn, |
tomlu | 29e306d | 2018-04-19 05:41:44 -0700 | [diff] [blame] | 526 | SpawnExecutionContext context, |
Jakob Buchgraber | 562fcf9 | 2017-07-27 12:51:13 +0200 | [diff] [blame] | 527 | SortedMap<PathFragment, ActionInput> inputMap, |
olaola | f0aa55d | 2018-08-16 08:51:06 -0700 | [diff] [blame] | 528 | ActionKey actionKey, |
| 529 | Action action, |
| 530 | Command command, |
| 531 | boolean uploadLocalResults) |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 532 | throws ExecException, IOException, InterruptedException { |
Ola Rozenfeld | ccbe40a | 2017-07-17 10:05:59 +0200 | [diff] [blame] | 533 | Map<Path, Long> ctimesBefore = getInputCtimes(inputMap); |
olaola | f0aa55d | 2018-08-16 08:51:06 -0700 | [diff] [blame] | 534 | SpawnResult result = execLocally(spawn, context); |
Ola Rozenfeld | ccbe40a | 2017-07-17 10:05:59 +0200 | [diff] [blame] | 535 | Map<Path, Long> ctimesAfter = getInputCtimes(inputMap); |
Jakob Buchgraber | bac30fe | 2019-01-28 05:24:23 -0800 | [diff] [blame] | 536 | uploadLocalResults = |
| 537 | uploadLocalResults && Status.SUCCESS.equals(result.status()) && result.exitCode() == 0; |
| 538 | if (!uploadLocalResults) { |
| 539 | return result; |
| 540 | } |
| 541 | |
Ola Rozenfeld | ccbe40a | 2017-07-17 10:05:59 +0200 | [diff] [blame] | 542 | for (Map.Entry<Path, Long> e : ctimesBefore.entrySet()) { |
| 543 | // Skip uploading to remote cache, because an input was modified during execution. |
| 544 | if (!ctimesAfter.get(e.getKey()).equals(e.getValue())) { |
| 545 | return result; |
| 546 | } |
| 547 | } |
Jakob Buchgraber | bac30fe | 2019-01-28 05:24:23 -0800 | [diff] [blame] | 548 | |
Benjamin Peterson | dd3ddb0 | 2018-05-03 09:20:08 -0700 | [diff] [blame] | 549 | Collection<Path> outputFiles = resolveActionInputs(execRoot, spawn.getOutputFiles()); |
Jakob Buchgraber | 4b3c2eb | 2019-04-04 02:01:48 -0700 | [diff] [blame] | 550 | try (SilentCloseable c = Profiler.instance().profile(UPLOAD_TIME, "upload outputs")) { |
olaola | f0aa55d | 2018-08-16 08:51:06 -0700 | [diff] [blame] | 551 | remoteCache.upload( |
Jakob Buchgraber | bac30fe | 2019-01-28 05:24:23 -0800 | [diff] [blame] | 552 | actionKey, action, command, execRoot, outputFiles, context.getFileOutErr()); |
Benjamin Peterson | 3ff87f7 | 2017-08-21 18:41:45 +0200 | [diff] [blame] | 553 | } catch (IOException e) { |
| 554 | if (verboseFailures) { |
| 555 | report(Event.debug("Upload to remote cache failed: " + e.getMessage())); |
| 556 | } else { |
| 557 | reportOnce(Event.warn("Some artifacts failed be uploaded to the remote cache.")); |
| 558 | } |
| 559 | } |
ulfjack | 2936047 | 2017-07-07 09:32:53 -0400 | [diff] [blame] | 560 | return result; |
| 561 | } |
ulfjack | 3fbd7c4 | 2017-07-10 13:19:19 +0200 | [diff] [blame] | 562 | |
Benjamin Peterson | 3ff87f7 | 2017-08-21 18:41:45 +0200 | [diff] [blame] | 563 | private void reportOnce(Event evt) { |
| 564 | if (warningReported.compareAndSet(false, true)) { |
| 565 | report(evt); |
| 566 | } |
| 567 | } |
| 568 | |
| 569 | private void report(Event evt) { |
| 570 | if (cmdlineReporter != null) { |
| 571 | cmdlineReporter.handle(evt); |
| 572 | } |
| 573 | } |
| 574 | |
philwo | d6ce264 | 2018-07-30 05:24:50 -0700 | [diff] [blame] | 575 | /** |
| 576 | * Resolve a collection of {@link com.google.devtools.build.lib.actions.ActionInput}s to {@link |
| 577 | * Path}s. |
| 578 | */ |
Benjamin Peterson | dd3ddb0 | 2018-05-03 09:20:08 -0700 | [diff] [blame] | 579 | static Collection<Path> resolveActionInputs( |
| 580 | Path execRoot, Collection<? extends ActionInput> actionInputs) { |
buchgr | d480c5f | 2019-04-03 00:53:34 -0700 | [diff] [blame] | 581 | return actionInputs.stream() |
Benjamin Peterson | dd3ddb0 | 2018-05-03 09:20:08 -0700 | [diff] [blame] | 582 | .map((inp) -> execRoot.getRelative(inp.getExecPath())) |
| 583 | .collect(ImmutableList.toImmutableList()); |
ulfjack | 85c533c | 2017-08-11 12:23:48 +0200 | [diff] [blame] | 584 | } |
ulfjack | 81940bd | 2017-03-29 15:38:28 +0000 | [diff] [blame] | 585 | } |