blob: 7f2d2e81fbf1771b458eb42011e6719f40c09251 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2015 The Bazel Authors. All rights reserved.
Ulf Adams633f5392015-09-15 11:13:08 +00002//
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
15package com.google.devtools.build.lib.runtime;
16
Ulf Adams706b7f22015-10-20 09:06:45 +000017import static com.google.devtools.build.lib.profiler.AutoProfiler.profiled;
18
Ulf Adams8d2e60d2015-09-17 08:11:24 +000019import com.google.common.annotations.VisibleForTesting;
Ulf Adams50e7db62015-10-20 09:14:16 +000020import com.google.common.collect.ImmutableList;
Ulf Adamsca2d8d22015-09-16 13:00:45 +000021import com.google.common.collect.ImmutableSet;
Ulf Adams633f5392015-09-15 11:13:08 +000022import com.google.common.eventbus.EventBus;
Ulf Adams5b9009b2015-09-24 09:52:53 +000023import com.google.devtools.build.lib.actions.PackageRootResolver;
Ulf Adams80613022015-09-16 09:11:33 +000024import com.google.devtools.build.lib.actions.cache.ActionCache;
Ulf Adams633f5392015-09-15 11:13:08 +000025import com.google.devtools.build.lib.analysis.BlazeDirectories;
Ulf Adams80613022015-09-16 09:11:33 +000026import com.google.devtools.build.lib.analysis.BuildView;
Ulf Adams5b9009b2015-09-24 09:52:53 +000027import com.google.devtools.build.lib.analysis.SkyframePackageRootResolver;
Ulf Adams3815b4c2015-09-18 07:34:13 +000028import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
Ulf Adamsca2d8d22015-09-16 13:00:45 +000029import com.google.devtools.build.lib.analysis.config.BuildConfigurationCollection;
30import com.google.devtools.build.lib.analysis.config.BuildOptions;
Damien Martin-Guillerezffbddeb2017-01-10 17:20:05 +000031import com.google.devtools.build.lib.analysis.config.DefaultsPackage;
Ulf Adamsca2d8d22015-09-16 13:00:45 +000032import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000033import com.google.devtools.build.lib.cmdline.Label;
Ulf Adams633f5392015-09-15 11:13:08 +000034import com.google.devtools.build.lib.events.Reporter;
Ulf Adams706b7f22015-10-20 09:06:45 +000035import com.google.devtools.build.lib.exec.OutputService;
Ulf Adamsca2d8d22015-09-16 13:00:45 +000036import com.google.devtools.build.lib.packages.NoSuchThingException;
37import com.google.devtools.build.lib.packages.Target;
Ulf Adams3815b4c2015-09-18 07:34:13 +000038import com.google.devtools.build.lib.pkgcache.PackageCacheOptions;
Ulf Adams80613022015-09-16 09:11:33 +000039import com.google.devtools.build.lib.pkgcache.PackageManager;
Ulf Adamsebf1b2e2015-09-29 11:06:53 +000040import com.google.devtools.build.lib.pkgcache.TargetPatternEvaluator;
Ulf Adams706b7f22015-10-20 09:06:45 +000041import com.google.devtools.build.lib.profiler.AutoProfiler;
42import com.google.devtools.build.lib.profiler.ProfilerTask;
Ulf Adams3d67e002016-03-29 16:23:01 +000043import com.google.devtools.build.lib.skyframe.SkyframeBuildView;
Ulf Adams80613022015-09-16 09:11:33 +000044import com.google.devtools.build.lib.skyframe.SkyframeExecutor;
Ulf Adamsca2d8d22015-09-16 13:00:45 +000045import com.google.devtools.build.lib.util.AbruptExitException;
46import com.google.devtools.build.lib.util.ExitCode;
Mark Schaller6df81792015-12-10 18:47:47 +000047import com.google.devtools.build.lib.util.Preconditions;
Ulf Adamsc73051c62016-03-23 09:18:13 +000048import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
Ulf Adams706b7f22015-10-20 09:06:45 +000049import com.google.devtools.build.lib.vfs.FileSystemUtils;
Ulf Adams80613022015-09-16 09:11:33 +000050import com.google.devtools.build.lib.vfs.Path;
Ulf Adams08663e62016-02-12 09:59:22 +000051import com.google.devtools.build.lib.vfs.PathFragment;
Ulf Adams50e7db62015-10-20 09:14:16 +000052import com.google.devtools.common.options.OptionPriority;
Ulf Adamsde14ade2016-10-14 14:20:31 +000053import com.google.devtools.common.options.OptionsClassProvider;
Ulf Adamsebf1b2e2015-09-29 11:06:53 +000054import com.google.devtools.common.options.OptionsParser;
Ulf Adams50e7db62015-10-20 09:14:16 +000055import com.google.devtools.common.options.OptionsParsingException;
Ulf Adamsca2d8d22015-09-16 13:00:45 +000056import com.google.devtools.common.options.OptionsProvider;
Ulf Adams80613022015-09-16 09:11:33 +000057import java.io.IOException;
Ulf Adams8d2e60d2015-09-17 08:11:24 +000058import java.util.Collection;
59import java.util.Collections;
Ulf Adams8d2e60d2015-09-17 08:11:24 +000060import java.util.List;
Ulf Adams633f5392015-09-15 11:13:08 +000061import java.util.Map;
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +000062import java.util.Set;
Ulf Adams50e7db62015-10-20 09:14:16 +000063import java.util.TreeMap;
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +000064import java.util.TreeSet;
Ulf Adams80613022015-09-16 09:11:33 +000065import java.util.UUID;
Ulf Adams88f643c2015-09-17 10:55:52 +000066import java.util.concurrent.atomic.AtomicReference;
Michael Staib6e5e8fb2016-10-04 21:26:37 +000067import javax.annotation.Nullable;
Ulf Adams633f5392015-09-15 11:13:08 +000068
69/**
70 * Encapsulates the state needed for a single command. The environment is dropped when the current
71 * command is done and all corresponding objects are garbage collected.
72 */
73public final class CommandEnvironment {
74 private final BlazeRuntime runtime;
Ulf Adamsab43b972016-03-30 12:23:50 +000075 private final BlazeWorkspace workspace;
Ulf Adams94b72db2016-03-30 11:58:37 +000076 private final BlazeDirectories directories;
Ulf Adams3815b4c2015-09-18 07:34:13 +000077
Klaus Aehlig88a590a2016-08-12 08:56:30 +000078 private UUID commandId; // Unique identifier for the command being run
Ulf Adamsca2d8d22015-09-16 13:00:45 +000079 private final Reporter reporter;
Ulf Adams633f5392015-09-15 11:13:08 +000080 private final EventBus eventBus;
Ulf Adamsca2d8d22015-09-16 13:00:45 +000081 private final BlazeModule.ModuleEnvironment blazeModuleEnvironment;
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +000082 private final Map<String, String> clientEnv = new TreeMap<>();
83 private final Set<String> visibleClientEnv = new TreeSet<>();
Damien Martin-Guillerezffbddeb2017-01-10 17:20:05 +000084 private final Map<String, String> actionClientEnv = new TreeMap<>();
Ulf Adamsc73051c62016-03-23 09:18:13 +000085 private final TimestampGranularityMonitor timestampGranularityMonitor;
Michael Staib6e5e8fb2016-10-04 21:26:37 +000086 private final Thread commandThread;
Ulf Adamsebf1b2e2015-09-29 11:06:53 +000087
Klaus Aehlig88a590a2016-08-12 08:56:30 +000088 private String[] crashData;
89
Ulf Adams08663e62016-02-12 09:59:22 +000090 private PathFragment relativeWorkingDirectory = PathFragment.EMPTY_FRAGMENT;
Ulf Adamsb5146102015-10-20 08:57:26 +000091 private long commandStartTime;
Ulf Adams706b7f22015-10-20 09:06:45 +000092 private OutputService outputService;
Ulf Adamsc5855302015-10-20 08:46:38 +000093 private Path workingDirectory;
Ulf Adams47cb9162015-09-18 08:12:30 +000094
Ulf Adams3877ebd2016-11-15 10:22:59 +000095 private String commandName;
96 private OptionsProvider options;
Ulf Adamsa0e3af42016-10-31 16:52:48 +000097
Ulf Adams88f643c2015-09-17 10:55:52 +000098 private AtomicReference<AbruptExitException> pendingException = new AtomicReference<>();
Ulf Adamsca2d8d22015-09-16 13:00:45 +000099
100 private class BlazeModuleEnvironment implements BlazeModule.ModuleEnvironment {
101 @Override
Ulf Adamsae90bc92015-09-18 09:18:58 +0000102 public Path getFileFromWorkspace(Label label)
Ulf Adamsca2d8d22015-09-16 13:00:45 +0000103 throws NoSuchThingException, InterruptedException, IOException {
104 Target target = getPackageManager().getTarget(reporter, label);
Ulf Adams706b7f22015-10-20 09:06:45 +0000105 return (outputService != null)
106 ? outputService.stageTool(target)
Ulf Adamsca2d8d22015-09-16 13:00:45 +0000107 : target.getPackage().getPackageDirectory().getRelative(target.getName());
108 }
109
110 @Override
111 public void exit(AbruptExitException exception) {
Michael Staib6e5e8fb2016-10-04 21:26:37 +0000112 Preconditions.checkNotNull(exception);
113 Preconditions.checkNotNull(exception.getExitCode());
114 if (pendingException.compareAndSet(null, exception)) {
115 // There was no exception, so we're the first one to ask for an exit. Interrupt the command.
116 commandThread.interrupt();
117 }
Ulf Adamsca2d8d22015-09-16 13:00:45 +0000118 }
119 }
120
Michael Staib6e5e8fb2016-10-04 21:26:37 +0000121 /**
122 * Creates a new command environment which can be used for executing commands for the given
123 * runtime in the given workspace, which will publish events on the given eventBus. The
124 * commandThread passed is interrupted when a module requests an early exit.
125 */
126 CommandEnvironment(
127 BlazeRuntime runtime, BlazeWorkspace workspace, EventBus eventBus, Thread commandThread) {
Ulf Adams633f5392015-09-15 11:13:08 +0000128 this.runtime = runtime;
Ulf Adams69cc0032016-03-30 13:52:25 +0000129 this.workspace = workspace;
Ulf Adamsab43b972016-03-30 12:23:50 +0000130 this.directories = workspace.getDirectories();
Klaus Aehlig88a590a2016-08-12 08:56:30 +0000131 this.commandId = null; // Will be set once we get the client environment
Klaus Aehlig777b30d2017-02-24 16:30:15 +0000132 this.reporter = new Reporter(eventBus);
Ulf Adams633f5392015-09-15 11:13:08 +0000133 this.eventBus = eventBus;
Michael Staib6e5e8fb2016-10-04 21:26:37 +0000134 this.commandThread = commandThread;
Ulf Adamsca2d8d22015-09-16 13:00:45 +0000135 this.blazeModuleEnvironment = new BlazeModuleEnvironment();
Ulf Adamsc73051c62016-03-23 09:18:13 +0000136 this.timestampGranularityMonitor = new TimestampGranularityMonitor(runtime.getClock());
137 // Record the command's starting time again, for use by
138 // TimestampGranularityMonitor.waitForTimestampGranularity().
139 // This should be done as close as possible to the start of
140 // the command's execution.
141 timestampGranularityMonitor.setCommandStartTime();
Ulf Adamsebf1b2e2015-09-29 11:06:53 +0000142
Ulf Adamsc5855302015-10-20 08:46:38 +0000143 // TODO(ulfjack): We don't call beforeCommand() in tests, but rely on workingDirectory being set
144 // in setupPackageCache(). This leads to NPE if we don't set it here.
Ulf Adams94b72db2016-03-30 11:58:37 +0000145 this.workingDirectory = directories.getWorkspace();
Ulf Adams69cc0032016-03-30 13:52:25 +0000146
147 workspace.getSkyframeExecutor().setEventBus(eventBus);
Ulf Adams633f5392015-09-15 11:13:08 +0000148 }
149
lpino1fde2302017-04-07 10:22:28 +0000150 /**
151 * Same as CommandEnvironment(BlazeRuntime, BlazeWorkspace, EventBus, Thread) but with an
152 * explicit commandName and options.
153 *
154 * ONLY for testing.
155 */
156 @VisibleForTesting
157 CommandEnvironment(
158 BlazeRuntime runtime, BlazeWorkspace workspace, EventBus eventBus, Thread commandThread,
159 String commandNameForTesting, OptionsProvider optionsForTesting) {
160 this(runtime, workspace, eventBus, commandThread);
161 // Both commandName and options are normally set by beforeCommand(); however this method is not
162 // called in tests (i.e. tests use BlazeRuntimeWrapper). These fields should only be set for
163 // testing.
164 this.commandName = commandNameForTesting;
165 this.options = optionsForTesting;
166 }
167
Ulf Adams633f5392015-09-15 11:13:08 +0000168 public BlazeRuntime getRuntime() {
169 return runtime;
170 }
171
Ulf Adamsab43b972016-03-30 12:23:50 +0000172 public BlazeWorkspace getBlazeWorkspace() {
173 return workspace;
174 }
175
Ulf Adams633f5392015-09-15 11:13:08 +0000176 public BlazeDirectories getDirectories() {
Ulf Adams94b72db2016-03-30 11:58:37 +0000177 return directories;
Ulf Adams633f5392015-09-15 11:13:08 +0000178 }
179
180 /**
181 * Returns the reporter for events.
182 */
183 public Reporter getReporter() {
Ulf Adamsca2d8d22015-09-16 13:00:45 +0000184 return reporter;
Ulf Adams633f5392015-09-15 11:13:08 +0000185 }
186
187 public EventBus getEventBus() {
188 return eventBus;
189 }
190
Ulf Adamsca2d8d22015-09-16 13:00:45 +0000191 public BlazeModule.ModuleEnvironment getBlazeModuleEnvironment() {
192 return blazeModuleEnvironment;
193 }
194
Ulf Adams8d2e60d2015-09-17 08:11:24 +0000195 /**
196 * Return an unmodifiable view of the blaze client's environment when it invoked the current
197 * command.
198 */
Ulf Adams633f5392015-09-15 11:13:08 +0000199 public Map<String, String> getClientEnv() {
Ulf Adams8d2e60d2015-09-17 08:11:24 +0000200 return Collections.unmodifiableMap(clientEnv);
201 }
202
Ulf Adams3877ebd2016-11-15 10:22:59 +0000203 public String getCommandName() {
204 return commandName;
205 }
206
207 public OptionsProvider getOptions() {
Ulf Adamsa0e3af42016-10-31 16:52:48 +0000208 return options;
209 }
210
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000211 /**
Klaus Aehligb8d09022016-09-20 13:39:08 +0000212 * Return an ordered version of the client environment restricted to those variables whitelisted
213 * by the command-line options to be inheritable by actions.
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000214 */
Klaus Aehligb8d09022016-09-20 13:39:08 +0000215 public Map<String, String> getWhitelistedClientEnv() {
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000216 Map<String, String> visibleEnv = new TreeMap<>();
217 for (String var : visibleClientEnv) {
218 String value = clientEnv.get(var);
219 if (value != null) {
220 visibleEnv.put(var, value);
221 }
222 }
223 return Collections.unmodifiableMap(visibleEnv);
224 }
225
Ulf Adams8d2e60d2015-09-17 08:11:24 +0000226 @VisibleForTesting
Dmitry Lomov31fab292017-03-07 18:33:08 +0000227 void updateClientEnv(List<Map.Entry<String, String>> clientEnvList) {
Ulf Adams8d2e60d2015-09-17 08:11:24 +0000228 Preconditions.checkState(clientEnv.isEmpty());
229
Dmitry Lomov31fab292017-03-07 18:33:08 +0000230 Collection<Map.Entry<String, String>> env = clientEnvList;
Ulf Adams8d2e60d2015-09-17 08:11:24 +0000231 for (Map.Entry<String, String> entry : env) {
232 clientEnv.put(entry.getKey(), entry.getValue());
233 }
Klaus Aehlig88a590a2016-08-12 08:56:30 +0000234 // Try to set the clientId from the client environment.
235 if (commandId == null) {
236 String uuidString = clientEnv.get("BAZEL_INTERNAL_INVOCATION_ID");
237 if (uuidString != null) {
238 try {
239 commandId = UUID.fromString(uuidString);
240 } catch (IllegalArgumentException e) {
241 // String was malformed, so we will resort to generating a random UUID
242 }
243 }
244 }
245 if (commandId == null) {
246 // We have been provided with the client environment, but it didn't contain
247 // the invocation id; hence generate our own.
248 commandId = UUID.randomUUID();
249 }
250 setCommandIdInCrashData();
Ulf Adams633f5392015-09-15 11:13:08 +0000251 }
Ulf Adams80613022015-09-16 09:11:33 +0000252
Ulf Adamsc73051c62016-03-23 09:18:13 +0000253 public TimestampGranularityMonitor getTimestampGranularityMonitor() {
254 return timestampGranularityMonitor;
255 }
256
Ulf Adams80613022015-09-16 09:11:33 +0000257 public PackageManager getPackageManager() {
Ulf Adamsab43b972016-03-30 12:23:50 +0000258 return getSkyframeExecutor().getPackageManager();
Ulf Adams80613022015-09-16 09:11:33 +0000259 }
260
Ulf Adams08663e62016-02-12 09:59:22 +0000261 public PathFragment getRelativeWorkingDirectory() {
262 return relativeWorkingDirectory;
Ulf Adamsebf1b2e2015-09-29 11:06:53 +0000263 }
264
265 /**
Ulf Adams08663e62016-02-12 09:59:22 +0000266 * Creates and returns a new target pattern parser.
Ulf Adamsebf1b2e2015-09-29 11:06:53 +0000267 */
Ulf Adams08663e62016-02-12 09:59:22 +0000268 public TargetPatternEvaluator newTargetPatternEvaluator() {
269 TargetPatternEvaluator result = getPackageManager().newTargetPatternEvaluator();
270 result.updateOffset(relativeWorkingDirectory);
271 return result;
Ulf Adamsebf1b2e2015-09-29 11:06:53 +0000272 }
273
Ulf Adams5b9009b2015-09-24 09:52:53 +0000274 public PackageRootResolver getPackageRootResolver() {
275 return new SkyframePackageRootResolver(getSkyframeExecutor(), reporter);
276 }
277
Ulf Adams3815b4c2015-09-18 07:34:13 +0000278 /**
279 * Returns the UUID that Blaze uses to identify everything logged from the current build command.
280 * It's also used to invalidate Skyframe nodes that are specific to a certain invocation, such as
281 * the build info.
282 */
Ulf Adams80613022015-09-16 09:11:33 +0000283 public UUID getCommandId() {
Klaus Aehlig88a590a2016-08-12 08:56:30 +0000284 if (commandId == null) {
285 // The commandId should not be requested before the beforeCommand is executed, as the
286 // commandId might be set through the client environment. However, to simplify testing,
287 // we set the id value before we throw the exception.
288 commandId = UUID.randomUUID();
289 throw new IllegalArgumentException("Build Id requested before client environment provided");
290 }
Ulf Adams3815b4c2015-09-18 07:34:13 +0000291 return commandId;
Ulf Adams80613022015-09-16 09:11:33 +0000292 }
293
294 public SkyframeExecutor getSkyframeExecutor() {
Ulf Adamsab43b972016-03-30 12:23:50 +0000295 return workspace.getSkyframeExecutor();
Ulf Adams80613022015-09-16 09:11:33 +0000296 }
297
Ulf Adams3d67e002016-03-29 16:23:01 +0000298 public SkyframeBuildView getSkyframeBuildView() {
Ulf Adamsab43b972016-03-30 12:23:50 +0000299 return getSkyframeExecutor().getSkyframeBuildView();
Ulf Adams3d67e002016-03-29 16:23:01 +0000300 }
301
Ulf Adamsc5855302015-10-20 08:46:38 +0000302 /**
Ulf Adams94b72db2016-03-30 11:58:37 +0000303 * Returns the working directory of the server.
304 *
305 * <p>This is often the first entry on the {@code --package_path}, but not always.
306 * Callers should certainly not make this assumption. The Path returned may be null.
307 */
308 public Path getWorkspace() {
309 return getDirectories().getWorkspace();
310 }
311
312 public String getWorkspaceName() {
hlopkoc4134802017-04-04 13:53:14 +0000313 Path workspace = getDirectories().getWorkspace();
314 if (workspace == null) {
315 return "";
316 }
317 return workspace.getBaseName();
Ulf Adams94b72db2016-03-30 11:58:37 +0000318 }
319
320 /**
321 * Returns if the client passed a valid workspace to be used for the build.
322 */
323 public boolean inWorkspace() {
324 return getDirectories().inWorkspace();
325 }
326
327 /**
328 * Returns the output base directory associated with this Blaze server
329 * process. This is the base directory for shared Blaze state as well as tool
330 * and strategy specific subdirectories.
331 */
332 public Path getOutputBase() {
333 return getDirectories().getOutputBase();
334 }
335
336 /**
Ulf Adams94b72db2016-03-30 11:58:37 +0000337 * Returns the execution root directory associated with this Blaze server
338 * process. This is where all input and output files visible to the actual
339 * build reside.
340 */
341 public Path getExecRoot() {
hlopkoc4134802017-04-04 13:53:14 +0000342 return getDirectories().getExecRoot();
Ulf Adams94b72db2016-03-30 11:58:37 +0000343 }
344
345 /**
janakr0049e962017-04-05 21:41:23 +0000346 * Returns the directory where actions' outputs and errors will be written. Is below the directory
347 * returned by {@link #getExecRoot}.
348 */
349 public Path getActionConsoleOutputDirectory() {
350 return getDirectories().getActionConsoleOutputDirectory(getExecRoot());
351 }
352
353 /**
Ulf Adamsc5855302015-10-20 08:46:38 +0000354 * Returns the working directory of the {@code blaze} client process.
355 *
356 * <p>This may be equal to {@code BlazeRuntime#getWorkspace()}, or beneath it.
357 *
Ulf Adams94b72db2016-03-30 11:58:37 +0000358 * @see #getWorkspace()
Ulf Adamsc5855302015-10-20 08:46:38 +0000359 */
Ulf Adams80613022015-09-16 09:11:33 +0000360 public Path getWorkingDirectory() {
Ulf Adamsc5855302015-10-20 08:46:38 +0000361 return workingDirectory;
Ulf Adams80613022015-09-16 09:11:33 +0000362 }
363
Ulf Adams706b7f22015-10-20 09:06:45 +0000364 /**
365 * @return the OutputService in use, or null if none.
366 */
367 public OutputService getOutputService() {
368 return outputService;
369 }
370
Ulf Adams80613022015-09-16 09:11:33 +0000371 public ActionCache getPersistentActionCache() throws IOException {
Ulf Adamsab43b972016-03-30 12:23:50 +0000372 return workspace.getPersistentActionCache(reporter);
373 }
374
375 /**
Janak Ramakrishnan5e946732016-11-17 17:01:46 +0000376 * An array of String values useful if Blaze crashes. For now, just returns the build id as soon
377 * as it is determined.
Ulf Adamsab43b972016-03-30 12:23:50 +0000378 */
Janak Ramakrishnan5e946732016-11-17 17:01:46 +0000379 String[] getCrashData() {
Klaus Aehlig88a590a2016-08-12 08:56:30 +0000380 if (crashData == null) {
381 String buildId;
382 if (commandId == null) {
383 buildId = " (build id not set yet)";
384 } else {
385 buildId = commandId + " (build id)";
386 }
Janak Ramakrishnan5e946732016-11-17 17:01:46 +0000387 crashData = new String[] {buildId};
Klaus Aehlig88a590a2016-08-12 08:56:30 +0000388 }
389 return crashData;
390 }
391
392 private void setCommandIdInCrashData() {
393 // Update the command id in the crash data, if it is already generated
394 if (crashData != null && crashData.length >= 2) {
395 crashData[1] = getCommandId() + " (build id)";
396 }
Ulf Adamsab43b972016-03-30 12:23:50 +0000397 }
398
Ulf Adamsca2d8d22015-09-16 13:00:45 +0000399 /**
400 * This method only exists for the benefit of InfoCommand, which needs to construct a {@link
401 * BuildConfigurationCollection} without running a full loading phase. Don't add any more clients;
402 * instead, we should change info so that it doesn't need the configuration.
403 */
404 public BuildConfigurationCollection getConfigurations(OptionsProvider optionsProvider)
405 throws InvalidConfigurationException, InterruptedException {
406 BuildOptions buildOptions = runtime.createBuildOptions(optionsProvider);
407 boolean keepGoing = optionsProvider.getOptions(BuildView.Options.class).keepGoing;
Ulf Adamse5aaacf2015-09-24 12:40:30 +0000408 return getSkyframeExecutor().createConfigurations(reporter, runtime.getConfigurationFactory(),
Ulf Adamsf069d45b2016-04-15 07:54:24 +0000409 buildOptions, ImmutableSet.<String>of(), keepGoing);
Ulf Adamsca2d8d22015-09-16 13:00:45 +0000410 }
411
412 /**
Michael Staib6e5e8fb2016-10-04 21:26:37 +0000413 * Prevents any further interruption of this command by modules, and returns the final exit code
414 * from modules, or null if no modules requested an abrupt exit.
415 *
416 * <p>Always returns the same value on subsequent calls.
417 */
418 @Nullable
419 private ExitCode finalizeExitCode() {
420 // Set the pending exception so that further calls to exit(AbruptExitException) don't lead to
421 // unwanted thread interrupts.
422 if (pendingException.compareAndSet(null, new AbruptExitException(null))) {
423 return null;
424 }
425 if (Thread.currentThread() == commandThread) {
426 // We may have interrupted the thread in the process, so clear the interrupted bit.
427 // Whether the command was interrupted or not, it's about to be over, so don't interrupt later
428 // things happening on this thread.
429 Thread.interrupted();
430 }
431 // Extract the exit code (it can be null if someone has already called finalizeExitCode()).
432 return getPendingExitCode();
433 }
434
435 /**
Ulf Adamsca2d8d22015-09-16 13:00:45 +0000436 * Hook method called by the BlazeCommandDispatcher right before the dispatch
437 * of each command ends (while its outcome can still be modified).
438 */
439 ExitCode precompleteCommand(ExitCode originalExit) {
440 eventBus.post(new CommandPrecompleteEvent(originalExit));
441 // If Blaze did not suffer an infrastructure failure, check for errors in modules.
442 ExitCode exitCode = originalExit;
Michael Staib6e5e8fb2016-10-04 21:26:37 +0000443 ExitCode newExitCode = finalizeExitCode();
444 if (!originalExit.isInfrastructureFailure() && newExitCode != null) {
445 exitCode = newExitCode;
Ulf Adamsca2d8d22015-09-16 13:00:45 +0000446 }
447 return exitCode;
448 }
449
450 /**
Michael Staib6e5e8fb2016-10-04 21:26:37 +0000451 * Returns the current exit code requested by modules, or null if no exit has been requested.
452 */
453 @Nullable
454 public ExitCode getPendingExitCode() {
455 AbruptExitException exception = getPendingException();
456 return exception == null ? null : exception.getExitCode();
457 }
458
459 /**
460 * Retrieves the exception currently queued by a Blaze module.
461 *
462 * <p>Prefer getPendingExitCode or throwPendingException where appropriate.
463 */
464 public AbruptExitException getPendingException() {
465 return pendingException.get();
466 }
467
468 /**
Ulf Adamsca2d8d22015-09-16 13:00:45 +0000469 * Throws the exception currently queued by a Blaze module.
470 *
471 * <p>This should be called as often as is practical so that errors are reported as soon as
472 * possible. Ideally, we'd not need this, but the event bus swallows exceptions so we raise
473 * the exception this way.
474 */
475 public void throwPendingException() throws AbruptExitException {
Michael Staib6e5e8fb2016-10-04 21:26:37 +0000476 AbruptExitException exception = getPendingException();
Ulf Adams88f643c2015-09-17 10:55:52 +0000477 if (exception != null) {
Michael Staib6e5e8fb2016-10-04 21:26:37 +0000478 if (Thread.currentThread() == commandThread) {
479 // Throwing this exception counts as the requested interruption. Clear the interrupted bit.
480 Thread.interrupted();
481 }
Ulf Adams88f643c2015-09-17 10:55:52 +0000482 throw exception;
Ulf Adamsca2d8d22015-09-16 13:00:45 +0000483 }
Ulf Adams80613022015-09-16 09:11:33 +0000484 }
Ulf Adams3815b4c2015-09-18 07:34:13 +0000485
486 /**
487 * Initializes the package cache using the given options, and syncs the package cache. Also
488 * injects a defaults package using the options for the {@link BuildConfiguration}.
489 *
490 * @see DefaultsPackage
491 */
Ulf Adamsde14ade2016-10-14 14:20:31 +0000492 public void setupPackageCache(OptionsClassProvider options,
Ulf Adams3815b4c2015-09-18 07:34:13 +0000493 String defaultsPackageContents) throws InterruptedException, AbruptExitException {
Ulf Adamsc5855302015-10-20 08:46:38 +0000494 SkyframeExecutor skyframeExecutor = getSkyframeExecutor();
495 if (!skyframeExecutor.hasIncrementalState()) {
496 skyframeExecutor.resetEvaluator();
497 }
kchodorowdfcd5da82017-04-19 18:58:50 +0200498
499 for (BlazeModule module : runtime.getBlazeModules()) {
500 skyframeExecutor.injectExtraPrecomputedValues(module.getPrecomputedValues());
501 }
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000502 skyframeExecutor.sync(
503 reporter,
Ulf Adamsde14ade2016-10-14 14:20:31 +0000504 options.getOptions(PackageCacheOptions.class),
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000505 getOutputBase(),
506 getWorkingDirectory(),
507 defaultsPackageContents,
508 getCommandId(),
Damien Martin-Guillerez777f3af2017-02-08 17:22:02 +0000509 clientEnv,
Ulf Adamsde14ade2016-10-14 14:20:31 +0000510 timestampGranularityMonitor,
511 options);
Ulf Adams3815b4c2015-09-18 07:34:13 +0000512 }
513
Ulf Adamsb5146102015-10-20 08:57:26 +0000514 public void recordLastExecutionTime() {
Ulf Adamsab43b972016-03-30 12:23:50 +0000515 workspace.recordLastExecutionTime(getCommandStartTime());
Ulf Adamsb5146102015-10-20 08:57:26 +0000516 }
517
518 public void recordCommandStartTime(long commandStartTime) {
519 this.commandStartTime = commandStartTime;
520 }
521
Ulf Adams3815b4c2015-09-18 07:34:13 +0000522 public long getCommandStartTime() {
Ulf Adamsb5146102015-10-20 08:57:26 +0000523 return commandStartTime;
Ulf Adams3815b4c2015-09-18 07:34:13 +0000524 }
Ulf Adams47cb9162015-09-18 08:12:30 +0000525
Ulf Adamsc5855302015-10-20 08:46:38 +0000526 void setWorkingDirectory(Path workingDirectory) {
527 this.workingDirectory = workingDirectory;
528 }
529
Ulf Adamsebf1b2e2015-09-29 11:06:53 +0000530 /**
531 * Hook method called by the BlazeCommandDispatcher prior to the dispatch of
532 * each command.
533 *
534 * @param options The CommonCommandOptions used by every command.
535 * @throws AbruptExitException if this command is unsuitable to be run as specified
536 */
537 void beforeCommand(Command command, OptionsParser optionsParser,
Lukacs Berki2896dc02016-07-07 07:55:04 +0000538 CommonCommandOptions options, long execStartTimeNanos, long waitTimeInMs)
Ulf Adamsebf1b2e2015-09-29 11:06:53 +0000539 throws AbruptExitException {
Ulf Adamsb5146102015-10-20 08:57:26 +0000540 commandStartTime -= options.startupTime;
Ulf Adamsde14ade2016-10-14 14:20:31 +0000541 if (runtime.getStartupOptionsProvider().getOptions(BlazeServerStartupOptions.class).watchFS) {
542 try {
543 // TODO(ulfjack): Get rid of the startup option and drop this code.
544 optionsParser.parse("--watchfs");
545 } catch (OptionsParsingException e) {
546 // This should never happen.
547 throw new IllegalStateException(e);
548 }
549 }
Ulf Adams3877ebd2016-11-15 10:22:59 +0000550 this.commandName = command.name();
Ulf Adamsa0e3af42016-10-31 16:52:48 +0000551 this.options = optionsParser;
Ulf Adamsb5146102015-10-20 08:57:26 +0000552
Ulf Adams706b7f22015-10-20 09:06:45 +0000553 eventBus.post(new GotOptionsEvent(runtime.getStartupOptionsProvider(), optionsParser));
554 throwPendingException();
555
556 outputService = null;
557 BlazeModule outputModule = null;
Janak Ramakrishnan6c3ac8a2016-11-17 18:30:46 +0000558 if (command.builds()) {
559 for (BlazeModule module : runtime.getBlazeModules()) {
560 OutputService moduleService = module.getOutputService();
561 if (moduleService != null) {
562 if (outputService != null) {
563 throw new IllegalStateException(
564 String.format(
565 "More than one module (%s and %s) returns an output service",
566 module.getClass(), outputModule.getClass()));
567 }
568 outputService = moduleService;
569 outputModule = module;
Ulf Adams706b7f22015-10-20 09:06:45 +0000570 }
Philipp Wollermann49c20aa2016-08-25 12:59:52 +0000571 }
Ulf Adams706b7f22015-10-20 09:06:45 +0000572 }
573
Ulf Adams50e7db62015-10-20 09:14:16 +0000574 SkyframeExecutor skyframeExecutor = getSkyframeExecutor();
Eric Fellheimerf3b43af2015-11-13 19:51:20 +0000575 skyframeExecutor.setOutputService(outputService);
Ulf Adams706b7f22015-10-20 09:06:45 +0000576
Ulf Adams50e7db62015-10-20 09:14:16 +0000577 // Ensure that the working directory will be under the workspace directory.
Ulf Adams94b72db2016-03-30 11:58:37 +0000578 Path workspace = getWorkspace();
Ulf Adams50e7db62015-10-20 09:14:16 +0000579 Path workingDirectory;
Ulf Adams94b72db2016-03-30 11:58:37 +0000580 if (inWorkspace()) {
Ulf Adams50e7db62015-10-20 09:14:16 +0000581 workingDirectory = workspace.getRelative(options.clientCwd);
582 } else {
Ulf Adams94b72db2016-03-30 11:58:37 +0000583 workspace = FileSystemUtils.getWorkingDirectory(getDirectories().getFileSystem());
Ulf Adams50e7db62015-10-20 09:14:16 +0000584 workingDirectory = workspace;
585 }
Ulf Adams08663e62016-02-12 09:59:22 +0000586 this.relativeWorkingDirectory = workingDirectory.relativeTo(workspace);
Ulf Adams50e7db62015-10-20 09:14:16 +0000587 this.workingDirectory = workingDirectory;
588
Dmitry Lomov31fab292017-03-07 18:33:08 +0000589 updateClientEnv(options.clientEnv);
Ulf Adams50e7db62015-10-20 09:14:16 +0000590
591 // Fail fast in the case where a Blaze command forgets to install the package path correctly.
592 skyframeExecutor.setActive(false);
593 // Let skyframe figure out if it needs to store graph edges for this build.
594 skyframeExecutor.decideKeepIncrementalState(
595 runtime.getStartupOptionsProvider().getOptions(BlazeServerStartupOptions.class).batch,
janakr95d42802017-04-06 00:33:39 +0000596 optionsParser.getOptions(BuildView.Options.class));
Ulf Adams50e7db62015-10-20 09:14:16 +0000597
598 // Start the performance and memory profilers.
599 runtime.beforeCommand(this, options, execStartTimeNanos);
600
Damien Martin-Guillerezffbddeb2017-01-10 17:20:05 +0000601 // actionClientEnv contains the environment where values from actionEnvironment are
602 // overridden.
603 actionClientEnv.clear();
604 actionClientEnv.putAll(clientEnv);
605
Ulf Adams50e7db62015-10-20 09:14:16 +0000606 if (command.builds()) {
607 Map<String, String> testEnv = new TreeMap<>();
608 for (Map.Entry<String, String> entry :
609 optionsParser.getOptions(BuildConfiguration.Options.class).testEnvironment) {
610 testEnv.put(entry.getKey(), entry.getValue());
611 }
612
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000613 // Compute the set of environment variables that are whitelisted on the commandline
614 // for inheritence.
615 for (Map.Entry<String, String> entry :
616 optionsParser.getOptions(BuildConfiguration.Options.class).actionEnvironment) {
617 if (entry.getValue() == null) {
618 visibleClientEnv.add(entry.getKey());
619 } else {
620 visibleClientEnv.remove(entry.getKey());
Damien Martin-Guillerezffbddeb2017-01-10 17:20:05 +0000621 actionClientEnv.put(entry.getKey(), entry.getValue());
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000622 }
623 }
624
Ulf Adams50e7db62015-10-20 09:14:16 +0000625 try {
626 for (Map.Entry<String, String> entry : testEnv.entrySet()) {
627 if (entry.getValue() == null) {
628 String clientValue = clientEnv.get(entry.getKey());
629 if (clientValue != null) {
630 optionsParser.parse(OptionPriority.SOFTWARE_REQUIREMENT,
631 "test environment variable from client environment",
632 ImmutableList.of(
633 "--test_env=" + entry.getKey() + "=" + clientEnv.get(entry.getKey())));
634 }
635 }
636 }
637 } catch (OptionsParsingException e) {
638 throw new IllegalStateException(e);
639 }
640 }
Ulf Adams50e7db62015-10-20 09:14:16 +0000641
Lukacs Berki2896dc02016-07-07 07:55:04 +0000642 eventBus.post(new CommandStartEvent(
Klaus Aehlig88a590a2016-08-12 08:56:30 +0000643 command.name(), getCommandId(), getClientEnv(), workingDirectory, getDirectories(),
Lukacs Berki2896dc02016-07-07 07:55:04 +0000644 waitTimeInMs + options.waitTime));
Ulf Adamsebf1b2e2015-09-29 11:06:53 +0000645 }
Ulf Adams706b7f22015-10-20 09:06:45 +0000646
Nathan Harmatadd615202016-04-29 22:17:00 +0000647 /** Returns the name of the file system we are writing output to. */
648 public String determineOutputFileSystem() {
649 // If we have a fancy OutputService, this may be different between consecutive Blaze commands
650 // and so we need to compute it freshly. Otherwise, we can used the immutable value that's
651 // precomputed by our BlazeWorkspace.
Ulf Adams706b7f22015-10-20 09:06:45 +0000652 if (getOutputService() != null) {
Nathan Harmatadd615202016-04-29 22:17:00 +0000653 try (AutoProfiler p = profiled("Finding output file system", ProfilerTask.INFO)) {
654 return getOutputService().getFilesSystemName();
655 }
Ulf Adams706b7f22015-10-20 09:06:45 +0000656 }
Nathan Harmatadd615202016-04-29 22:17:00 +0000657 return workspace.getOutputBaseFilesystemTypeName();
Ulf Adams706b7f22015-10-20 09:06:45 +0000658 }
Damien Martin-Guillerezffbddeb2017-01-10 17:20:05 +0000659
660 /**
661 * Returns the client environment for which value specified in the command line with the flag
662 * --action_env have been enforced.
663 */
664 public Map<String, String> getActionClientEnv() {
665 return Collections.unmodifiableMap(actionClientEnv);
666 }
Ulf Adams633f5392015-09-15 11:13:08 +0000667}