blob: a5336fa9207f406908b04d0a907144d7c8712539 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
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
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010017import com.google.common.annotations.VisibleForTesting;
tomlua155b532017-11-08 20:12:47 +010018import com.google.common.base.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010019import com.google.common.collect.ImmutableList;
Ulf Adams6bfd2c52016-08-29 08:00:39 +000020import com.google.common.collect.ImmutableMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010021import com.google.common.collect.Lists;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010022import com.google.common.eventbus.SubscriberExceptionContext;
23import com.google.common.eventbus.SubscriberExceptionHandler;
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +000024import com.google.common.util.concurrent.Futures;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010025import com.google.common.util.concurrent.Uninterruptibles;
tomlu3d1a1942017-11-29 14:01:21 -080026import com.google.devtools.build.lib.actions.ActionKeyContext;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010027import com.google.devtools.build.lib.analysis.BlazeDirectories;
28import com.google.devtools.build.lib.analysis.BlazeVersionInfo;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010029import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
Ulf Adams15a23b92016-08-09 11:46:00 +000030import com.google.devtools.build.lib.analysis.ServerDirectories;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010031import com.google.devtools.build.lib.analysis.config.BuildOptions;
gregced79b9372017-08-10 21:51:15 +020032import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory;
ulfjackab21d182017-08-10 15:36:14 +020033import com.google.devtools.build.lib.analysis.test.CoverageReportActionFactory;
buchgr35ff63a2018-07-09 05:55:40 -070034import com.google.devtools.build.lib.buildeventstream.BuildEventArtifactUploaderFactoryMap;
philwo3bcb9f62017-09-06 12:52:21 +020035import com.google.devtools.build.lib.clock.BlazeClock;
36import com.google.devtools.build.lib.clock.Clock;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010037import com.google.devtools.build.lib.events.Event;
ulfjack8e099412018-06-15 02:53:42 -070038import com.google.devtools.build.lib.events.EventHandler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010039import com.google.devtools.build.lib.events.OutputFilter;
ulfjack9d8ff352018-02-15 01:50:36 -080040import com.google.devtools.build.lib.exec.BinTools;
Nathan Harmata42fb5602016-05-25 20:32:08 +000041import com.google.devtools.build.lib.packages.Package;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010042import com.google.devtools.build.lib.packages.PackageFactory;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010043import com.google.devtools.build.lib.packages.RuleClassProvider;
Nathan Harmatad4803012015-09-08 20:03:22 +000044import com.google.devtools.build.lib.profiler.AutoProfiler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010045import com.google.devtools.build.lib.profiler.MemoryProfiler;
46import com.google.devtools.build.lib.profiler.ProfilePhase;
47import com.google.devtools.build.lib.profiler.Profiler;
48import com.google.devtools.build.lib.profiler.Profiler.ProfiledTaskKinds;
49import com.google.devtools.build.lib.profiler.ProfilerTask;
ulfjackef5b63d2018-06-15 07:19:11 -070050import com.google.devtools.build.lib.profiler.SilentCloseable;
Nathan Harmataf0cc5b82016-03-18 14:56:28 +000051import com.google.devtools.build.lib.query2.AbstractBlazeQueryEnvironment;
52import com.google.devtools.build.lib.query2.QueryEnvironmentFactory;
Ulf Adams6bfd2c52016-08-29 08:00:39 +000053import com.google.devtools.build.lib.query2.engine.QueryEnvironment.QueryFunction;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010054import com.google.devtools.build.lib.query2.output.OutputFormatter;
Lukacs Berkice1445f2016-04-19 15:52:55 +000055import com.google.devtools.build.lib.runtime.BlazeCommandDispatcher.LockingMode;
Ulf Adams6bfd2c52016-08-29 08:00:39 +000056import com.google.devtools.build.lib.runtime.commands.InfoItem;
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +000057import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
lberki4741aa82018-02-05 07:59:13 -080058import com.google.devtools.build.lib.server.CommandProtos.EnvironmentVariable;
59import com.google.devtools.build.lib.server.CommandProtos.ExecRequest;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010060import com.google.devtools.build.lib.server.RPCServer;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010061import com.google.devtools.build.lib.server.signal.InterruptSignalHandler;
Lukacs Berki74dcfee2016-07-04 12:57:25 +000062import com.google.devtools.build.lib.shell.JavaSubprocessFactory;
Lukacs Berki74dcfee2016-07-04 12:57:25 +000063import com.google.devtools.build.lib.shell.SubprocessBuilder;
ulfjack27758e42017-09-07 13:41:33 +020064import com.google.devtools.build.lib.shell.SubprocessFactory;
Ulf Adams8afbd3c2017-02-28 10:42:48 +000065import com.google.devtools.build.lib.unix.UnixFileSystem;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010066import com.google.devtools.build.lib.util.AbruptExitException;
Chloe Calvarineaa3be72016-12-13 19:48:34 +000067import com.google.devtools.build.lib.util.CustomExitCodePublisher;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010068import com.google.devtools.build.lib.util.ExitCode;
69import com.google.devtools.build.lib.util.LoggingUtil;
70import com.google.devtools.build.lib.util.OS;
ccalvarin1cbe62a2017-08-14 21:09:07 +020071import com.google.devtools.build.lib.util.Pair;
Ulf Adams8afbd3c2017-02-28 10:42:48 +000072import com.google.devtools.build.lib.util.ProcessUtils;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010073import com.google.devtools.build.lib.util.ThreadUtils;
74import com.google.devtools.build.lib.util.io.OutErr;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010075import com.google.devtools.build.lib.vfs.FileSystem;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010076import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
77import com.google.devtools.build.lib.vfs.Path;
78import com.google.devtools.build.lib.vfs.PathFragment;
Ulf Adams8afbd3c2017-02-28 10:42:48 +000079import com.google.devtools.build.lib.windows.WindowsFileSystem;
Lukacs Berki74dcfee2016-07-04 12:57:25 +000080import com.google.devtools.build.lib.windows.WindowsSubprocessFactory;
ccalvarinf39dc6f2017-06-09 11:51:45 -040081import com.google.devtools.common.options.CommandNameCache;
82import com.google.devtools.common.options.InvocationPolicyParser;
ccalvarine8aae032017-08-22 07:17:44 +020083import com.google.devtools.common.options.OptionDefinition;
ccalvarin7cd9e882017-10-16 22:18:32 +020084import com.google.devtools.common.options.OptionPriority.PriorityCategory;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010085import com.google.devtools.common.options.OptionsBase;
86import com.google.devtools.common.options.OptionsClassProvider;
87import com.google.devtools.common.options.OptionsParser;
88import com.google.devtools.common.options.OptionsParsingException;
89import com.google.devtools.common.options.OptionsProvider;
90import com.google.devtools.common.options.TriState;
lberki4741aa82018-02-05 07:59:13 -080091import java.io.File;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010092import java.io.IOException;
93import java.io.OutputStream;
ccalvarin987f09f2017-08-31 19:50:39 +020094import java.lang.reflect.Type;
lberki4741aa82018-02-05 07:59:13 -080095import java.nio.charset.StandardCharsets;
ccalvarinaf79eb42018-02-22 12:41:44 -080096import java.time.Duration;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010097import java.util.ArrayList;
98import java.util.Arrays;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010099import java.util.Date;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100100import java.util.Iterator;
Ulf Adams47cb9162015-09-18 08:12:30 +0000101import java.util.LinkedHashMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100102import java.util.List;
Ulf Adamsb5a06f332016-06-21 14:55:49 +0000103import java.util.Locale;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100104import java.util.Map;
ccalvarin1cbe62a2017-08-14 21:09:07 +0200105import java.util.Optional;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100106import java.util.UUID;
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +0000107import java.util.concurrent.Future;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100108import java.util.concurrent.TimeUnit;
109import java.util.concurrent.atomic.AtomicInteger;
ccalvarine8aae032017-08-22 07:17:44 +0200110import java.util.function.Function;
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +0000111import java.util.logging.Handler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100112import java.util.logging.Level;
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +0000113import java.util.logging.LogRecord;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100114import java.util.logging.Logger;
Ulf Adamsfd370042017-06-16 15:52:06 +0200115import java.util.regex.Matcher;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100116import java.util.regex.Pattern;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100117import javax.annotation.Nullable;
118
119/**
Ulf Adams633f5392015-09-15 11:13:08 +0000120 * The BlazeRuntime class encapsulates the immutable configuration of the current instance. These
121 * runtime settings and services are available to most parts of any Blaze application for the
122 * duration of the batch run or server lifetime.
123 *
124 * <p>The parts specific to the current command are stored in {@link CommandEnvironment}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100125 */
126public final class BlazeRuntime {
Michajlo Matijkiwa132f0e2016-11-21 17:30:43 +0000127 private static final Pattern suppressFromLog =
Ulf Adamsfd370042017-06-16 15:52:06 +0200128 Pattern.compile("--client_env=([^=]*(?:auth|pass|cookie)[^=]*)=", Pattern.CASE_INSENSITIVE);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100129
lberki97abb522017-09-04 18:51:57 +0200130 private static final Logger logger = Logger.getLogger(BlazeRuntime.class.getName());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100131
tomluf903eb52017-10-27 12:12:11 -0400132 private final FileSystem fileSystem;
Ulf Adams50e7db62015-10-20 09:14:16 +0000133 private final Iterable<BlazeModule> blazeModules;
134 private final Map<String, BlazeCommand> commandMap = new LinkedHashMap<>();
135 private final Clock clock;
Lukacs Berki9f603952017-01-13 14:04:08 +0000136 private final Runnable abruptShutdownHandler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100137
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100138 private final PackageFactory packageFactory;
gregced79b9372017-08-10 21:51:15 +0200139 private final ImmutableList<ConfigurationFragmentFactory> configurationFragmentFactories;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100140 private final ConfiguredRuleClassProvider ruleClassProvider;
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000141 // For bazel info.
142 private final ImmutableMap<String, InfoItem> infoItems;
143 // For bazel query.
144 private final QueryEnvironmentFactory queryEnvironmentFactory;
145 private final ImmutableList<QueryFunction> queryFunctions;
Ulf Adams54eeac52016-08-29 09:07:35 +0000146 private final ImmutableList<OutputFormatter> queryOutputFormatters;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100147
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100148 private final AtomicInteger storedExitCode = new AtomicInteger();
149
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100150 // We pass this through here to make it available to the MasterLogWriter.
151 private final OptionsProvider startupOptionsProvider;
152
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100153 private final ProjectFile.Provider projectFileProvider;
Chloe Calvarin6b607182017-03-27 15:37:50 +0000154 @Nullable private final InvocationPolicy moduleInvocationPolicy;
Janak Ramakrishnan1d899602016-04-28 03:48:55 +0000155 private final String defaultsPackageContent;
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000156 private final SubscriberExceptionHandler eventBusExceptionHandler;
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +0000157 private final String productName;
buchgr35ff63a2018-07-09 05:55:40 -0700158 private final BuildEventArtifactUploaderFactoryMap buildEventArtifactUploaderFactoryMap;
tomlu3d1a1942017-11-29 14:01:21 -0800159 private final ActionKeyContext actionKeyContext;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100160
Ulf Adams50e7db62015-10-20 09:14:16 +0000161 // Workspace state (currently exactly one workspace per server)
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000162 private BlazeWorkspace workspace;
Ulf Adams50e7db62015-10-20 09:14:16 +0000163
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000164 private BlazeRuntime(
tomluf903eb52017-10-27 12:12:11 -0400165 FileSystem fileSystem,
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000166 QueryEnvironmentFactory queryEnvironmentFactory,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000167 ImmutableList<QueryFunction> queryFunctions,
Ulf Adams54eeac52016-08-29 09:07:35 +0000168 ImmutableList<OutputFormatter> queryOutputFormatters,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000169 PackageFactory pkgFactory,
170 ConfiguredRuleClassProvider ruleClassProvider,
gregced79b9372017-08-10 21:51:15 +0200171 ImmutableList<ConfigurationFragmentFactory> configurationFragmentFactories,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000172 ImmutableMap<String, InfoItem> infoItems,
tomlu3d1a1942017-11-29 14:01:21 -0800173 ActionKeyContext actionKeyContext,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000174 Clock clock,
Lukacs Berki9f603952017-01-13 14:04:08 +0000175 Runnable abruptShutdownHandler,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000176 OptionsProvider startupOptionsProvider,
177 Iterable<BlazeModule> blazeModules,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100178 SubscriberExceptionHandler eventBusExceptionHandler,
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000179 ProjectFile.Provider projectFileProvider,
Chloe Calvarin6b607182017-03-27 15:37:50 +0000180 InvocationPolicy moduleInvocationPolicy,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000181 Iterable<BlazeCommand> commands,
Klaus Aehligdadde6f2017-01-18 17:32:07 +0000182 String productName,
buchgr35ff63a2018-07-09 05:55:40 -0700183 BuildEventArtifactUploaderFactoryMap buildEventArtifactUploaderFactoryMap) {
Ulf Adams50e7db62015-10-20 09:14:16 +0000184 // Server state
tomluf903eb52017-10-27 12:12:11 -0400185 this.fileSystem = fileSystem;
Ulf Adams50e7db62015-10-20 09:14:16 +0000186 this.blazeModules = blazeModules;
187 overrideCommands(commands);
188
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100189 this.packageFactory = pkgFactory;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100190 this.projectFileProvider = projectFileProvider;
Chloe Calvarin6b607182017-03-27 15:37:50 +0000191 this.moduleInvocationPolicy = moduleInvocationPolicy;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100192
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100193 this.ruleClassProvider = ruleClassProvider;
gregced79b9372017-08-10 21:51:15 +0200194 this.configurationFragmentFactories = configurationFragmentFactories;
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000195 this.infoItems = infoItems;
tomlu3d1a1942017-11-29 14:01:21 -0800196 this.actionKeyContext = actionKeyContext;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100197 this.clock = clock;
Lukacs Berki9f603952017-01-13 14:04:08 +0000198 this.abruptShutdownHandler = abruptShutdownHandler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100199 this.startupOptionsProvider = startupOptionsProvider;
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000200 this.queryEnvironmentFactory = queryEnvironmentFactory;
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000201 this.queryFunctions = queryFunctions;
Ulf Adams54eeac52016-08-29 09:07:35 +0000202 this.queryOutputFormatters = queryOutputFormatters;
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000203 this.eventBusExceptionHandler = eventBusExceptionHandler;
Janak Ramakrishnan1d899602016-04-28 03:48:55 +0000204
205 this.defaultsPackageContent =
Chloe Calvarin6b607182017-03-27 15:37:50 +0000206 ruleClassProvider.getDefaultsPackageContent(getModuleInvocationPolicy());
Janak Ramakrishnane9cd0f32016-04-29 22:46:16 +0000207 CommandNameCache.CommandNameCacheInstance.INSTANCE.setCommandNameCache(
208 new CommandNameCacheImpl(getCommandMap()));
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +0000209 this.productName = productName;
buchgr35ff63a2018-07-09 05:55:40 -0700210 this.buildEventArtifactUploaderFactoryMap = buildEventArtifactUploaderFactoryMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100211 }
212
ulfjack5a3b24c2017-06-08 17:10:06 +0200213 public BlazeWorkspace initWorkspace(BlazeDirectories directories, BinTools binTools)
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000214 throws AbruptExitException {
Ulf Adamsea46dfc2016-08-10 08:44:19 +0000215 Preconditions.checkState(this.workspace == null);
Ulf Adamsde14ade2016-10-14 14:20:31 +0000216 WorkspaceBuilder builder = new WorkspaceBuilder(directories, binTools);
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000217 for (BlazeModule module : blazeModules) {
ulfjack94bee752017-06-13 19:13:35 +0200218 module.workspaceInit(this, directories, builder);
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000219 }
janakreea486c2017-09-22 15:38:50 -0400220 this.workspace =
221 builder.build(this, packageFactory, ruleClassProvider, eventBusExceptionHandler);
ulfjack5a3b24c2017-06-08 17:10:06 +0200222 return workspace;
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000223 }
224
Ulf Adams33644a42016-08-10 11:32:41 +0000225 @Nullable public CoverageReportActionFactory getCoverageReportActionFactory(
226 OptionsClassProvider commandOptions) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100227 CoverageReportActionFactory firstFactory = null;
228 for (BlazeModule module : blazeModules) {
Ulf Adams33644a42016-08-10 11:32:41 +0000229 CoverageReportActionFactory factory = module.getCoverageReportFactory(commandOptions);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100230 if (factory != null) {
Dan Fabulich1d35ca02018-07-05 16:08:06 -0700231 Preconditions.checkState(
232 firstFactory == null, "only one Bazel Module can have a Coverage Report Factory");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100233 firstFactory = factory;
234 }
235 }
236 return firstFactory;
237 }
238
239 /**
Ulf Adams47cb9162015-09-18 08:12:30 +0000240 * Adds the given command under the given name to the map of commands.
241 *
242 * @throws AssertionError if the name is already used by another command.
243 */
244 private void addCommand(BlazeCommand command) {
245 String name = command.getClass().getAnnotation(Command.class).name();
246 if (commandMap.containsKey(name)) {
247 throw new IllegalStateException("Command name or alias " + name + " is already used.");
248 }
249 commandMap.put(name, command);
250 }
251
252 final void overrideCommands(Iterable<BlazeCommand> commands) {
253 commandMap.clear();
254 for (BlazeCommand command : commands) {
255 addCommand(command);
256 }
Ulf Adams47cb9162015-09-18 08:12:30 +0000257 }
258
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +0000259 @Nullable
Chloe Calvarin6b607182017-03-27 15:37:50 +0000260 public InvocationPolicy getModuleInvocationPolicy() {
261 return moduleInvocationPolicy;
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +0000262 }
263
ulfjackcca111f2018-06-15 06:15:57 -0700264 /** Configure profiling based on the provided options. */
265 void initProfiler(
ulfjack8e099412018-06-15 02:53:42 -0700266 EventHandler eventHandler,
267 BlazeWorkspace workspace,
ulfjackf92107b2018-06-08 14:14:05 -0700268 CommonCommandOptions options,
269 UUID buildID,
270 long execStartTimeNanos) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100271 OutputStream out = null;
272 boolean recordFullProfilerData = false;
273 ProfiledTaskKinds profiledTasks = ProfiledTaskKinds.NONE;
ulfjack4a12e072018-06-14 01:15:13 -0700274 Profiler.Format format = Profiler.Format.BINARY_BAZEL_FORMAT;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100275 try {
ulfjack400fffe2018-06-12 12:21:25 -0700276 if (options.enableTracer) {
277 Path profilePath = options.profilePath != null
ulfjack8e099412018-06-15 02:53:42 -0700278 ? workspace.getWorkspace().getRelative(options.profilePath)
279 : workspace.getOutputBase().getRelative("command.profile");
ulfjack400fffe2018-06-12 12:21:25 -0700280 recordFullProfilerData = false;
281 out = profilePath.getOutputStream();
ulfjack8e099412018-06-15 02:53:42 -0700282 eventHandler.handle(Event.info("Writing tracer profile to '" + profilePath + "'"));
ulfjack400fffe2018-06-12 12:21:25 -0700283 profiledTasks = ProfiledTaskKinds.ALL_FOR_TRACE;
ulfjack4a12e072018-06-14 01:15:13 -0700284 format = Profiler.Format.JSON_TRACE_FILE_FORMAT;
ulfjack400fffe2018-06-12 12:21:25 -0700285 } else if (options.profilePath != null) {
ulfjack8e099412018-06-15 02:53:42 -0700286 Path profilePath = workspace.getWorkspace().getRelative(options.profilePath);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100287
288 recordFullProfilerData = options.recordFullProfilerData;
ulfjack21d60de2018-06-12 07:38:51 -0700289 out = profilePath.getOutputStream();
ulfjack8e099412018-06-15 02:53:42 -0700290 eventHandler.handle(Event.info("Writing profile data to '" + profilePath + "'"));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100291 profiledTasks = ProfiledTaskKinds.ALL;
292 } else if (options.alwaysProfileSlowOperations) {
293 recordFullProfilerData = false;
294 out = null;
295 profiledTasks = ProfiledTaskKinds.SLOWEST;
296 }
297 if (profiledTasks != ProfiledTaskKinds.NONE) {
ulfjack8e099412018-06-15 02:53:42 -0700298 Profiler profiler = Profiler.instance();
299 profiler.start(
ulfjackf92107b2018-06-08 14:14:05 -0700300 profiledTasks,
301 out,
ulfjack4a12e072018-06-14 01:15:13 -0700302 format,
ulfjack21d60de2018-06-12 07:38:51 -0700303 String.format(
304 "%s profile for %s at %s, build ID: %s",
305 getProductName(),
ulfjack8e099412018-06-15 02:53:42 -0700306 workspace.getOutputBase(),
ulfjack21d60de2018-06-12 07:38:51 -0700307 new Date(),
308 buildID),
ulfjackf92107b2018-06-08 14:14:05 -0700309 recordFullProfilerData,
310 clock,
311 execStartTimeNanos);
ulfjack8e099412018-06-15 02:53:42 -0700312 // Instead of logEvent() we're calling the low level function to pass the timings we took in
313 // the launcher. We're setting the INIT phase marker so that it follows immediately the
314 // LAUNCH phase.
315 long startupTimeNanos = options.startupTime * 1000000L;
316 profiler.logSimpleTaskDuration(
317 execStartTimeNanos - startupTimeNanos,
318 Duration.ZERO,
319 ProfilerTask.PHASE,
320 ProfilePhase.LAUNCH.description);
321 profiler.logSimpleTaskDuration(
322 execStartTimeNanos, Duration.ZERO, ProfilerTask.PHASE, ProfilePhase.INIT.description);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100323 }
324 } catch (IOException e) {
ulfjack8e099412018-06-15 02:53:42 -0700325 eventHandler.handle(Event.error("Error while creating profile file: " + e.getMessage()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100326 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100327 }
328
tomluf903eb52017-10-27 12:12:11 -0400329 public FileSystem getFileSystem() {
330 return fileSystem;
331 }
332
Ulf Adamsab43b972016-03-30 12:23:50 +0000333 public BlazeWorkspace getWorkspace() {
334 return workspace;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100335 }
336
tomlu3d1a1942017-11-29 14:01:21 -0800337 public ActionKeyContext getActionKeyContext() {
338 return actionKeyContext;
339 }
340
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100341 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100342 * The directory in which blaze stores the server state - that is, the socket
343 * file and a log.
344 */
Ulf Adams94b72db2016-03-30 11:58:37 +0000345 private Path getServerDirectory() {
Ulf Adamsab43b972016-03-30 12:23:50 +0000346 return getWorkspace().getDirectories().getOutputBase().getChild("server");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100347 }
348
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100349 /**
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000350 * Returns the {@link QueryEnvironmentFactory} that should be used to create a
351 * {@link AbstractBlazeQueryEnvironment}, whenever one is needed.
352 */
353 public QueryEnvironmentFactory getQueryEnvironmentFactory() {
354 return queryEnvironmentFactory;
355 }
356
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000357 public ImmutableList<QueryFunction> getQueryFunctions() {
358 return queryFunctions;
359 }
360
Ulf Adams54eeac52016-08-29 09:07:35 +0000361 public ImmutableList<OutputFormatter> getQueryOutputFormatters() {
362 return queryOutputFormatters;
363 }
364
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000365 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100366 * Returns the package factory.
367 */
368 public PackageFactory getPackageFactory() {
369 return packageFactory;
370 }
371
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100372 /**
373 * Returns the rule class provider.
374 */
375 public ConfiguredRuleClassProvider getRuleClassProvider() {
376 return ruleClassProvider;
377 }
378
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000379 public ImmutableMap<String, InfoItem> getInfoItems() {
380 return infoItems;
381 }
382
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100383 public Iterable<BlazeModule> getBlazeModules() {
384 return blazeModules;
385 }
386
mjhalupka5d7fa7b2018-03-22 13:37:38 -0700387 public BuildOptions getDefaultBuildOptions() {
388 BuildOptions options = null;
389 for (BlazeModule module : blazeModules) {
390 BuildOptions optionsFromModule = module.getDefaultBuildOptions(this);
391 if (optionsFromModule != null) {
392 if (options == null) {
393 options = optionsFromModule;
394 } else {
395 throw new IllegalArgumentException(
Dan Fabulich1d35ca02018-07-05 16:08:06 -0700396 "Two or more bazel modules contained default build options.");
mjhalupka5d7fa7b2018-03-22 13:37:38 -0700397 }
398 }
399 }
400 if (options == null) {
Dan Fabulich1d35ca02018-07-05 16:08:06 -0700401 throw new IllegalArgumentException("No default build options specified in any Bazel module");
mjhalupka5d7fa7b2018-03-22 13:37:38 -0700402 }
403 return options;
404 }
405
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100406 @SuppressWarnings("unchecked")
407 public <T extends BlazeModule> T getBlazeModule(Class<T> moduleClass) {
408 for (BlazeModule module : blazeModules) {
409 if (module.getClass() == moduleClass) {
410 return (T) module;
411 }
412 }
413
414 return null;
415 }
416
gregced79b9372017-08-10 21:51:15 +0200417 public ImmutableList<ConfigurationFragmentFactory> getConfigurationFragmentFactories() {
418 return configurationFragmentFactories;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100419 }
420
421 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100422 * Returns a provider for project file objects. Can be null if no such provider was set by any of
423 * the modules.
424 */
425 @Nullable
426 public ProjectFile.Provider getProjectFileProvider() {
427 return projectFileProvider;
428 }
429
shahan2cbfb892018-03-28 09:00:11 -0700430 public Path getOutputBase() {
431 return getWorkspace().getDirectories().getOutputBase();
432 }
433
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100434 /**
435 * Hook method called by the BlazeCommandDispatcher prior to the dispatch of
436 * each command.
437 *
438 * @param options The CommonCommandOptions used by every command.
439 * @throws AbruptExitException if this command is unsuitable to be run as specified
440 */
ulfjackcca111f2018-06-15 06:15:57 -0700441 void beforeCommand(CommandEnvironment env, CommonCommandOptions options)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100442 throws AbruptExitException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100443 if (options.memoryProfilePath != null) {
Ulf Adamsc5855302015-10-20 08:46:38 +0000444 Path memoryProfilePath = env.getWorkingDirectory().getRelative(options.memoryProfilePath);
janakrdfa0b122018-02-28 09:46:06 -0800445 MemoryProfiler.instance()
446 .setStableMemoryParameters(options.memoryProfileStableHeapParameters);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100447 try {
448 MemoryProfiler.instance().start(memoryProfilePath.getOutputStream());
449 } catch (IOException e) {
Ulf Adams633f5392015-09-15 11:13:08 +0000450 env.getReporter().handle(
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100451 Event.error("Error while creating memory profile file: " + e.getMessage()));
452 }
453 }
454
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100455 // Initialize exit code to dummy value for afterCommand.
456 storedExitCode.set(ExitCode.RESERVED.getNumericExitCode());
457 }
458
459 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100460 * Posts the {@link CommandCompleteEvent}, so that listeners can tidy up. Called by {@link
461 * #afterCommand}, and by BugReport when crashing from an exception in an async thread.
462 */
Janak Ramakrishnan39a55132016-05-23 21:55:20 +0000463 void notifyCommandComplete(int exitCode) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100464 if (!storedExitCode.compareAndSet(ExitCode.RESERVED.getNumericExitCode(), exitCode)) {
465 // This command has already been called, presumably because there is a race between the main
466 // thread and a worker thread that crashed. Don't try to arbitrate the dispute. If the main
467 // thread won the race (unlikely, but possible), this may be incorrectly logged as a success.
468 return;
469 }
Janak Ramakrishnana50b7b72016-06-02 21:46:40 +0000470 workspace.getSkyframeExecutor().getEventBus().post(new CommandCompleteEvent(exitCode));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100471 }
472
janakrd1af4302018-04-24 09:32:36 -0700473 /**
474 * Hook method called by the BlazeCommandDispatcher after the dispatch of each command. Returns a
475 * new exit code in case exceptions were encountered during cleanup.
476 */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100477 @VisibleForTesting
janakrd1af4302018-04-24 09:32:36 -0700478 public int afterCommand(CommandEnvironment env, int exitCode) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100479 // Remove any filters that the command might have added to the reporter.
Ulf Adams633f5392015-09-15 11:13:08 +0000480 env.getReporter().setOutputFilter(OutputFilter.OUTPUT_EVERYTHING);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100481
482 notifyCommandComplete(exitCode);
483
484 for (BlazeModule module : blazeModules) {
ulfjackef5b63d2018-06-15 07:19:11 -0700485 try (SilentCloseable c = Profiler.instance().profile(module + ".afterCommand")) {
486 module.afterCommand();
487 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100488 }
489
fellyac7d5f62018-05-15 10:57:33 -0700490 // Wipe the dependency graph if requested. Note that this method always runs at the end of
491 // a commands unless the server crashes, in which case no inmemory state will linger for the
492 // next build anyway.
493 CommonCommandOptions commonOptions =
494 Preconditions.checkNotNull(env.getOptions().getOptions(CommonCommandOptions.class));
495 if (!commonOptions.keepStateAfterBuild) {
ccalvarin28c20f72018-01-22 07:53:56 -0800496 workspace.getSkyframeExecutor().resetEvaluator();
497 }
498
janakrd1af4302018-04-24 09:32:36 -0700499 // Build-related commands already call this hook in BuildTool#stopRequest, but non-build
500 // commands might also need to notify the SkyframeExecutor. It's called in #stopRequest so that
501 // timing metrics for builds can be more accurate (since this call can be slow).
502 try {
503 workspace.getSkyframeExecutor().notifyCommandComplete();
504 } catch (InterruptedException e) {
505 exitCode = ExitCode.INTERRUPTED.getNumericExitCode();
506 Thread.currentThread().interrupt();
507 }
508
Ulf Adamsab43b972016-03-30 12:23:50 +0000509 env.getBlazeWorkspace().clearEventBus();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100510
511 try {
512 Profiler.instance().stop();
513 MemoryProfiler.instance().stop();
514 } catch (IOException e) {
Ulf Adams633f5392015-09-15 11:13:08 +0000515 env.getReporter().handle(Event.error("Error while writing profile file: " + e.getMessage()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100516 }
Klaus Aehlig777b30d2017-02-24 16:30:15 +0000517 env.getReporter().clearEventBus();
tomlu3d1a1942017-11-29 14:01:21 -0800518
519 actionKeyContext.clear();
janakrd1af4302018-04-24 09:32:36 -0700520 return exitCode;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100521 }
522
523 // Make sure we keep a strong reference to this logger, so that the
524 // configuration isn't lost when the gc kicks in.
525 private static Logger templateLogger = Logger.getLogger("com.google.devtools.build");
526
527 /**
528 * Configures "com.google.devtools.build.*" loggers to the given
529 * {@code level}. Note: This code relies on static state.
530 */
531 public static void setupLogging(Level level) {
532 templateLogger.setLevel(level);
533 templateLogger.info("Log level: " + templateLogger.getLevel());
534 }
535
536 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100537 * Returns the Clock-instance used for the entire build. Before,
538 * individual classes (such as Profiler) used to specify the type
539 * of clock (e.g. EpochClock) they wanted to use. This made it
540 * difficult to get Blaze working on Windows as some of the clocks
541 * available for Linux aren't (directly) available on Windows.
542 * Setting the Blaze-wide clock upon construction of BlazeRuntime
543 * allows injecting whatever Clock instance should be used from
544 * BlazeMain.
545 *
546 * @return The Blaze-wide clock
547 */
548 public Clock getClock() {
549 return clock;
550 }
551
552 public OptionsProvider getStartupOptionsProvider() {
553 return startupOptionsProvider;
554 }
555
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100556 public Map<String, BlazeCommand> getCommandMap() {
557 return commandMap;
558 }
559
Julio Merinod1619b752016-10-17 09:50:11 +0000560 /** Invokes {@link BlazeModule#blazeShutdown()} on all registered modules. */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100561 public void shutdown() {
562 for (BlazeModule module : blazeModules) {
563 module.blazeShutdown();
564 }
565 }
566
Lukacs Berki9f603952017-01-13 14:04:08 +0000567 public void prepareForAbruptShutdown() {
568 if (abruptShutdownHandler != null) {
569 abruptShutdownHandler.run();
570 }
571 }
572
Julio Merinod1619b752016-10-17 09:50:11 +0000573 /** Invokes {@link BlazeModule#blazeShutdownOnCrash()} on all registered modules. */
574 public void shutdownOnCrash() {
575 for (BlazeModule module : blazeModules) {
576 module.blazeShutdownOnCrash();
577 }
578 }
579
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100580 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100581 * Returns the defaults package for the default settings. Should only be called by commands that
582 * do <i>not</i> process {@link BuildOptions}, since build options can alter the contents of the
583 * defaults package, which will not be reflected here.
584 */
585 public String getDefaultsPackageContent() {
Janak Ramakrishnan1d899602016-04-28 03:48:55 +0000586 return defaultsPackageContent;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100587 }
588
589 /**
590 * Returns the defaults package for the given options taken from an optionsProvider.
591 */
592 public String getDefaultsPackageContent(OptionsClassProvider optionsProvider) {
593 return ruleClassProvider.getDefaultsPackageContent(optionsProvider);
594 }
595
596 /**
597 * Creates a BuildOptions class for the given options taken from an optionsProvider.
598 */
599 public BuildOptions createBuildOptions(OptionsClassProvider optionsProvider) {
600 return ruleClassProvider.createBuildOptions(optionsProvider);
601 }
602
603 /**
604 * An EventBus exception handler that will report the exception to a remote server, if a
605 * handler is registered.
606 */
607 public static final class RemoteExceptionHandler implements SubscriberExceptionHandler {
608 @Override
609 public void handleException(Throwable exception, SubscriberExceptionContext context) {
lberki97abb522017-09-04 18:51:57 +0200610 logger.log(Level.SEVERE, "Failure in EventBus subscriber", exception);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100611 LoggingUtil.logToRemote(Level.SEVERE, "Failure in EventBus subscriber.", exception);
612 }
613 }
614
615 /**
616 * An EventBus exception handler that will call BugReport.handleCrash exiting
617 * the current thread.
618 */
619 public static final class BugReportingExceptionHandler implements SubscriberExceptionHandler {
620 @Override
621 public void handleException(Throwable exception, SubscriberExceptionContext context) {
622 BugReport.handleCrash(exception);
623 }
624 }
625
626 /**
627 * Main method for the Blaze server startup. Note: This method logs
628 * exceptions to remote servers. Do not add this to a unittest.
629 */
630 public static void main(Iterable<Class<? extends BlazeModule>> moduleClasses, String[] args) {
631 setupUncaughtHandler(args);
632 List<BlazeModule> modules = createModules(moduleClasses);
Googlerc8c64e72015-03-23 23:22:18 +0000633 // blaze.cc will put --batch first if the user set it.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100634 if (args.length >= 1 && args[0].equals("--batch")) {
635 // Run Blaze in batch mode.
636 System.exit(batchMain(modules, args));
637 }
lberki97abb522017-09-04 18:51:57 +0200638 logger.info(
Dan Fabulich1d35ca02018-07-05 16:08:06 -0700639 "Starting Bazel server with " + maybeGetPidString() + "args " + Arrays.toString(args));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100640 try {
641 // Run Blaze in server mode.
642 System.exit(serverMain(modules, OutErr.SYSTEM_OUT_ERR, args));
643 } catch (RuntimeException | Error e) { // A definite bug...
644 BugReport.printBug(OutErr.SYSTEM_OUT_ERR, e);
645 BugReport.sendBugReport(e, Arrays.asList(args));
646 System.exit(ExitCode.BLAZE_INTERNAL_ERROR.getNumericExitCode());
647 throw e; // Shouldn't get here.
648 }
649 }
650
651 @VisibleForTesting
652 public static List<BlazeModule> createModules(
653 Iterable<Class<? extends BlazeModule>> moduleClasses) {
654 ImmutableList.Builder<BlazeModule> result = ImmutableList.builder();
655 for (Class<? extends BlazeModule> moduleClass : moduleClasses) {
656 try {
Ulf Adams653a16a2016-08-11 12:39:46 +0000657 BlazeModule module = moduleClass.getConstructor().newInstance();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100658 result.add(module);
659 } catch (Throwable e) {
660 throw new IllegalStateException("Cannot instantiate module " + moduleClass.getName(), e);
661 }
662 }
663
664 return result.build();
665 }
666
667 /**
Ulf Adamsfd370042017-06-16 15:52:06 +0200668 * Generates a string form of a request to be written to the logs, filtering the user environment
669 * to remove anything that looks private. The current filter criteria removes any variable whose
670 * name includes "auth", "pass", or "cookie".
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100671 *
672 * @param requestStrings
673 * @return the filtered request to write to the log.
674 */
lberkiac8eb422018-02-06 09:27:30 -0800675 public static String getRequestLogString(List<String> requestStrings) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100676 StringBuilder buf = new StringBuilder();
677 buf.append('[');
678 String sep = "";
Ulf Adamsfd370042017-06-16 15:52:06 +0200679 Matcher m = suppressFromLog.matcher("");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100680 for (String s : requestStrings) {
681 buf.append(sep);
Ulf Adamsfd370042017-06-16 15:52:06 +0200682 m.reset(s);
683 if (m.lookingAt()) {
684 buf.append(m.group());
685 buf.append("__private_value_removed__");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100686 } else {
687 buf.append(s);
688 }
689 sep = ", ";
690 }
691 buf.append(']');
692 return buf.toString();
693 }
694
695 /**
696 * Command line options split in to two parts: startup options and everything else.
697 */
698 @VisibleForTesting
699 static class CommandLineOptions {
700 private final List<String> startupArgs;
701 private final List<String> otherArgs;
702
703 CommandLineOptions(List<String> startupArgs, List<String> otherArgs) {
704 this.startupArgs = ImmutableList.copyOf(startupArgs);
705 this.otherArgs = ImmutableList.copyOf(otherArgs);
706 }
707
708 public List<String> getStartupArgs() {
709 return startupArgs;
710 }
711
712 public List<String> getOtherArgs() {
713 return otherArgs;
714 }
715 }
716
717 /**
Lukacs Berki3d486832016-10-26 12:51:38 +0000718 * Splits given options into two lists - arguments matching options defined in this class and
719 * everything else, while preserving order in each list.
720 *
721 * <p>Note that this method relies on the startup options always being in the
722 * <code>--flag=ARG</code> form (instead of <code>--flag ARG</code>). This is enforced by
723 * <code>GetArgumentArray()</code> in <code>blaze.cc</code> by reconstructing the startup
724 * options from their parsed versions instead of using <code>argv</code> verbatim.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100725 */
726 static CommandLineOptions splitStartupOptions(
727 Iterable<BlazeModule> modules, String... args) {
728 List<String> prefixes = new ArrayList<>();
ccalvarin987f09f2017-08-31 19:50:39 +0200729 List<OptionDefinition> startupOptions = Lists.newArrayList();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100730 for (Class<? extends OptionsBase> defaultOptions
731 : BlazeCommandUtils.getStartupOptions(modules)) {
ccalvarin987f09f2017-08-31 19:50:39 +0200732 startupOptions.addAll(OptionsParser.getOptionDefinitions(defaultOptions));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100733 }
734
ccalvarin987f09f2017-08-31 19:50:39 +0200735 for (OptionDefinition optionDefinition : startupOptions) {
736 Type optionType = optionDefinition.getField().getType();
737 prefixes.add("--" + optionDefinition.getOptionName());
738 if (optionType == boolean.class || optionType == TriState.class) {
739 prefixes.add("--no" + optionDefinition.getOptionName());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100740 }
741 }
742
743 List<String> startupArgs = new ArrayList<>();
744 List<String> otherArgs = Lists.newArrayList(args);
745
746 for (Iterator<String> argi = otherArgs.iterator(); argi.hasNext(); ) {
747 String arg = argi.next();
748 if (!arg.startsWith("--")) {
749 break; // stop at command - all startup options would be specified before it.
750 }
751 for (String prefix : prefixes) {
752 if (arg.startsWith(prefix)) {
753 startupArgs.add(arg);
754 argi.remove();
755 break;
756 }
757 }
758 }
759 return new CommandLineOptions(startupArgs, otherArgs);
760 }
761
lberki4741aa82018-02-05 07:59:13 -0800762 private static InterruptSignalHandler captureSigint() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100763 final Thread mainThread = Thread.currentThread();
764 final AtomicInteger numInterrupts = new AtomicInteger();
765
laurentlb3d2a68c2017-06-30 00:32:04 +0200766 final Runnable interruptWatcher =
767 () -> {
768 int count = 0;
769 // Not an actual infinite loop because it's run in a daemon thread.
770 while (true) {
771 count++;
772 Uninterruptibles.sleepUninterruptibly(10, TimeUnit.SECONDS);
lberki97abb522017-09-04 18:51:57 +0200773 logger.warning("Slow interrupt number " + count + " in batch mode");
laurentlb3d2a68c2017-06-30 00:32:04 +0200774 ThreadUtils.warnAboutSlowInterrupt();
775 }
776 };
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100777
lberki4741aa82018-02-05 07:59:13 -0800778 return new InterruptSignalHandler() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100779 @Override
Janak Ramakrishnan7be337f2017-01-05 20:21:35 +0000780 public void run() {
lberki97abb522017-09-04 18:51:57 +0200781 logger.info("User interrupt");
Dan Fabulich1d35ca02018-07-05 16:08:06 -0700782 OutErr.SYSTEM_OUT_ERR.printErrLn("Bazel received an interrupt");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100783 mainThread.interrupt();
784
785 int curNumInterrupts = numInterrupts.incrementAndGet();
786 if (curNumInterrupts == 1) {
787 Thread interruptWatcherThread = new Thread(interruptWatcher, "interrupt-watcher");
788 interruptWatcherThread.setDaemon(true);
789 interruptWatcherThread.start();
790 } else if (curNumInterrupts == 2) {
lberki97abb522017-09-04 18:51:57 +0200791 logger.warning("Second --batch interrupt: Reverting to JVM SIGINT handler");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100792 uninstall();
793 }
794 }
795 };
796 }
797
798 /**
799 * A main method that runs blaze commands in batch mode. The return value indicates the desired
800 * exit status of the program.
801 */
802 private static int batchMain(Iterable<BlazeModule> modules, String[] args) {
lberki4741aa82018-02-05 07:59:13 -0800803 InterruptSignalHandler signalHandler = captureSigint();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100804 CommandLineOptions commandLineOptions = splitStartupOptions(modules, args);
lberki97abb522017-09-04 18:51:57 +0200805 logger.info(
Dan Fabulich1d35ca02018-07-05 16:08:06 -0700806 "Running Bazel in batch mode with "
janakr1cb9a012017-03-28 20:18:21 +0000807 + maybeGetPidString()
808 + "startup args "
809 + commandLineOptions.getStartupArgs());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100810
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100811 BlazeRuntime runtime;
ccalvarin0ddda782017-04-06 21:12:11 +0000812 InvocationPolicy policy;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100813 try {
Lukacs Berki9f603952017-01-13 14:04:08 +0000814 runtime = newRuntime(modules, commandLineOptions.getStartupArgs(), null);
ccalvarin0ddda782017-04-06 21:12:11 +0000815 policy = InvocationPolicyParser.parsePolicy(
816 runtime.getStartupOptionsProvider().getOptions(BlazeServerStartupOptions.class)
817 .invocationPolicy);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100818 } catch (OptionsParsingException e) {
ccalvarin0ddda782017-04-06 21:12:11 +0000819 OutErr.SYSTEM_OUT_ERR.printErrLn(e.getMessage());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100820 return ExitCode.COMMAND_LINE_ERROR.getNumericExitCode();
821 } catch (AbruptExitException e) {
ccalvarin0ddda782017-04-06 21:12:11 +0000822 OutErr.SYSTEM_OUT_ERR.printErrLn(e.getMessage());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100823 return e.getExitCode().getNumericExitCode();
824 }
825
ccalvarin1cbe62a2017-08-14 21:09:07 +0200826 ImmutableList.Builder<Pair<String, String>> startupOptionsFromCommandLine =
827 ImmutableList.builder();
828 for (String option : commandLineOptions.getStartupArgs()) {
829 startupOptionsFromCommandLine.add(new Pair<>("", option));
830 }
831
Ulf Adams47cb9162015-09-18 08:12:30 +0000832 BlazeCommandDispatcher dispatcher = new BlazeCommandDispatcher(runtime);
lberki4741aa82018-02-05 07:59:13 -0800833 boolean shutdownDone = false;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100834
835 try {
lberki97abb522017-09-04 18:51:57 +0200836 logger.info(getRequestLogString(commandLineOptions.getOtherArgs()));
lberki4741aa82018-02-05 07:59:13 -0800837 BlazeCommandResult result = dispatcher.exec(
ccalvarin1cbe62a2017-08-14 21:09:07 +0200838 policy,
839 commandLineOptions.getOtherArgs(),
840 OutErr.SYSTEM_OUT_ERR,
841 LockingMode.ERROR_OUT,
842 "batch client",
843 runtime.getClock().currentTimeMillis(),
844 Optional.of(startupOptionsFromCommandLine.build()));
lberki4741aa82018-02-05 07:59:13 -0800845 if (result.getExecRequest() == null) {
846 // Simple case: we are given an exit code
847 return result.getExitCode().getNumericExitCode();
848 }
849
850 // Not so simple case: we need to execute a binary on shutdown. exec() is not accessible from
851 // Java and is impossible on Windows in any case, so we just execute the binary after getting
852 // out of the way as completely as possible and forward its exit code.
853 // When this code is executed, no locks are held: the client lock is released by the client
854 // before it executes any command and the server lock is handled by BlazeCommandDispatcher,
855 // whose job is done by the time we get here.
856 runtime.shutdown();
857 dispatcher.shutdown();
858 shutdownDone = true;
859 signalHandler.uninstall();
860 ExecRequest request = result.getExecRequest();
861 String[] argv = new String[request.getArgvCount()];
862 for (int i = 0; i < argv.length; i++) {
863 argv[i] = request.getArgv(i).toString(StandardCharsets.ISO_8859_1);
864 }
865
866 String workingDirectory = request.getWorkingDirectory().toString(StandardCharsets.ISO_8859_1);
867 try {
868 ProcessBuilder process = new ProcessBuilder()
869 .command(argv)
870 .directory(new File(workingDirectory))
871 .inheritIO();
872
873 for (int i = 0; i < request.getEnvironmentVariableCount(); i++) {
874 EnvironmentVariable variable = request.getEnvironmentVariable(i);
875 process.environment().put(variable.getName().toString(StandardCharsets.ISO_8859_1),
876 variable.getValue().toString(StandardCharsets.ISO_8859_1));
877 }
878
879 return process.start().waitFor();
880 } catch (IOException e) {
881 // We are in batch mode, thus, stdout/stderr are the same as that of the client.
882 System.err.println("Cannot execute process for 'run' command: " + e.getMessage());
883 logger.log(Level.SEVERE, "Exception while executing binary from 'run' command", e);
884 return ExitCode.LOCAL_ENVIRONMENTAL_ERROR.getNumericExitCode();
885 }
Lukacs Berkice1445f2016-04-19 15:52:55 +0000886 } catch (InterruptedException e) {
887 // This is almost main(), so it's okay to just swallow it. We are exiting soon.
888 return ExitCode.INTERRUPTED.getNumericExitCode();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100889 } finally {
lberki4741aa82018-02-05 07:59:13 -0800890 if (!shutdownDone) {
891 runtime.shutdown();
892 dispatcher.shutdown();
893 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100894 }
895 }
896
897 /**
898 * A main method that does not send email. The return value indicates the desired exit status of
899 * the program.
900 */
901 private static int serverMain(Iterable<BlazeModule> modules, OutErr outErr, String[] args) {
Lukacs Berkif7633792016-04-21 12:38:29 +0000902 InterruptSignalHandler sigintHandler = null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100903 try {
lberkiac8eb422018-02-06 09:27:30 -0800904 final RPCServer[] rpcServer = new RPCServer[1];
905 Runnable prepareForAbruptShutdown = () -> rpcServer[0].prepareForAbruptShutdown();
906 BlazeRuntime runtime = newRuntime(modules, Arrays.asList(args), prepareForAbruptShutdown);
907 BlazeCommandDispatcher dispatcher = new BlazeCommandDispatcher(runtime);
lberkiac8eb422018-02-06 09:27:30 -0800908 BlazeServerStartupOptions startupOptions =
909 runtime.getStartupOptionsProvider().getOptions(BlazeServerStartupOptions.class);
910 try {
911 // This is necessary so that Bazel kind of works during bootstrapping, at which time the
912 // gRPC server is not compiled in so that we don't need gRPC for bootstrapping.
913 Class<?> factoryClass = Class.forName(
914 "com.google.devtools.build.lib.server.GrpcServerImpl$Factory");
915 RPCServer.Factory factory = (RPCServer.Factory) factoryClass.getConstructor().newInstance();
lberkibbcf2032018-02-06 10:11:56 -0800916 rpcServer[0] = factory.create(dispatcher, runtime.getClock(),
lberkiac8eb422018-02-06 09:27:30 -0800917 startupOptions.commandPort,
918 runtime.getWorkspace().getWorkspace(),
919 runtime.getServerDirectory(),
920 startupOptions.maxIdleSeconds);
921 } catch (ReflectiveOperationException | IllegalArgumentException e) {
922 throw new AbruptExitException("gRPC server not compiled in", ExitCode.BLAZE_INTERNAL_ERROR);
923 }
Lukacs Berkif7633792016-04-21 12:38:29 +0000924
925 // Register the signal handler.
Janak Ramakrishnan7be337f2017-01-05 20:21:35 +0000926 sigintHandler =
927 new InterruptSignalHandler() {
928 @Override
929 public void run() {
lberki97abb522017-09-04 18:51:57 +0200930 logger.severe("User interrupt");
lberkiac8eb422018-02-06 09:27:30 -0800931 rpcServer[0].interrupt();
Janak Ramakrishnan7be337f2017-01-05 20:21:35 +0000932 }
933 };
Lukacs Berkif7633792016-04-21 12:38:29 +0000934
lberkiac8eb422018-02-06 09:27:30 -0800935 rpcServer[0].serve();
936 runtime.shutdown();
937 dispatcher.shutdown();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100938 return ExitCode.SUCCESS.getNumericExitCode();
939 } catch (OptionsParsingException e) {
940 outErr.printErr(e.getMessage());
941 return ExitCode.COMMAND_LINE_ERROR.getNumericExitCode();
942 } catch (IOException e) {
943 outErr.printErr("I/O Error: " + e.getMessage());
944 return ExitCode.BUILD_FAILURE.getNumericExitCode();
945 } catch (AbruptExitException e) {
946 outErr.printErr(e.getMessage());
947 return e.getExitCode().getNumericExitCode();
Lukacs Berkif7633792016-04-21 12:38:29 +0000948 } finally {
949 if (sigintHandler != null) {
950 sigintHandler.uninstall();
951 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100952 }
953 }
954
ccalvarinfb3293c2017-09-26 11:13:33 -0400955 private static FileSystem defaultFileSystemImplementation() {
Lukacs Berki28219722016-06-30 12:34:39 +0000956 if ("0".equals(System.getProperty("io.bazel.EnableJni"))) {
Damien Martin-Guillerez8c04e9e2016-01-18 12:53:39 +0000957 // Ignore UnixFileSystem, to be used for bootstrapping.
Dmitry Lomovbe939512016-02-02 20:26:25 +0000958 return OS.getCurrent() == OS.WINDOWS ? new WindowsFileSystem() : new JavaIoFileSystem();
Damien Martin-Guillerez8c04e9e2016-01-18 12:53:39 +0000959 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100960 // The JNI-based UnixFileSystem is faster, but on Windows it is not available.
Lukacs Berki58dbb7f2016-01-29 11:56:29 +0000961 return OS.getCurrent() == OS.WINDOWS ? new WindowsFileSystem() : new UnixFileSystem();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100962 }
963
ulfjack27758e42017-09-07 13:41:33 +0200964 private static SubprocessFactory subprocessFactoryImplementation() {
Lukacs Berki74dcfee2016-07-04 12:57:25 +0000965 if (!"0".equals(System.getProperty("io.bazel.EnableJni")) && OS.getCurrent() == OS.WINDOWS) {
966 return WindowsSubprocessFactory.INSTANCE;
967 } else {
968 return JavaSubprocessFactory.INSTANCE;
969 }
970 }
971
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100972 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100973 * Parses the command line arguments into a {@link OptionsParser} object.
974 *
ccalvarin7a491b72018-05-16 09:13:27 -0700975 * <p>This function needs to parse the --option_sources option manually so that the real option
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100976 * parser can set the source for every option correctly. If that cannot be parsed or is missing,
977 * we just report an unknown source for every startup option.
978 */
ccalvarin7a491b72018-05-16 09:13:27 -0700979 private static OptionsProvider parseStartupOptions(
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100980 Iterable<BlazeModule> modules, List<String> args) throws OptionsParsingException {
Ulf Adams7e71f892016-08-10 09:06:50 +0000981 ImmutableList<Class<? extends OptionsBase>> optionClasses =
982 BlazeCommandUtils.getStartupOptions(modules);
983
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100984 // First parse the command line so that we get the option_sources argument
985 OptionsParser parser = OptionsParser.newOptionsParser(optionClasses);
986 parser.setAllowResidue(false);
ccalvarin7cd9e882017-10-16 22:18:32 +0200987 parser.parse(PriorityCategory.COMMAND_LINE, null, args);
lberki6a8a1742017-06-29 10:09:42 +0200988 Map<String, String> optionSources =
989 parser.getOptions(BlazeServerStartupOptions.class).optionSources;
ccalvarin1dce0972017-09-11 20:03:02 +0200990 Function<OptionDefinition, String> sourceFunction =
991 option ->
992 !optionSources.containsKey(option.getOptionName())
993 ? "default"
994 : optionSources.get(option.getOptionName()).isEmpty()
995 ? "command line"
996 : optionSources.get(option.getOptionName());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100997
998 // Then parse the command line again, this time with the correct option sources
999 parser = OptionsParser.newOptionsParser(optionClasses);
1000 parser.setAllowResidue(false);
ccalvarin7cd9e882017-10-16 22:18:32 +02001001 parser.parseWithSourceFunction(PriorityCategory.COMMAND_LINE, sourceFunction, args);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001002 return parser;
1003 }
1004
1005 /**
1006 * Creates a new blaze runtime, given the install and output base directories.
1007 *
1008 * <p>Note: This method can and should only be called once per startup, as it also creates the
1009 * filesystem object that will be used for the runtime. So it should only ever be called from the
1010 * main method of the Blaze program.
1011 *
Ulf Adams7db73d32016-08-10 12:41:02 +00001012 * @param args Blaze startup options.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001013 *
1014 * @return a new BlazeRuntime instance initialized with the given filesystem and directories, and
1015 * an error string that, if not null, describes a fatal initialization failure that makes
1016 * this runtime unsuitable for real commands
1017 */
Lukacs Berki9f603952017-01-13 14:04:08 +00001018 private static BlazeRuntime newRuntime(Iterable<BlazeModule> blazeModules, List<String> args,
1019 Runnable abruptShutdownHandler)
Ulf Adams7db73d32016-08-10 12:41:02 +00001020 throws AbruptExitException, OptionsParsingException {
ccalvarin7a491b72018-05-16 09:13:27 -07001021 OptionsProvider options = parseStartupOptions(blazeModules, args);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001022 for (BlazeModule module : blazeModules) {
1023 module.globalInit(options);
1024 }
1025
1026 BlazeServerStartupOptions startupOptions = options.getOptions(BlazeServerStartupOptions.class);
Ulf Adamsb5a06f332016-06-21 14:55:49 +00001027 String productName = startupOptions.productName.toLowerCase(Locale.US);
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +00001028
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001029 PathFragment workspaceDirectory = startupOptions.workspaceDirectory;
cushon849df362018-05-14 01:51:45 -07001030 PathFragment defaultSystemJavabase = startupOptions.defaultSystemJavabase;
Klaus Aehligc2499c42018-02-27 05:47:21 -08001031 PathFragment outputUserRoot = startupOptions.outputUserRoot;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001032 PathFragment installBase = startupOptions.installBase;
1033 PathFragment outputBase = startupOptions.outputBase;
1034
janakr1cb9a012017-03-28 20:18:21 +00001035 maybeForceJNIByGettingPid(installBase); // Must be before first use of JNI.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001036
Klaus Aehligc2499c42018-02-27 05:47:21 -08001037 // From the point of view of the Java program --install_base, --output_base, and
1038 // --output_user_root are mandatory options, despite the comment in their declarations.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001039 if (installBase == null || !installBase.isAbsolute()) { // (includes "" default case)
1040 throw new IllegalArgumentException(
1041 "Bad --install_base option specified: '" + installBase + "'");
1042 }
Klaus Aehligc2499c42018-02-27 05:47:21 -08001043 if (outputUserRoot != null && !outputUserRoot.isAbsolute()) { // (includes "" default case)
1044 throw new IllegalArgumentException(
1045 "Bad --output_user_root option specified: '" + outputUserRoot + "'");
1046 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001047 if (outputBase != null && !outputBase.isAbsolute()) { // (includes "" default case)
1048 throw new IllegalArgumentException(
1049 "Bad --output_base option specified: '" + outputBase + "'");
1050 }
1051
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001052 FileSystem fs = null;
1053 for (BlazeModule module : blazeModules) {
Ulf Adams124bd0a2016-08-10 13:10:46 +00001054 FileSystem moduleFs = module.getFileSystem(options);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001055 if (moduleFs != null) {
1056 Preconditions.checkState(fs == null, "more than one module returns a file system");
1057 fs = moduleFs;
1058 }
1059 }
1060
1061 if (fs == null) {
ccalvarinfb3293c2017-09-26 11:13:33 -04001062 fs = defaultFileSystemImplementation();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001063 }
Lukacs Berki74dcfee2016-07-04 12:57:25 +00001064
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001065 Path.setFileSystemForSerialization(fs);
Lukacs Berki74dcfee2016-07-04 12:57:25 +00001066 SubprocessBuilder.setSubprocessFactory(subprocessFactoryImplementation());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001067
Klaus Aehligc2499c42018-02-27 05:47:21 -08001068 Path outputUserRootPath = fs.getPath(outputUserRoot);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001069 Path installBasePath = fs.getPath(installBase);
1070 Path outputBasePath = fs.getPath(outputBase);
1071 Path workspaceDirectoryPath = null;
1072 if (!workspaceDirectory.equals(PathFragment.EMPTY_FRAGMENT)) {
1073 workspaceDirectoryPath = fs.getPath(workspaceDirectory);
1074 }
cushon849df362018-05-14 01:51:45 -07001075 Path defaultSystemJavabasePath = null;
1076 if (!defaultSystemJavabase.equals(PathFragment.EMPTY_FRAGMENT)) {
1077 defaultSystemJavabasePath = fs.getPath(defaultSystemJavabase);
1078 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001079
Ulf Adams15a23b92016-08-09 11:46:00 +00001080 ServerDirectories serverDirectories =
Klaus Aehligc2499c42018-02-27 05:47:21 -08001081 new ServerDirectories(
1082 installBasePath, outputBasePath, outputUserRootPath, startupOptions.installMD5);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001083 Clock clock = BlazeClock.instance();
tomluf903eb52017-10-27 12:12:11 -04001084 BlazeRuntime.Builder runtimeBuilder =
1085 new BlazeRuntime.Builder()
1086 .setProductName(productName)
1087 .setFileSystem(fs)
1088 .setServerDirectories(serverDirectories)
tomlu3d1a1942017-11-29 14:01:21 -08001089 .setActionKeyContext(new ActionKeyContext())
tomluf903eb52017-10-27 12:12:11 -04001090 .setStartupOptionsProvider(options)
1091 .setClock(clock)
1092 .setAbruptShutdownHandler(abruptShutdownHandler)
1093 // TODO(bazel-team): Make BugReportingExceptionHandler the default.
1094 // See bug "Make exceptions in EventBus subscribers fatal"
1095 .setEventBusExceptionHandler(
1096 startupOptions.fatalEventBusExceptions
1097 || !BlazeVersionInfo.instance().isReleasedBlaze()
1098 ? new BlazeRuntime.BugReportingExceptionHandler()
1099 : new BlazeRuntime.RemoteExceptionHandler());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001100
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +00001101 if (System.getenv("TEST_TMPDIR") != null
1102 && System.getenv("NO_CRASH_ON_LOGGING_IN_TEST") == null) {
1103 LoggingUtil.installRemoteLogger(getTestCrashLogger());
1104 }
1105
ulfjack9274cba2017-08-11 23:19:48 +02001106 // This module needs to be registered before any module providing a SpawnCache implementation.
1107 runtimeBuilder.addBlazeModule(new NoSpawnCacheModule());
ulfjack236635a2017-06-26 09:25:52 +02001108 runtimeBuilder.addBlazeModule(new CommandLogModule());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001109 for (BlazeModule blazeModule : blazeModules) {
1110 runtimeBuilder.addBlazeModule(blazeModule);
1111 }
1112
1113 BlazeRuntime runtime = runtimeBuilder.build();
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001114
1115 BlazeDirectories directories =
cushon849df362018-05-14 01:51:45 -07001116 new BlazeDirectories(
1117 serverDirectories, workspaceDirectoryPath, defaultSystemJavabasePath, productName);
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001118 BinTools binTools;
1119 try {
1120 binTools = BinTools.forProduction(directories);
1121 } catch (IOException e) {
1122 throw new AbruptExitException(
1123 "Cannot enumerate embedded binaries: " + e.getMessage(),
1124 ExitCode.LOCAL_ENVIRONMENTAL_ERROR);
1125 }
1126 runtime.initWorkspace(directories, binTools);
janakr5bfe3432018-04-20 14:45:13 -07001127 CustomExitCodePublisher.setAbruptExitStatusFileDir(
1128 serverDirectories.getOutputBase().getPathString());
ccalvarin1419dda2017-03-28 21:12:28 +00001129
brandjon73f1a0a2018-03-06 14:19:47 -08001130 // Most static initializers for @SkylarkSignature-containing classes have already run by this
1131 // point, but this will pick up the stragglers.
1132 initSkylarkBuiltinsRegistry();
1133
Nathan Harmatad4803012015-09-08 20:03:22 +00001134 AutoProfiler.setClock(runtime.getClock());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001135 BugReport.setRuntime(runtime);
1136 return runtime;
1137 }
1138
brandjon73f1a0a2018-03-06 14:19:47 -08001139 /**
1140 * Configures the Skylark builtins registry.
1141 *
1142 * <p>Any class containing {@link SkylarkSignature}-annotated fields should call
1143 * {@link SkylarkSignatureProcessor#configureSkylarkFunctions} on itself. This serves two
1144 * purposes: 1) it initializes those fields for use, and 2) it registers them with the Skylark
1145 * builtins registry object
1146 * ({@link com.google.devtools.build.lib.syntax.Runtime#getBuiltinRegistry}). Unfortunately
1147 * there's some technical debt here: The registry object is static and the registration occurs
1148 * inside static initializer blocks.
1149 *
1150 * <p>The registry supports concurrent read/write access, but read access is not actually
1151 * efficient (lockless) until write access is disallowed by calling its
1152 * {@link com.google.devtools.build.lib.syntax.Runtime.BuiltinRegistry#freeze freeze} method.
1153 * We want to freeze before the build begins, but not before all classes have had a chance to run
1154 * their static initializers.
1155 *
1156 * <p>Therefore, this method first ensures that the initializers have run, and then explicitly
1157 * freezes the registry. It ensures initialization by calling a no-op static method on the class.
1158 * Only classes whose initializers have been observed to cause {@code BuiltinRegistry} to throw an
1159 * exception need to be included here, since that indicates that their initialization did not
1160 * happen by this point in time.
1161 *
1162 * <p>Unit tests don't need to worry about registry freeze exceptions, since the registry isn't
1163 * frozen at all for them. They just pay the cost of extra synchronization on every access.
1164 */
1165 private static void initSkylarkBuiltinsRegistry() {
cparsons2c814672018-03-22 09:07:25 -07001166 // Currently no classes need to be initialized here. The hook's still here because it's
1167 // possible it may be needed again in the future.
brandjon73f1a0a2018-03-06 14:19:47 -08001168 com.google.devtools.build.lib.syntax.Runtime.getBuiltinRegistry().freeze();
1169 }
1170
janakr1cb9a012017-03-28 20:18:21 +00001171 private static String maybeGetPidString() {
1172 Integer pid = maybeForceJNIByGettingPid(null);
1173 return pid == null ? "" : "pid " + pid + " and ";
1174 }
1175
1176 /** Loads JNI libraries, if necessary under the current platform. */
1177 @Nullable
1178 private static Integer maybeForceJNIByGettingPid(@Nullable PathFragment installBase) {
1179 return jniLibsAvailable() ? getPidUsingJNI(installBase) : null;
Ulf Adams8afbd3c2017-02-28 10:42:48 +00001180 }
1181
1182 private static boolean jniLibsAvailable() {
1183 return !"0".equals(System.getProperty("io.bazel.EnableJni"));
1184 }
1185
1186 // Force JNI linking at a moment when we have 'installBase' handy, and print
1187 // an informative error if it fails.
janakr1cb9a012017-03-28 20:18:21 +00001188 private static int getPidUsingJNI(@Nullable PathFragment installBase) {
Ulf Adams8afbd3c2017-02-28 10:42:48 +00001189 try {
janakr1cb9a012017-03-28 20:18:21 +00001190 return ProcessUtils.getpid(); // force JNI initialization
Ulf Adams8afbd3c2017-02-28 10:42:48 +00001191 } catch (UnsatisfiedLinkError t) {
janakr1cb9a012017-03-28 20:18:21 +00001192 System.err.println(
1193 "JNI initialization failed: "
1194 + t.getMessage()
1195 + ". "
1196 + "Possibly your installation has been corrupted"
1197 + (installBase == null
1198 ? ""
1199 : "; if this problem persists, try 'rm -fr " + installBase + "'")
1200 + ".");
Ulf Adams8afbd3c2017-02-28 10:42:48 +00001201 throw t;
1202 }
1203 }
1204
1205 /**
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +00001206 * Returns a logger that crashes as soon as it's written to, since tests should not cause events
1207 * that would be logged.
1208 */
1209 @VisibleForTesting
1210 public static Future<Logger> getTestCrashLogger() {
1211 Logger crashLogger = Logger.getAnonymousLogger();
1212 crashLogger.addHandler(
1213 new Handler() {
1214 @Override
1215 public void publish(LogRecord record) {
Michajlo Matijkiwf5cc0b22016-01-20 17:44:44 +00001216 System.err.println("Remote logging disabled for testing, forcing abrupt shutdown.");
1217 System.err.printf("%s#%s: %s\n",
1218 record.getSourceClassName(),
1219 record.getSourceMethodName(),
1220 record.getMessage());
1221
Janak Ramakrishnana88e65b2015-09-17 15:07:26 +00001222 Throwable e = record.getThrown();
Michajlo Matijkiwf5cc0b22016-01-20 17:44:44 +00001223 if (e != null) {
1224 e.printStackTrace();
Janak Ramakrishnana88e65b2015-09-17 15:07:26 +00001225 }
Michajlo Matijkiwf5cc0b22016-01-20 17:44:44 +00001226
1227 Runtime.getRuntime().halt(ExitCode.BLAZE_INTERNAL_ERROR.getNumericExitCode());
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +00001228 }
1229
1230 @Override
1231 public void flush() {
1232 throw new IllegalStateException();
1233 }
1234
1235 @Override
1236 public void close() {
1237 throw new IllegalStateException();
1238 }
1239 });
1240 return Futures.immediateFuture(crashLogger);
1241 }
1242
1243 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001244 * Make sure async threads cannot be orphaned. This method makes sure bugs are reported to
1245 * telemetry and the proper exit code is reported.
1246 */
1247 private static void setupUncaughtHandler(final String[] args) {
Janak Ramakrishnanb6582fa2016-03-14 16:19:40 +00001248 Thread.setDefaultUncaughtExceptionHandler(
laurentlb3d2a68c2017-06-30 00:32:04 +02001249 (thread, throwable) -> BugReport.handleCrash(throwable, args));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001250 }
1251
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +00001252 public String getProductName() {
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +00001253 return productName;
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +00001254 }
1255
buchgr35ff63a2018-07-09 05:55:40 -07001256 public BuildEventArtifactUploaderFactoryMap getBuildEventArtifactUploaderFactoryMap() {
1257 return buildEventArtifactUploaderFactoryMap;
Klaus Aehligdadde6f2017-01-18 17:32:07 +00001258 }
1259
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001260 /**
1261 * A builder for {@link BlazeRuntime} objects. The only required fields are the {@link
1262 * BlazeDirectories}, and the {@link RuleClassProvider} (except for testing). All other fields
1263 * have safe default values.
1264 *
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001265 * <p>The default behavior of the BlazeRuntime's EventBus is to exit when a subscriber throws
1266 * an exception. Please plan appropriately.
1267 */
1268 public static class Builder {
tomluf903eb52017-10-27 12:12:11 -04001269 private FileSystem fileSystem;
Ulf Adams15a23b92016-08-09 11:46:00 +00001270 private ServerDirectories serverDirectories;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001271 private Clock clock;
Lukacs Berki9f603952017-01-13 14:04:08 +00001272 private Runnable abruptShutdownHandler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001273 private OptionsProvider startupOptionsProvider;
Ulf Adams47cb9162015-09-18 08:12:30 +00001274 private final List<BlazeModule> blazeModules = new ArrayList<>();
Ulf Adams8b56c8c2016-04-12 13:45:56 +00001275 private SubscriberExceptionHandler eventBusExceptionHandler = new RemoteExceptionHandler();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001276 private UUID instanceId;
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +00001277 private String productName;
tomlu3d1a1942017-11-29 14:01:21 -08001278 private ActionKeyContext actionKeyContext;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001279
1280 public BlazeRuntime build() throws AbruptExitException {
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +00001281 Preconditions.checkNotNull(productName);
Ulf Adams15a23b92016-08-09 11:46:00 +00001282 Preconditions.checkNotNull(serverDirectories);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001283 Preconditions.checkNotNull(startupOptionsProvider);
tomlu3d1a1942017-11-29 14:01:21 -08001284 ActionKeyContext actionKeyContext =
1285 this.actionKeyContext != null ? this.actionKeyContext : new ActionKeyContext();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001286 Clock clock = (this.clock == null) ? BlazeClock.instance() : this.clock;
1287 UUID instanceId = (this.instanceId == null) ? UUID.randomUUID() : this.instanceId;
1288
1289 Preconditions.checkNotNull(clock);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001290
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001291 for (BlazeModule module : blazeModules) {
tomluf903eb52017-10-27 12:12:11 -04001292 module.blazeStartup(
1293 startupOptionsProvider,
1294 BlazeVersionInfo.instance(),
1295 instanceId,
1296 fileSystem,
1297 serverDirectories,
1298 clock);
Ulf Adams8b56c8c2016-04-12 13:45:56 +00001299 }
Ulf Adams345e15e2016-07-07 13:27:28 +00001300 ServerBuilder serverBuilder = new ServerBuilder();
Ulf Adams54eeac52016-08-29 09:07:35 +00001301 serverBuilder.addQueryOutputFormatters(OutputFormatter.getDefaultFormatters());
Ulf Adams8b56c8c2016-04-12 13:45:56 +00001302 for (BlazeModule module : blazeModules) {
Ulf Adams345e15e2016-07-07 13:27:28 +00001303 module.serverInit(startupOptionsProvider, serverBuilder);
Nathan Harmataf0cc5b82016-03-18 14:56:28 +00001304 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001305
1306 ConfiguredRuleClassProvider.Builder ruleClassBuilder =
1307 new ConfiguredRuleClassProvider.Builder();
1308 for (BlazeModule module : blazeModules) {
1309 module.initializeRuleClasses(ruleClassBuilder);
1310 }
1311
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001312 ConfiguredRuleClassProvider ruleClassProvider = ruleClassBuilder.build();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001313
Nathan Harmata42fb5602016-05-25 20:32:08 +00001314 Package.Builder.Helper packageBuilderHelper = null;
1315 for (BlazeModule module : blazeModules) {
nharmatae57e9a32018-04-02 15:10:24 -07001316 Package.Builder.Helper candidateHelper = module.getPackageBuilderHelper(ruleClassProvider);
Nathan Harmata42fb5602016-05-25 20:32:08 +00001317 if (candidateHelper != null) {
1318 Preconditions.checkState(packageBuilderHelper == null,
1319 "more than one module defines a package builder helper");
1320 packageBuilderHelper = candidateHelper;
1321 }
1322 }
1323 if (packageBuilderHelper == null) {
1324 packageBuilderHelper = Package.Builder.DefaultHelper.INSTANCE;
1325 }
1326
Janak Ramakrishnan02498412016-05-11 22:28:43 +00001327 PackageFactory packageFactory =
1328 new PackageFactory(
1329 ruleClassProvider,
Ulf Adams345e15e2016-07-07 13:27:28 +00001330 serverBuilder.getAttributeContainerFactory(),
Ulf Adamsed5be452016-11-02 16:33:57 +00001331 serverBuilder.getEnvironmentExtensions(),
Nathan Harmata42fb5602016-05-25 20:32:08 +00001332 BlazeVersionInfo.instance().getVersion(),
1333 packageBuilderHelper);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001334
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001335 ProjectFile.Provider projectFileProvider = null;
1336 for (BlazeModule module : blazeModules) {
1337 ProjectFile.Provider candidate = module.createProjectFileProvider();
1338 if (candidate != null) {
1339 Preconditions.checkState(projectFileProvider == null,
1340 "more than one module defines a project file provider");
1341 projectFileProvider = candidate;
1342 }
1343 }
1344
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001345 return new BlazeRuntime(
tomluf903eb52017-10-27 12:12:11 -04001346 fileSystem,
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001347 serverBuilder.getQueryEnvironmentFactory(),
Ulf Adams6bfd2c52016-08-29 08:00:39 +00001348 serverBuilder.getQueryFunctions(),
Ulf Adams54eeac52016-08-29 09:07:35 +00001349 serverBuilder.getQueryOutputFormatters(),
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001350 packageFactory,
1351 ruleClassProvider,
gregced79b9372017-08-10 21:51:15 +02001352 ruleClassProvider.getConfigurationFragments(),
Ulf Adams6bfd2c52016-08-29 08:00:39 +00001353 serverBuilder.getInfoItems(),
tomlu3d1a1942017-11-29 14:01:21 -08001354 actionKeyContext,
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001355 clock,
Lukacs Berki9f603952017-01-13 14:04:08 +00001356 abruptShutdownHandler,
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001357 startupOptionsProvider,
1358 ImmutableList.copyOf(blazeModules),
1359 eventBusExceptionHandler,
1360 projectFileProvider,
1361 serverBuilder.getInvocationPolicy(),
1362 serverBuilder.getCommands(),
Klaus Aehligdadde6f2017-01-18 17:32:07 +00001363 productName,
ulfjack68aa4102018-06-15 01:40:02 -07001364 serverBuilder.getBuildEventArtifactUploaderMap());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001365 }
1366
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +00001367 public Builder setProductName(String productName) {
1368 this.productName = productName;
1369 return this;
1370 }
1371
tomluf903eb52017-10-27 12:12:11 -04001372 public Builder setFileSystem(FileSystem fileSystem) {
1373 this.fileSystem = fileSystem;
1374 return this;
1375 }
1376
Ulf Adams15a23b92016-08-09 11:46:00 +00001377 public Builder setServerDirectories(ServerDirectories serverDirectories) {
1378 this.serverDirectories = serverDirectories;
1379 return this;
1380 }
1381
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001382 public Builder setClock(Clock clock) {
1383 this.clock = clock;
1384 return this;
1385 }
1386
Lukacs Berki9f603952017-01-13 14:04:08 +00001387 public Builder setAbruptShutdownHandler(Runnable handler) {
1388 this.abruptShutdownHandler = handler;
1389 return this;
1390 }
1391
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001392 public Builder setStartupOptionsProvider(OptionsProvider startupOptionsProvider) {
1393 this.startupOptionsProvider = startupOptionsProvider;
1394 return this;
1395 }
1396
1397 public Builder addBlazeModule(BlazeModule blazeModule) {
1398 blazeModules.add(blazeModule);
1399 return this;
1400 }
1401
1402 public Builder setInstanceId(UUID id) {
1403 instanceId = id;
1404 return this;
1405 }
1406
1407 @VisibleForTesting
1408 public Builder setEventBusExceptionHandler(
1409 SubscriberExceptionHandler eventBusExceptionHandler) {
1410 this.eventBusExceptionHandler = eventBusExceptionHandler;
1411 return this;
1412 }
tomlu3d1a1942017-11-29 14:01:21 -08001413
1414 public Builder setActionKeyContext(ActionKeyContext actionKeyContext) {
1415 this.actionKeyContext = actionKeyContext;
1416 return this;
1417 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001418 }
1419}