blob: 1fbf9ed3a6d917cda46e4ae03e2634dcbdef0a57 [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;
Klaus Aehligdadde6f2017-01-18 17:32:07 +000034import com.google.devtools.build.lib.buildeventstream.PathConverter;
ccalvarin28c20f72018-01-22 07:53:56 -080035import com.google.devtools.build.lib.buildtool.BuildRequestOptions;
philwo3bcb9f62017-09-06 12:52:21 +020036import com.google.devtools.build.lib.clock.BlazeClock;
37import com.google.devtools.build.lib.clock.Clock;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010038import com.google.devtools.build.lib.events.Event;
39import 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;
Nathan Harmataf0cc5b82016-03-18 14:56:28 +000050import com.google.devtools.build.lib.query2.AbstractBlazeQueryEnvironment;
51import com.google.devtools.build.lib.query2.QueryEnvironmentFactory;
Ulf Adams6bfd2c52016-08-29 08:00:39 +000052import com.google.devtools.build.lib.query2.engine.QueryEnvironment.QueryFunction;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010053import com.google.devtools.build.lib.query2.output.OutputFormatter;
Lukacs Berkice1445f2016-04-19 15:52:55 +000054import com.google.devtools.build.lib.runtime.BlazeCommandDispatcher.LockingMode;
Ulf Adams6bfd2c52016-08-29 08:00:39 +000055import com.google.devtools.build.lib.runtime.commands.InfoItem;
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +000056import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
lberki4741aa82018-02-05 07:59:13 -080057import com.google.devtools.build.lib.server.CommandProtos.EnvironmentVariable;
58import com.google.devtools.build.lib.server.CommandProtos.ExecRequest;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010059import com.google.devtools.build.lib.server.RPCServer;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010060import com.google.devtools.build.lib.server.signal.InterruptSignalHandler;
Lukacs Berki74dcfee2016-07-04 12:57:25 +000061import com.google.devtools.build.lib.shell.JavaSubprocessFactory;
Lukacs Berki74dcfee2016-07-04 12:57:25 +000062import com.google.devtools.build.lib.shell.SubprocessBuilder;
ulfjack27758e42017-09-07 13:41:33 +020063import com.google.devtools.build.lib.shell.SubprocessFactory;
Ulf Adams8afbd3c2017-02-28 10:42:48 +000064import com.google.devtools.build.lib.unix.UnixFileSystem;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010065import com.google.devtools.build.lib.util.AbruptExitException;
Chloe Calvarineaa3be72016-12-13 19:48:34 +000066import com.google.devtools.build.lib.util.CustomExitCodePublisher;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010067import com.google.devtools.build.lib.util.ExitCode;
68import com.google.devtools.build.lib.util.LoggingUtil;
69import com.google.devtools.build.lib.util.OS;
ccalvarin1cbe62a2017-08-14 21:09:07 +020070import com.google.devtools.build.lib.util.Pair;
Ulf Adams8afbd3c2017-02-28 10:42:48 +000071import com.google.devtools.build.lib.util.ProcessUtils;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010072import com.google.devtools.build.lib.util.ThreadUtils;
73import com.google.devtools.build.lib.util.io.OutErr;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010074import com.google.devtools.build.lib.vfs.FileSystem;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010075import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
76import com.google.devtools.build.lib.vfs.Path;
77import com.google.devtools.build.lib.vfs.PathFragment;
Ulf Adams8afbd3c2017-02-28 10:42:48 +000078import com.google.devtools.build.lib.windows.WindowsFileSystem;
Lukacs Berki74dcfee2016-07-04 12:57:25 +000079import com.google.devtools.build.lib.windows.WindowsSubprocessFactory;
ccalvarinf39dc6f2017-06-09 11:51:45 -040080import com.google.devtools.common.options.CommandNameCache;
81import com.google.devtools.common.options.InvocationPolicyParser;
ccalvarine8aae032017-08-22 07:17:44 +020082import com.google.devtools.common.options.OptionDefinition;
ccalvarin7cd9e882017-10-16 22:18:32 +020083import com.google.devtools.common.options.OptionPriority.PriorityCategory;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010084import com.google.devtools.common.options.OptionsBase;
85import com.google.devtools.common.options.OptionsClassProvider;
86import com.google.devtools.common.options.OptionsParser;
87import com.google.devtools.common.options.OptionsParsingException;
88import com.google.devtools.common.options.OptionsProvider;
89import com.google.devtools.common.options.TriState;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010090import java.io.BufferedOutputStream;
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;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010096import java.util.ArrayList;
97import java.util.Arrays;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010098import java.util.Date;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010099import java.util.Iterator;
Ulf Adams47cb9162015-09-18 08:12:30 +0000100import java.util.LinkedHashMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100101import java.util.List;
Ulf Adamsb5a06f332016-06-21 14:55:49 +0000102import java.util.Locale;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100103import java.util.Map;
ccalvarin1cbe62a2017-08-14 21:09:07 +0200104import java.util.Optional;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100105import java.util.UUID;
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +0000106import java.util.concurrent.Future;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100107import java.util.concurrent.TimeUnit;
108import java.util.concurrent.atomic.AtomicInteger;
ccalvarine8aae032017-08-22 07:17:44 +0200109import java.util.function.Function;
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +0000110import java.util.logging.Handler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100111import java.util.logging.Level;
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +0000112import java.util.logging.LogRecord;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100113import java.util.logging.Logger;
Ulf Adamsfd370042017-06-16 15:52:06 +0200114import java.util.regex.Matcher;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100115import java.util.regex.Pattern;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100116import javax.annotation.Nullable;
117
118/**
Ulf Adams633f5392015-09-15 11:13:08 +0000119 * The BlazeRuntime class encapsulates the immutable configuration of the current instance. These
120 * runtime settings and services are available to most parts of any Blaze application for the
121 * duration of the batch run or server lifetime.
122 *
123 * <p>The parts specific to the current command are stored in {@link CommandEnvironment}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100124 */
125public final class BlazeRuntime {
Michajlo Matijkiwa132f0e2016-11-21 17:30:43 +0000126 private static final Pattern suppressFromLog =
Ulf Adamsfd370042017-06-16 15:52:06 +0200127 Pattern.compile("--client_env=([^=]*(?:auth|pass|cookie)[^=]*)=", Pattern.CASE_INSENSITIVE);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100128
lberki97abb522017-09-04 18:51:57 +0200129 private static final Logger logger = Logger.getLogger(BlazeRuntime.class.getName());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100130
tomluf903eb52017-10-27 12:12:11 -0400131 private final FileSystem fileSystem;
Ulf Adams50e7db62015-10-20 09:14:16 +0000132 private final Iterable<BlazeModule> blazeModules;
133 private final Map<String, BlazeCommand> commandMap = new LinkedHashMap<>();
134 private final Clock clock;
Lukacs Berki9f603952017-01-13 14:04:08 +0000135 private final Runnable abruptShutdownHandler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100136
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100137 private final PackageFactory packageFactory;
gregced79b9372017-08-10 21:51:15 +0200138 private final ImmutableList<ConfigurationFragmentFactory> configurationFragmentFactories;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100139 private final ConfiguredRuleClassProvider ruleClassProvider;
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000140 // For bazel info.
141 private final ImmutableMap<String, InfoItem> infoItems;
142 // For bazel query.
143 private final QueryEnvironmentFactory queryEnvironmentFactory;
144 private final ImmutableList<QueryFunction> queryFunctions;
Ulf Adams54eeac52016-08-29 09:07:35 +0000145 private final ImmutableList<OutputFormatter> queryOutputFormatters;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100146
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100147 private final AtomicInteger storedExitCode = new AtomicInteger();
148
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100149 // We pass this through here to make it available to the MasterLogWriter.
150 private final OptionsProvider startupOptionsProvider;
151
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100152 private final ProjectFile.Provider projectFileProvider;
Chloe Calvarin6b607182017-03-27 15:37:50 +0000153 @Nullable private final InvocationPolicy moduleInvocationPolicy;
Janak Ramakrishnan1d899602016-04-28 03:48:55 +0000154 private final String defaultsPackageContent;
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000155 private final SubscriberExceptionHandler eventBusExceptionHandler;
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +0000156 private final String productName;
Klaus Aehligdadde6f2017-01-18 17:32:07 +0000157 private final PathConverter pathToUriConverter;
tomlu3d1a1942017-11-29 14:01:21 -0800158 private final ActionKeyContext actionKeyContext;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100159
Ulf Adams50e7db62015-10-20 09:14:16 +0000160 // Workspace state (currently exactly one workspace per server)
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000161 private BlazeWorkspace workspace;
Ulf Adams50e7db62015-10-20 09:14:16 +0000162
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000163 private BlazeRuntime(
tomluf903eb52017-10-27 12:12:11 -0400164 FileSystem fileSystem,
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000165 QueryEnvironmentFactory queryEnvironmentFactory,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000166 ImmutableList<QueryFunction> queryFunctions,
Ulf Adams54eeac52016-08-29 09:07:35 +0000167 ImmutableList<OutputFormatter> queryOutputFormatters,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000168 PackageFactory pkgFactory,
169 ConfiguredRuleClassProvider ruleClassProvider,
gregced79b9372017-08-10 21:51:15 +0200170 ImmutableList<ConfigurationFragmentFactory> configurationFragmentFactories,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000171 ImmutableMap<String, InfoItem> infoItems,
tomlu3d1a1942017-11-29 14:01:21 -0800172 ActionKeyContext actionKeyContext,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000173 Clock clock,
Lukacs Berki9f603952017-01-13 14:04:08 +0000174 Runnable abruptShutdownHandler,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000175 OptionsProvider startupOptionsProvider,
176 Iterable<BlazeModule> blazeModules,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100177 SubscriberExceptionHandler eventBusExceptionHandler,
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000178 ProjectFile.Provider projectFileProvider,
Chloe Calvarin6b607182017-03-27 15:37:50 +0000179 InvocationPolicy moduleInvocationPolicy,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000180 Iterable<BlazeCommand> commands,
Klaus Aehligdadde6f2017-01-18 17:32:07 +0000181 String productName,
182 PathConverter pathToUriConverter) {
Ulf Adams50e7db62015-10-20 09:14:16 +0000183 // Server state
tomluf903eb52017-10-27 12:12:11 -0400184 this.fileSystem = fileSystem;
Ulf Adams50e7db62015-10-20 09:14:16 +0000185 this.blazeModules = blazeModules;
186 overrideCommands(commands);
187
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100188 this.packageFactory = pkgFactory;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100189 this.projectFileProvider = projectFileProvider;
Chloe Calvarin6b607182017-03-27 15:37:50 +0000190 this.moduleInvocationPolicy = moduleInvocationPolicy;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100191
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100192 this.ruleClassProvider = ruleClassProvider;
gregced79b9372017-08-10 21:51:15 +0200193 this.configurationFragmentFactories = configurationFragmentFactories;
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000194 this.infoItems = infoItems;
tomlu3d1a1942017-11-29 14:01:21 -0800195 this.actionKeyContext = actionKeyContext;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100196 this.clock = clock;
Lukacs Berki9f603952017-01-13 14:04:08 +0000197 this.abruptShutdownHandler = abruptShutdownHandler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100198 this.startupOptionsProvider = startupOptionsProvider;
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000199 this.queryEnvironmentFactory = queryEnvironmentFactory;
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000200 this.queryFunctions = queryFunctions;
Ulf Adams54eeac52016-08-29 09:07:35 +0000201 this.queryOutputFormatters = queryOutputFormatters;
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000202 this.eventBusExceptionHandler = eventBusExceptionHandler;
Janak Ramakrishnan1d899602016-04-28 03:48:55 +0000203
204 this.defaultsPackageContent =
Chloe Calvarin6b607182017-03-27 15:37:50 +0000205 ruleClassProvider.getDefaultsPackageContent(getModuleInvocationPolicy());
Janak Ramakrishnane9cd0f32016-04-29 22:46:16 +0000206 CommandNameCache.CommandNameCacheInstance.INSTANCE.setCommandNameCache(
207 new CommandNameCacheImpl(getCommandMap()));
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +0000208 this.productName = productName;
Klaus Aehligdadde6f2017-01-18 17:32:07 +0000209 this.pathToUriConverter = pathToUriConverter;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100210 }
211
ulfjack5a3b24c2017-06-08 17:10:06 +0200212 public BlazeWorkspace initWorkspace(BlazeDirectories directories, BinTools binTools)
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000213 throws AbruptExitException {
Ulf Adamsea46dfc2016-08-10 08:44:19 +0000214 Preconditions.checkState(this.workspace == null);
Ulf Adamsde14ade2016-10-14 14:20:31 +0000215 WorkspaceBuilder builder = new WorkspaceBuilder(directories, binTools);
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000216 for (BlazeModule module : blazeModules) {
ulfjack94bee752017-06-13 19:13:35 +0200217 module.workspaceInit(this, directories, builder);
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000218 }
janakreea486c2017-09-22 15:38:50 -0400219 this.workspace =
220 builder.build(this, packageFactory, ruleClassProvider, eventBusExceptionHandler);
ulfjack5a3b24c2017-06-08 17:10:06 +0200221 return workspace;
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000222 }
223
Ulf Adams33644a42016-08-10 11:32:41 +0000224 @Nullable public CoverageReportActionFactory getCoverageReportActionFactory(
225 OptionsClassProvider commandOptions) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100226 CoverageReportActionFactory firstFactory = null;
227 for (BlazeModule module : blazeModules) {
Ulf Adams33644a42016-08-10 11:32:41 +0000228 CoverageReportActionFactory factory = module.getCoverageReportFactory(commandOptions);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100229 if (factory != null) {
230 Preconditions.checkState(firstFactory == null,
231 "only one Blaze Module can have a Coverage Report Factory");
232 firstFactory = factory;
233 }
234 }
235 return firstFactory;
236 }
237
238 /**
Ulf Adams47cb9162015-09-18 08:12:30 +0000239 * Adds the given command under the given name to the map of commands.
240 *
241 * @throws AssertionError if the name is already used by another command.
242 */
243 private void addCommand(BlazeCommand command) {
244 String name = command.getClass().getAnnotation(Command.class).name();
245 if (commandMap.containsKey(name)) {
246 throw new IllegalStateException("Command name or alias " + name + " is already used.");
247 }
248 commandMap.put(name, command);
249 }
250
251 final void overrideCommands(Iterable<BlazeCommand> commands) {
252 commandMap.clear();
253 for (BlazeCommand command : commands) {
254 addCommand(command);
255 }
Ulf Adams47cb9162015-09-18 08:12:30 +0000256 }
257
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +0000258 @Nullable
Chloe Calvarin6b607182017-03-27 15:37:50 +0000259 public InvocationPolicy getModuleInvocationPolicy() {
260 return moduleInvocationPolicy;
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +0000261 }
262
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100263 /**
264 * Conditionally enable profiling.
265 */
Ulf Adams633f5392015-09-15 11:13:08 +0000266 private final boolean initProfiler(CommandEnvironment env, CommonCommandOptions options,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100267 UUID buildID, long execStartTimeNanos) {
268 OutputStream out = null;
269 boolean recordFullProfilerData = false;
270 ProfiledTaskKinds profiledTasks = ProfiledTaskKinds.NONE;
271
272 try {
273 if (options.profilePath != null) {
Ulf Adamsab43b972016-03-30 12:23:50 +0000274 Path profilePath = env.getWorkspace().getRelative(options.profilePath);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100275
276 recordFullProfilerData = options.recordFullProfilerData;
277 out = new BufferedOutputStream(profilePath.getOutputStream(), 1024 * 1024);
Ulf Adams633f5392015-09-15 11:13:08 +0000278 env.getReporter().handle(Event.info("Writing profile data to '" + profilePath + "'"));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100279 profiledTasks = ProfiledTaskKinds.ALL;
280 } else if (options.alwaysProfileSlowOperations) {
281 recordFullProfilerData = false;
282 out = null;
283 profiledTasks = ProfiledTaskKinds.SLOWEST;
284 }
285 if (profiledTasks != ProfiledTaskKinds.NONE) {
286 Profiler.instance().start(profiledTasks, out,
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +0000287 getProductName() + " profile for " + env.getOutputBase() + " at " + new Date()
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100288 + ", build ID: " + buildID,
289 recordFullProfilerData, clock, execStartTimeNanos);
290 return true;
291 }
292 } catch (IOException e) {
Ulf Adams633f5392015-09-15 11:13:08 +0000293 env.getReporter().handle(Event.error("Error while creating profile file: " + e.getMessage()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100294 }
295 return false;
296 }
297
tomluf903eb52017-10-27 12:12:11 -0400298 public FileSystem getFileSystem() {
299 return fileSystem;
300 }
301
Ulf Adamsab43b972016-03-30 12:23:50 +0000302 public BlazeWorkspace getWorkspace() {
303 return workspace;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100304 }
305
tomlu3d1a1942017-11-29 14:01:21 -0800306 public ActionKeyContext getActionKeyContext() {
307 return actionKeyContext;
308 }
309
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100310 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100311 * The directory in which blaze stores the server state - that is, the socket
312 * file and a log.
313 */
Ulf Adams94b72db2016-03-30 11:58:37 +0000314 private Path getServerDirectory() {
Ulf Adamsab43b972016-03-30 12:23:50 +0000315 return getWorkspace().getDirectories().getOutputBase().getChild("server");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100316 }
317
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100318 /**
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000319 * Returns the {@link QueryEnvironmentFactory} that should be used to create a
320 * {@link AbstractBlazeQueryEnvironment}, whenever one is needed.
321 */
322 public QueryEnvironmentFactory getQueryEnvironmentFactory() {
323 return queryEnvironmentFactory;
324 }
325
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000326 public ImmutableList<QueryFunction> getQueryFunctions() {
327 return queryFunctions;
328 }
329
Ulf Adams54eeac52016-08-29 09:07:35 +0000330 public ImmutableList<OutputFormatter> getQueryOutputFormatters() {
331 return queryOutputFormatters;
332 }
333
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000334 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100335 * Returns the package factory.
336 */
337 public PackageFactory getPackageFactory() {
338 return packageFactory;
339 }
340
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100341 /**
342 * Returns the rule class provider.
343 */
344 public ConfiguredRuleClassProvider getRuleClassProvider() {
345 return ruleClassProvider;
346 }
347
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000348 public ImmutableMap<String, InfoItem> getInfoItems() {
349 return infoItems;
350 }
351
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100352 public Iterable<BlazeModule> getBlazeModules() {
353 return blazeModules;
354 }
355
356 @SuppressWarnings("unchecked")
357 public <T extends BlazeModule> T getBlazeModule(Class<T> moduleClass) {
358 for (BlazeModule module : blazeModules) {
359 if (module.getClass() == moduleClass) {
360 return (T) module;
361 }
362 }
363
364 return null;
365 }
366
gregced79b9372017-08-10 21:51:15 +0200367 public ImmutableList<ConfigurationFragmentFactory> getConfigurationFragmentFactories() {
368 return configurationFragmentFactories;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100369 }
370
371 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100372 * Returns a provider for project file objects. Can be null if no such provider was set by any of
373 * the modules.
374 */
375 @Nullable
376 public ProjectFile.Provider getProjectFileProvider() {
377 return projectFileProvider;
378 }
379
380 /**
381 * Hook method called by the BlazeCommandDispatcher prior to the dispatch of
382 * each command.
383 *
384 * @param options The CommonCommandOptions used by every command.
385 * @throws AbruptExitException if this command is unsuitable to be run as specified
386 */
Ulf Adams50e7db62015-10-20 09:14:16 +0000387 void beforeCommand(CommandEnvironment env, CommonCommandOptions options, long execStartTimeNanos)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100388 throws AbruptExitException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100389 // Conditionally enable profiling
390 // We need to compensate for launchTimeNanos (measurements taken outside of the jvm).
391 long startupTimeNanos = options.startupTime * 1000000L;
Ulf Adams3815b4c2015-09-18 07:34:13 +0000392 if (initProfiler(env, options, env.getCommandId(), execStartTimeNanos - startupTimeNanos)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100393 Profiler profiler = Profiler.instance();
394
395 // Instead of logEvent() we're calling the low level function to pass the timings we took in
396 // the launcher. We're setting the INIT phase marker so that it follows immediately the LAUNCH
397 // phase.
398 profiler.logSimpleTaskDuration(execStartTimeNanos - startupTimeNanos, 0, ProfilerTask.PHASE,
399 ProfilePhase.LAUNCH.description);
400 profiler.logSimpleTaskDuration(execStartTimeNanos, 0, ProfilerTask.PHASE,
401 ProfilePhase.INIT.description);
402 }
403
404 if (options.memoryProfilePath != null) {
Ulf Adamsc5855302015-10-20 08:46:38 +0000405 Path memoryProfilePath = env.getWorkingDirectory().getRelative(options.memoryProfilePath);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100406 try {
407 MemoryProfiler.instance().start(memoryProfilePath.getOutputStream());
408 } catch (IOException e) {
Ulf Adams633f5392015-09-15 11:13:08 +0000409 env.getReporter().handle(
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100410 Event.error("Error while creating memory profile file: " + e.getMessage()));
411 }
412 }
413
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100414 // Initialize exit code to dummy value for afterCommand.
415 storedExitCode.set(ExitCode.RESERVED.getNumericExitCode());
416 }
417
418 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100419 * Posts the {@link CommandCompleteEvent}, so that listeners can tidy up. Called by {@link
420 * #afterCommand}, and by BugReport when crashing from an exception in an async thread.
421 */
Janak Ramakrishnan39a55132016-05-23 21:55:20 +0000422 void notifyCommandComplete(int exitCode) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100423 if (!storedExitCode.compareAndSet(ExitCode.RESERVED.getNumericExitCode(), exitCode)) {
424 // This command has already been called, presumably because there is a race between the main
425 // thread and a worker thread that crashed. Don't try to arbitrate the dispute. If the main
426 // thread won the race (unlikely, but possible), this may be incorrectly logged as a success.
427 return;
428 }
Janak Ramakrishnana50b7b72016-06-02 21:46:40 +0000429 workspace.getSkyframeExecutor().getEventBus().post(new CommandCompleteEvent(exitCode));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100430 }
431
ccalvarin28c20f72018-01-22 07:53:56 -0800432 /** Hook method called by the BlazeCommandDispatcher after the dispatch of each command. */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100433 @VisibleForTesting
Ulf Adams633f5392015-09-15 11:13:08 +0000434 public void afterCommand(CommandEnvironment env, int exitCode) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100435 // Remove any filters that the command might have added to the reporter.
Ulf Adams633f5392015-09-15 11:13:08 +0000436 env.getReporter().setOutputFilter(OutputFilter.OUTPUT_EVERYTHING);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100437
438 notifyCommandComplete(exitCode);
439
440 for (BlazeModule module : blazeModules) {
441 module.afterCommand();
442 }
443
ccalvarin28c20f72018-01-22 07:53:56 -0800444 // If the command just completed was or inherits from Build, wipe the dependency graph if
445 // requested. This is sufficient, as this method is always run at the end of commands unless
446 // the server crashes, in which case no inmemory state will linger for the next build anyway.
447 BuildRequestOptions buildRequestOptions =
448 env.getOptions().getOptions(BuildRequestOptions.class);
449 if (buildRequestOptions != null && !buildRequestOptions.keepStateAfterBuild) {
450 workspace.getSkyframeExecutor().resetEvaluator();
451 }
452
Ulf Adamsab43b972016-03-30 12:23:50 +0000453 env.getBlazeWorkspace().clearEventBus();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100454
455 try {
456 Profiler.instance().stop();
457 MemoryProfiler.instance().stop();
458 } catch (IOException e) {
Ulf Adams633f5392015-09-15 11:13:08 +0000459 env.getReporter().handle(Event.error("Error while writing profile file: " + e.getMessage()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100460 }
Klaus Aehlig777b30d2017-02-24 16:30:15 +0000461 env.getReporter().clearEventBus();
tomlu3d1a1942017-11-29 14:01:21 -0800462
463 actionKeyContext.clear();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100464 }
465
466 // Make sure we keep a strong reference to this logger, so that the
467 // configuration isn't lost when the gc kicks in.
468 private static Logger templateLogger = Logger.getLogger("com.google.devtools.build");
469
470 /**
471 * Configures "com.google.devtools.build.*" loggers to the given
472 * {@code level}. Note: This code relies on static state.
473 */
474 public static void setupLogging(Level level) {
475 templateLogger.setLevel(level);
476 templateLogger.info("Log level: " + templateLogger.getLevel());
477 }
478
479 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100480 * Returns the Clock-instance used for the entire build. Before,
481 * individual classes (such as Profiler) used to specify the type
482 * of clock (e.g. EpochClock) they wanted to use. This made it
483 * difficult to get Blaze working on Windows as some of the clocks
484 * available for Linux aren't (directly) available on Windows.
485 * Setting the Blaze-wide clock upon construction of BlazeRuntime
486 * allows injecting whatever Clock instance should be used from
487 * BlazeMain.
488 *
489 * @return The Blaze-wide clock
490 */
491 public Clock getClock() {
492 return clock;
493 }
494
495 public OptionsProvider getStartupOptionsProvider() {
496 return startupOptionsProvider;
497 }
498
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100499 public Map<String, BlazeCommand> getCommandMap() {
500 return commandMap;
501 }
502
Julio Merinod1619b752016-10-17 09:50:11 +0000503 /** Invokes {@link BlazeModule#blazeShutdown()} on all registered modules. */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100504 public void shutdown() {
505 for (BlazeModule module : blazeModules) {
506 module.blazeShutdown();
507 }
508 }
509
Lukacs Berki9f603952017-01-13 14:04:08 +0000510 public void prepareForAbruptShutdown() {
511 if (abruptShutdownHandler != null) {
512 abruptShutdownHandler.run();
513 }
514 }
515
Julio Merinod1619b752016-10-17 09:50:11 +0000516 /** Invokes {@link BlazeModule#blazeShutdownOnCrash()} on all registered modules. */
517 public void shutdownOnCrash() {
518 for (BlazeModule module : blazeModules) {
519 module.blazeShutdownOnCrash();
520 }
521 }
522
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100523 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100524 * Returns the defaults package for the default settings. Should only be called by commands that
525 * do <i>not</i> process {@link BuildOptions}, since build options can alter the contents of the
526 * defaults package, which will not be reflected here.
527 */
528 public String getDefaultsPackageContent() {
Janak Ramakrishnan1d899602016-04-28 03:48:55 +0000529 return defaultsPackageContent;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100530 }
531
532 /**
533 * Returns the defaults package for the given options taken from an optionsProvider.
534 */
535 public String getDefaultsPackageContent(OptionsClassProvider optionsProvider) {
536 return ruleClassProvider.getDefaultsPackageContent(optionsProvider);
537 }
538
539 /**
540 * Creates a BuildOptions class for the given options taken from an optionsProvider.
541 */
542 public BuildOptions createBuildOptions(OptionsClassProvider optionsProvider) {
543 return ruleClassProvider.createBuildOptions(optionsProvider);
544 }
545
546 /**
547 * An EventBus exception handler that will report the exception to a remote server, if a
548 * handler is registered.
549 */
550 public static final class RemoteExceptionHandler implements SubscriberExceptionHandler {
551 @Override
552 public void handleException(Throwable exception, SubscriberExceptionContext context) {
lberki97abb522017-09-04 18:51:57 +0200553 logger.log(Level.SEVERE, "Failure in EventBus subscriber", exception);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100554 LoggingUtil.logToRemote(Level.SEVERE, "Failure in EventBus subscriber.", exception);
555 }
556 }
557
558 /**
559 * An EventBus exception handler that will call BugReport.handleCrash exiting
560 * the current thread.
561 */
562 public static final class BugReportingExceptionHandler implements SubscriberExceptionHandler {
563 @Override
564 public void handleException(Throwable exception, SubscriberExceptionContext context) {
565 BugReport.handleCrash(exception);
566 }
567 }
568
569 /**
570 * Main method for the Blaze server startup. Note: This method logs
571 * exceptions to remote servers. Do not add this to a unittest.
572 */
573 public static void main(Iterable<Class<? extends BlazeModule>> moduleClasses, String[] args) {
574 setupUncaughtHandler(args);
575 List<BlazeModule> modules = createModules(moduleClasses);
Googlerc8c64e72015-03-23 23:22:18 +0000576 // blaze.cc will put --batch first if the user set it.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100577 if (args.length >= 1 && args[0].equals("--batch")) {
578 // Run Blaze in batch mode.
579 System.exit(batchMain(modules, args));
580 }
lberki97abb522017-09-04 18:51:57 +0200581 logger.info(
nharmata6d6a5772017-12-08 12:00:13 -0800582 "Starting Blaze server with "
janakr1cb9a012017-03-28 20:18:21 +0000583 + maybeGetPidString()
nharmata6d6a5772017-12-08 12:00:13 -0800584 + "args "
janakr1cb9a012017-03-28 20:18:21 +0000585 + Arrays.toString(args));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100586 try {
587 // Run Blaze in server mode.
588 System.exit(serverMain(modules, OutErr.SYSTEM_OUT_ERR, args));
589 } catch (RuntimeException | Error e) { // A definite bug...
590 BugReport.printBug(OutErr.SYSTEM_OUT_ERR, e);
591 BugReport.sendBugReport(e, Arrays.asList(args));
592 System.exit(ExitCode.BLAZE_INTERNAL_ERROR.getNumericExitCode());
593 throw e; // Shouldn't get here.
594 }
595 }
596
597 @VisibleForTesting
598 public static List<BlazeModule> createModules(
599 Iterable<Class<? extends BlazeModule>> moduleClasses) {
600 ImmutableList.Builder<BlazeModule> result = ImmutableList.builder();
601 for (Class<? extends BlazeModule> moduleClass : moduleClasses) {
602 try {
Ulf Adams653a16a2016-08-11 12:39:46 +0000603 BlazeModule module = moduleClass.getConstructor().newInstance();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100604 result.add(module);
605 } catch (Throwable e) {
606 throw new IllegalStateException("Cannot instantiate module " + moduleClass.getName(), e);
607 }
608 }
609
610 return result.build();
611 }
612
613 /**
Ulf Adamsfd370042017-06-16 15:52:06 +0200614 * Generates a string form of a request to be written to the logs, filtering the user environment
615 * to remove anything that looks private. The current filter criteria removes any variable whose
616 * name includes "auth", "pass", or "cookie".
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100617 *
618 * @param requestStrings
619 * @return the filtered request to write to the log.
620 */
lberkiac8eb422018-02-06 09:27:30 -0800621 public static String getRequestLogString(List<String> requestStrings) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100622 StringBuilder buf = new StringBuilder();
623 buf.append('[');
624 String sep = "";
Ulf Adamsfd370042017-06-16 15:52:06 +0200625 Matcher m = suppressFromLog.matcher("");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100626 for (String s : requestStrings) {
627 buf.append(sep);
Ulf Adamsfd370042017-06-16 15:52:06 +0200628 m.reset(s);
629 if (m.lookingAt()) {
630 buf.append(m.group());
631 buf.append("__private_value_removed__");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100632 } else {
633 buf.append(s);
634 }
635 sep = ", ";
636 }
637 buf.append(']');
638 return buf.toString();
639 }
640
641 /**
642 * Command line options split in to two parts: startup options and everything else.
643 */
644 @VisibleForTesting
645 static class CommandLineOptions {
646 private final List<String> startupArgs;
647 private final List<String> otherArgs;
648
649 CommandLineOptions(List<String> startupArgs, List<String> otherArgs) {
650 this.startupArgs = ImmutableList.copyOf(startupArgs);
651 this.otherArgs = ImmutableList.copyOf(otherArgs);
652 }
653
654 public List<String> getStartupArgs() {
655 return startupArgs;
656 }
657
658 public List<String> getOtherArgs() {
659 return otherArgs;
660 }
661 }
662
663 /**
Lukacs Berki3d486832016-10-26 12:51:38 +0000664 * Splits given options into two lists - arguments matching options defined in this class and
665 * everything else, while preserving order in each list.
666 *
667 * <p>Note that this method relies on the startup options always being in the
668 * <code>--flag=ARG</code> form (instead of <code>--flag ARG</code>). This is enforced by
669 * <code>GetArgumentArray()</code> in <code>blaze.cc</code> by reconstructing the startup
670 * options from their parsed versions instead of using <code>argv</code> verbatim.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100671 */
672 static CommandLineOptions splitStartupOptions(
673 Iterable<BlazeModule> modules, String... args) {
674 List<String> prefixes = new ArrayList<>();
ccalvarin987f09f2017-08-31 19:50:39 +0200675 List<OptionDefinition> startupOptions = Lists.newArrayList();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100676 for (Class<? extends OptionsBase> defaultOptions
677 : BlazeCommandUtils.getStartupOptions(modules)) {
ccalvarin987f09f2017-08-31 19:50:39 +0200678 startupOptions.addAll(OptionsParser.getOptionDefinitions(defaultOptions));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100679 }
680
ccalvarin987f09f2017-08-31 19:50:39 +0200681 for (OptionDefinition optionDefinition : startupOptions) {
682 Type optionType = optionDefinition.getField().getType();
683 prefixes.add("--" + optionDefinition.getOptionName());
684 if (optionType == boolean.class || optionType == TriState.class) {
685 prefixes.add("--no" + optionDefinition.getOptionName());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100686 }
687 }
688
689 List<String> startupArgs = new ArrayList<>();
690 List<String> otherArgs = Lists.newArrayList(args);
691
692 for (Iterator<String> argi = otherArgs.iterator(); argi.hasNext(); ) {
693 String arg = argi.next();
694 if (!arg.startsWith("--")) {
695 break; // stop at command - all startup options would be specified before it.
696 }
697 for (String prefix : prefixes) {
698 if (arg.startsWith(prefix)) {
699 startupArgs.add(arg);
700 argi.remove();
701 break;
702 }
703 }
704 }
705 return new CommandLineOptions(startupArgs, otherArgs);
706 }
707
lberki4741aa82018-02-05 07:59:13 -0800708 private static InterruptSignalHandler captureSigint() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100709 final Thread mainThread = Thread.currentThread();
710 final AtomicInteger numInterrupts = new AtomicInteger();
711
laurentlb3d2a68c2017-06-30 00:32:04 +0200712 final Runnable interruptWatcher =
713 () -> {
714 int count = 0;
715 // Not an actual infinite loop because it's run in a daemon thread.
716 while (true) {
717 count++;
718 Uninterruptibles.sleepUninterruptibly(10, TimeUnit.SECONDS);
lberki97abb522017-09-04 18:51:57 +0200719 logger.warning("Slow interrupt number " + count + " in batch mode");
laurentlb3d2a68c2017-06-30 00:32:04 +0200720 ThreadUtils.warnAboutSlowInterrupt();
721 }
722 };
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100723
lberki4741aa82018-02-05 07:59:13 -0800724 return new InterruptSignalHandler() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100725 @Override
Janak Ramakrishnan7be337f2017-01-05 20:21:35 +0000726 public void run() {
lberki97abb522017-09-04 18:51:57 +0200727 logger.info("User interrupt");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100728 OutErr.SYSTEM_OUT_ERR.printErrLn("Blaze received an interrupt");
729 mainThread.interrupt();
730
731 int curNumInterrupts = numInterrupts.incrementAndGet();
732 if (curNumInterrupts == 1) {
733 Thread interruptWatcherThread = new Thread(interruptWatcher, "interrupt-watcher");
734 interruptWatcherThread.setDaemon(true);
735 interruptWatcherThread.start();
736 } else if (curNumInterrupts == 2) {
lberki97abb522017-09-04 18:51:57 +0200737 logger.warning("Second --batch interrupt: Reverting to JVM SIGINT handler");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100738 uninstall();
739 }
740 }
741 };
742 }
743
744 /**
745 * A main method that runs blaze commands in batch mode. The return value indicates the desired
746 * exit status of the program.
747 */
748 private static int batchMain(Iterable<BlazeModule> modules, String[] args) {
lberki4741aa82018-02-05 07:59:13 -0800749 InterruptSignalHandler signalHandler = captureSigint();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100750 CommandLineOptions commandLineOptions = splitStartupOptions(modules, args);
lberki97abb522017-09-04 18:51:57 +0200751 logger.info(
janakr1cb9a012017-03-28 20:18:21 +0000752 "Running Blaze in batch mode with "
753 + maybeGetPidString()
754 + "startup args "
755 + commandLineOptions.getStartupArgs());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100756
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100757 BlazeRuntime runtime;
ccalvarin0ddda782017-04-06 21:12:11 +0000758 InvocationPolicy policy;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100759 try {
Lukacs Berki9f603952017-01-13 14:04:08 +0000760 runtime = newRuntime(modules, commandLineOptions.getStartupArgs(), null);
ccalvarin0ddda782017-04-06 21:12:11 +0000761 policy = InvocationPolicyParser.parsePolicy(
762 runtime.getStartupOptionsProvider().getOptions(BlazeServerStartupOptions.class)
763 .invocationPolicy);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100764 } catch (OptionsParsingException e) {
ccalvarin0ddda782017-04-06 21:12:11 +0000765 OutErr.SYSTEM_OUT_ERR.printErrLn(e.getMessage());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100766 return ExitCode.COMMAND_LINE_ERROR.getNumericExitCode();
767 } catch (AbruptExitException e) {
ccalvarin0ddda782017-04-06 21:12:11 +0000768 OutErr.SYSTEM_OUT_ERR.printErrLn(e.getMessage());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100769 return e.getExitCode().getNumericExitCode();
770 }
771
ccalvarin1cbe62a2017-08-14 21:09:07 +0200772 ImmutableList.Builder<Pair<String, String>> startupOptionsFromCommandLine =
773 ImmutableList.builder();
774 for (String option : commandLineOptions.getStartupArgs()) {
775 startupOptionsFromCommandLine.add(new Pair<>("", option));
776 }
777
Ulf Adams47cb9162015-09-18 08:12:30 +0000778 BlazeCommandDispatcher dispatcher = new BlazeCommandDispatcher(runtime);
lberki4741aa82018-02-05 07:59:13 -0800779 boolean shutdownDone = false;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100780
781 try {
lberki97abb522017-09-04 18:51:57 +0200782 logger.info(getRequestLogString(commandLineOptions.getOtherArgs()));
lberki4741aa82018-02-05 07:59:13 -0800783 BlazeCommandResult result = dispatcher.exec(
ccalvarin1cbe62a2017-08-14 21:09:07 +0200784 policy,
785 commandLineOptions.getOtherArgs(),
786 OutErr.SYSTEM_OUT_ERR,
787 LockingMode.ERROR_OUT,
788 "batch client",
789 runtime.getClock().currentTimeMillis(),
790 Optional.of(startupOptionsFromCommandLine.build()));
lberki4741aa82018-02-05 07:59:13 -0800791 if (result.getExecRequest() == null) {
792 // Simple case: we are given an exit code
793 return result.getExitCode().getNumericExitCode();
794 }
795
796 // Not so simple case: we need to execute a binary on shutdown. exec() is not accessible from
797 // Java and is impossible on Windows in any case, so we just execute the binary after getting
798 // out of the way as completely as possible and forward its exit code.
799 // When this code is executed, no locks are held: the client lock is released by the client
800 // before it executes any command and the server lock is handled by BlazeCommandDispatcher,
801 // whose job is done by the time we get here.
802 runtime.shutdown();
803 dispatcher.shutdown();
804 shutdownDone = true;
805 signalHandler.uninstall();
806 ExecRequest request = result.getExecRequest();
807 String[] argv = new String[request.getArgvCount()];
808 for (int i = 0; i < argv.length; i++) {
809 argv[i] = request.getArgv(i).toString(StandardCharsets.ISO_8859_1);
810 }
811
812 String workingDirectory = request.getWorkingDirectory().toString(StandardCharsets.ISO_8859_1);
813 try {
814 ProcessBuilder process = new ProcessBuilder()
815 .command(argv)
816 .directory(new File(workingDirectory))
817 .inheritIO();
818
819 for (int i = 0; i < request.getEnvironmentVariableCount(); i++) {
820 EnvironmentVariable variable = request.getEnvironmentVariable(i);
821 process.environment().put(variable.getName().toString(StandardCharsets.ISO_8859_1),
822 variable.getValue().toString(StandardCharsets.ISO_8859_1));
823 }
824
825 return process.start().waitFor();
826 } catch (IOException e) {
827 // We are in batch mode, thus, stdout/stderr are the same as that of the client.
828 System.err.println("Cannot execute process for 'run' command: " + e.getMessage());
829 logger.log(Level.SEVERE, "Exception while executing binary from 'run' command", e);
830 return ExitCode.LOCAL_ENVIRONMENTAL_ERROR.getNumericExitCode();
831 }
Lukacs Berkice1445f2016-04-19 15:52:55 +0000832 } catch (InterruptedException e) {
833 // This is almost main(), so it's okay to just swallow it. We are exiting soon.
834 return ExitCode.INTERRUPTED.getNumericExitCode();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100835 } finally {
lberki4741aa82018-02-05 07:59:13 -0800836 if (!shutdownDone) {
837 runtime.shutdown();
838 dispatcher.shutdown();
839 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100840 }
841 }
842
843 /**
844 * A main method that does not send email. The return value indicates the desired exit status of
845 * the program.
846 */
847 private static int serverMain(Iterable<BlazeModule> modules, OutErr outErr, String[] args) {
Lukacs Berkif7633792016-04-21 12:38:29 +0000848 InterruptSignalHandler sigintHandler = null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100849 try {
lberkiac8eb422018-02-06 09:27:30 -0800850 final RPCServer[] rpcServer = new RPCServer[1];
851 Runnable prepareForAbruptShutdown = () -> rpcServer[0].prepareForAbruptShutdown();
852 BlazeRuntime runtime = newRuntime(modules, Arrays.asList(args), prepareForAbruptShutdown);
853 BlazeCommandDispatcher dispatcher = new BlazeCommandDispatcher(runtime);
lberkiac8eb422018-02-06 09:27:30 -0800854 BlazeServerStartupOptions startupOptions =
855 runtime.getStartupOptionsProvider().getOptions(BlazeServerStartupOptions.class);
856 try {
857 // This is necessary so that Bazel kind of works during bootstrapping, at which time the
858 // gRPC server is not compiled in so that we don't need gRPC for bootstrapping.
859 Class<?> factoryClass = Class.forName(
860 "com.google.devtools.build.lib.server.GrpcServerImpl$Factory");
861 RPCServer.Factory factory = (RPCServer.Factory) factoryClass.getConstructor().newInstance();
lberkibbcf2032018-02-06 10:11:56 -0800862 rpcServer[0] = factory.create(dispatcher, runtime.getClock(),
lberkiac8eb422018-02-06 09:27:30 -0800863 startupOptions.commandPort,
864 runtime.getWorkspace().getWorkspace(),
865 runtime.getServerDirectory(),
866 startupOptions.maxIdleSeconds);
867 } catch (ReflectiveOperationException | IllegalArgumentException e) {
868 throw new AbruptExitException("gRPC server not compiled in", ExitCode.BLAZE_INTERNAL_ERROR);
869 }
Lukacs Berkif7633792016-04-21 12:38:29 +0000870
871 // Register the signal handler.
Janak Ramakrishnan7be337f2017-01-05 20:21:35 +0000872 sigintHandler =
873 new InterruptSignalHandler() {
874 @Override
875 public void run() {
lberki97abb522017-09-04 18:51:57 +0200876 logger.severe("User interrupt");
lberkiac8eb422018-02-06 09:27:30 -0800877 rpcServer[0].interrupt();
Janak Ramakrishnan7be337f2017-01-05 20:21:35 +0000878 }
879 };
Lukacs Berkif7633792016-04-21 12:38:29 +0000880
lberkiac8eb422018-02-06 09:27:30 -0800881 rpcServer[0].serve();
882 runtime.shutdown();
883 dispatcher.shutdown();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100884 return ExitCode.SUCCESS.getNumericExitCode();
885 } catch (OptionsParsingException e) {
886 outErr.printErr(e.getMessage());
887 return ExitCode.COMMAND_LINE_ERROR.getNumericExitCode();
888 } catch (IOException e) {
889 outErr.printErr("I/O Error: " + e.getMessage());
890 return ExitCode.BUILD_FAILURE.getNumericExitCode();
891 } catch (AbruptExitException e) {
892 outErr.printErr(e.getMessage());
893 return e.getExitCode().getNumericExitCode();
Lukacs Berkif7633792016-04-21 12:38:29 +0000894 } finally {
895 if (sigintHandler != null) {
896 sigintHandler.uninstall();
897 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100898 }
899 }
900
ccalvarinfb3293c2017-09-26 11:13:33 -0400901 private static FileSystem defaultFileSystemImplementation() {
Lukacs Berki28219722016-06-30 12:34:39 +0000902 if ("0".equals(System.getProperty("io.bazel.EnableJni"))) {
Damien Martin-Guillerez8c04e9e2016-01-18 12:53:39 +0000903 // Ignore UnixFileSystem, to be used for bootstrapping.
Dmitry Lomovbe939512016-02-02 20:26:25 +0000904 return OS.getCurrent() == OS.WINDOWS ? new WindowsFileSystem() : new JavaIoFileSystem();
Damien Martin-Guillerez8c04e9e2016-01-18 12:53:39 +0000905 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100906 // The JNI-based UnixFileSystem is faster, but on Windows it is not available.
Lukacs Berki58dbb7f2016-01-29 11:56:29 +0000907 return OS.getCurrent() == OS.WINDOWS ? new WindowsFileSystem() : new UnixFileSystem();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100908 }
909
ulfjack27758e42017-09-07 13:41:33 +0200910 private static SubprocessFactory subprocessFactoryImplementation() {
Lukacs Berki74dcfee2016-07-04 12:57:25 +0000911 if (!"0".equals(System.getProperty("io.bazel.EnableJni")) && OS.getCurrent() == OS.WINDOWS) {
912 return WindowsSubprocessFactory.INSTANCE;
913 } else {
914 return JavaSubprocessFactory.INSTANCE;
915 }
916 }
917
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100918 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100919 * Parses the command line arguments into a {@link OptionsParser} object.
920 *
921 * <p>This function needs to parse the --option_sources option manually so that the real option
922 * parser can set the source for every option correctly. If that cannot be parsed or is missing,
923 * we just report an unknown source for every startup option.
924 */
925 private static OptionsProvider parseOptions(
926 Iterable<BlazeModule> modules, List<String> args) throws OptionsParsingException {
Ulf Adams7e71f892016-08-10 09:06:50 +0000927 ImmutableList<Class<? extends OptionsBase>> optionClasses =
928 BlazeCommandUtils.getStartupOptions(modules);
929
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100930 // First parse the command line so that we get the option_sources argument
931 OptionsParser parser = OptionsParser.newOptionsParser(optionClasses);
932 parser.setAllowResidue(false);
ccalvarin7cd9e882017-10-16 22:18:32 +0200933 parser.parse(PriorityCategory.COMMAND_LINE, null, args);
lberki6a8a1742017-06-29 10:09:42 +0200934 Map<String, String> optionSources =
935 parser.getOptions(BlazeServerStartupOptions.class).optionSources;
ccalvarin1dce0972017-09-11 20:03:02 +0200936 Function<OptionDefinition, String> sourceFunction =
937 option ->
938 !optionSources.containsKey(option.getOptionName())
939 ? "default"
940 : optionSources.get(option.getOptionName()).isEmpty()
941 ? "command line"
942 : optionSources.get(option.getOptionName());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100943
944 // Then parse the command line again, this time with the correct option sources
945 parser = OptionsParser.newOptionsParser(optionClasses);
946 parser.setAllowResidue(false);
ccalvarin7cd9e882017-10-16 22:18:32 +0200947 parser.parseWithSourceFunction(PriorityCategory.COMMAND_LINE, sourceFunction, args);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100948 return parser;
949 }
950
951 /**
952 * Creates a new blaze runtime, given the install and output base directories.
953 *
954 * <p>Note: This method can and should only be called once per startup, as it also creates the
955 * filesystem object that will be used for the runtime. So it should only ever be called from the
956 * main method of the Blaze program.
957 *
Ulf Adams7db73d32016-08-10 12:41:02 +0000958 * @param args Blaze startup options.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100959 *
960 * @return a new BlazeRuntime instance initialized with the given filesystem and directories, and
961 * an error string that, if not null, describes a fatal initialization failure that makes
962 * this runtime unsuitable for real commands
963 */
Lukacs Berki9f603952017-01-13 14:04:08 +0000964 private static BlazeRuntime newRuntime(Iterable<BlazeModule> blazeModules, List<String> args,
965 Runnable abruptShutdownHandler)
Ulf Adams7db73d32016-08-10 12:41:02 +0000966 throws AbruptExitException, OptionsParsingException {
967 OptionsProvider options = parseOptions(blazeModules, args);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100968 for (BlazeModule module : blazeModules) {
969 module.globalInit(options);
970 }
971
972 BlazeServerStartupOptions startupOptions = options.getOptions(BlazeServerStartupOptions.class);
Ulf Adamsb5a06f332016-06-21 14:55:49 +0000973 String productName = startupOptions.productName.toLowerCase(Locale.US);
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +0000974
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100975 PathFragment workspaceDirectory = startupOptions.workspaceDirectory;
976 PathFragment installBase = startupOptions.installBase;
977 PathFragment outputBase = startupOptions.outputBase;
978
janakr1cb9a012017-03-28 20:18:21 +0000979 maybeForceJNIByGettingPid(installBase); // Must be before first use of JNI.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100980
981 // From the point of view of the Java program --install_base and --output_base
982 // are mandatory options, despite the comment in their declarations.
983 if (installBase == null || !installBase.isAbsolute()) { // (includes "" default case)
984 throw new IllegalArgumentException(
985 "Bad --install_base option specified: '" + installBase + "'");
986 }
987 if (outputBase != null && !outputBase.isAbsolute()) { // (includes "" default case)
988 throw new IllegalArgumentException(
989 "Bad --output_base option specified: '" + outputBase + "'");
990 }
991
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100992 FileSystem fs = null;
993 for (BlazeModule module : blazeModules) {
Ulf Adams124bd0a2016-08-10 13:10:46 +0000994 FileSystem moduleFs = module.getFileSystem(options);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100995 if (moduleFs != null) {
996 Preconditions.checkState(fs == null, "more than one module returns a file system");
997 fs = moduleFs;
998 }
999 }
1000
1001 if (fs == null) {
ccalvarinfb3293c2017-09-26 11:13:33 -04001002 fs = defaultFileSystemImplementation();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001003 }
Lukacs Berki74dcfee2016-07-04 12:57:25 +00001004
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001005 Path.setFileSystemForSerialization(fs);
Lukacs Berki74dcfee2016-07-04 12:57:25 +00001006 SubprocessBuilder.setSubprocessFactory(subprocessFactoryImplementation());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001007
1008 Path installBasePath = fs.getPath(installBase);
1009 Path outputBasePath = fs.getPath(outputBase);
1010 Path workspaceDirectoryPath = null;
1011 if (!workspaceDirectory.equals(PathFragment.EMPTY_FRAGMENT)) {
1012 workspaceDirectoryPath = fs.getPath(workspaceDirectory);
1013 }
1014
Ulf Adams15a23b92016-08-09 11:46:00 +00001015 ServerDirectories serverDirectories =
1016 new ServerDirectories(installBasePath, outputBasePath, startupOptions.installMD5);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001017 Clock clock = BlazeClock.instance();
tomluf903eb52017-10-27 12:12:11 -04001018 BlazeRuntime.Builder runtimeBuilder =
1019 new BlazeRuntime.Builder()
1020 .setProductName(productName)
1021 .setFileSystem(fs)
1022 .setServerDirectories(serverDirectories)
tomlu3d1a1942017-11-29 14:01:21 -08001023 .setActionKeyContext(new ActionKeyContext())
tomluf903eb52017-10-27 12:12:11 -04001024 .setStartupOptionsProvider(options)
1025 .setClock(clock)
1026 .setAbruptShutdownHandler(abruptShutdownHandler)
1027 // TODO(bazel-team): Make BugReportingExceptionHandler the default.
1028 // See bug "Make exceptions in EventBus subscribers fatal"
1029 .setEventBusExceptionHandler(
1030 startupOptions.fatalEventBusExceptions
1031 || !BlazeVersionInfo.instance().isReleasedBlaze()
1032 ? new BlazeRuntime.BugReportingExceptionHandler()
1033 : new BlazeRuntime.RemoteExceptionHandler());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001034
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +00001035 if (System.getenv("TEST_TMPDIR") != null
1036 && System.getenv("NO_CRASH_ON_LOGGING_IN_TEST") == null) {
1037 LoggingUtil.installRemoteLogger(getTestCrashLogger());
1038 }
1039
Ulf Adams345e15e2016-07-07 13:27:28 +00001040 runtimeBuilder.addBlazeModule(new BuiltinCommandModule());
ulfjack9274cba2017-08-11 23:19:48 +02001041 // This module needs to be registered before any module providing a SpawnCache implementation.
1042 runtimeBuilder.addBlazeModule(new NoSpawnCacheModule());
ulfjack236635a2017-06-26 09:25:52 +02001043 runtimeBuilder.addBlazeModule(new CommandLogModule());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001044 for (BlazeModule blazeModule : blazeModules) {
1045 runtimeBuilder.addBlazeModule(blazeModule);
1046 }
1047
1048 BlazeRuntime runtime = runtimeBuilder.build();
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001049
1050 BlazeDirectories directories =
janakr3b63a4e2017-09-14 09:55:40 +02001051 new BlazeDirectories(serverDirectories, workspaceDirectoryPath, productName);
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001052 BinTools binTools;
1053 try {
1054 binTools = BinTools.forProduction(directories);
1055 } catch (IOException e) {
1056 throw new AbruptExitException(
1057 "Cannot enumerate embedded binaries: " + e.getMessage(),
1058 ExitCode.LOCAL_ENVIRONMENTAL_ERROR);
1059 }
1060 runtime.initWorkspace(directories, binTools);
ccalvarin1419dda2017-03-28 21:12:28 +00001061 CustomExitCodePublisher.setAbruptExitStatusFileDir(serverDirectories.getOutputBase());
1062
Nathan Harmatad4803012015-09-08 20:03:22 +00001063 AutoProfiler.setClock(runtime.getClock());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001064 BugReport.setRuntime(runtime);
1065 return runtime;
1066 }
1067
janakr1cb9a012017-03-28 20:18:21 +00001068 private static String maybeGetPidString() {
1069 Integer pid = maybeForceJNIByGettingPid(null);
1070 return pid == null ? "" : "pid " + pid + " and ";
1071 }
1072
1073 /** Loads JNI libraries, if necessary under the current platform. */
1074 @Nullable
1075 private static Integer maybeForceJNIByGettingPid(@Nullable PathFragment installBase) {
1076 return jniLibsAvailable() ? getPidUsingJNI(installBase) : null;
Ulf Adams8afbd3c2017-02-28 10:42:48 +00001077 }
1078
1079 private static boolean jniLibsAvailable() {
1080 return !"0".equals(System.getProperty("io.bazel.EnableJni"));
1081 }
1082
1083 // Force JNI linking at a moment when we have 'installBase' handy, and print
1084 // an informative error if it fails.
janakr1cb9a012017-03-28 20:18:21 +00001085 private static int getPidUsingJNI(@Nullable PathFragment installBase) {
Ulf Adams8afbd3c2017-02-28 10:42:48 +00001086 try {
janakr1cb9a012017-03-28 20:18:21 +00001087 return ProcessUtils.getpid(); // force JNI initialization
Ulf Adams8afbd3c2017-02-28 10:42:48 +00001088 } catch (UnsatisfiedLinkError t) {
janakr1cb9a012017-03-28 20:18:21 +00001089 System.err.println(
1090 "JNI initialization failed: "
1091 + t.getMessage()
1092 + ". "
1093 + "Possibly your installation has been corrupted"
1094 + (installBase == null
1095 ? ""
1096 : "; if this problem persists, try 'rm -fr " + installBase + "'")
1097 + ".");
Ulf Adams8afbd3c2017-02-28 10:42:48 +00001098 throw t;
1099 }
1100 }
1101
1102 /**
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +00001103 * Returns a logger that crashes as soon as it's written to, since tests should not cause events
1104 * that would be logged.
1105 */
1106 @VisibleForTesting
1107 public static Future<Logger> getTestCrashLogger() {
1108 Logger crashLogger = Logger.getAnonymousLogger();
1109 crashLogger.addHandler(
1110 new Handler() {
1111 @Override
1112 public void publish(LogRecord record) {
Michajlo Matijkiwf5cc0b22016-01-20 17:44:44 +00001113 System.err.println("Remote logging disabled for testing, forcing abrupt shutdown.");
1114 System.err.printf("%s#%s: %s\n",
1115 record.getSourceClassName(),
1116 record.getSourceMethodName(),
1117 record.getMessage());
1118
Janak Ramakrishnana88e65b2015-09-17 15:07:26 +00001119 Throwable e = record.getThrown();
Michajlo Matijkiwf5cc0b22016-01-20 17:44:44 +00001120 if (e != null) {
1121 e.printStackTrace();
Janak Ramakrishnana88e65b2015-09-17 15:07:26 +00001122 }
Michajlo Matijkiwf5cc0b22016-01-20 17:44:44 +00001123
1124 Runtime.getRuntime().halt(ExitCode.BLAZE_INTERNAL_ERROR.getNumericExitCode());
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +00001125 }
1126
1127 @Override
1128 public void flush() {
1129 throw new IllegalStateException();
1130 }
1131
1132 @Override
1133 public void close() {
1134 throw new IllegalStateException();
1135 }
1136 });
1137 return Futures.immediateFuture(crashLogger);
1138 }
1139
1140 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001141 * Make sure async threads cannot be orphaned. This method makes sure bugs are reported to
1142 * telemetry and the proper exit code is reported.
1143 */
1144 private static void setupUncaughtHandler(final String[] args) {
Janak Ramakrishnanb6582fa2016-03-14 16:19:40 +00001145 Thread.setDefaultUncaughtExceptionHandler(
laurentlb3d2a68c2017-06-30 00:32:04 +02001146 (thread, throwable) -> BugReport.handleCrash(throwable, args));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001147 }
1148
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +00001149 public String getProductName() {
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +00001150 return productName;
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +00001151 }
1152
Klaus Aehligdadde6f2017-01-18 17:32:07 +00001153 public PathConverter getPathToUriConverter() {
1154 return pathToUriConverter;
1155 }
1156
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001157 /**
1158 * A builder for {@link BlazeRuntime} objects. The only required fields are the {@link
1159 * BlazeDirectories}, and the {@link RuleClassProvider} (except for testing). All other fields
1160 * have safe default values.
1161 *
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001162 * <p>The default behavior of the BlazeRuntime's EventBus is to exit when a subscriber throws
1163 * an exception. Please plan appropriately.
1164 */
1165 public static class Builder {
tomluf903eb52017-10-27 12:12:11 -04001166 private FileSystem fileSystem;
Ulf Adams15a23b92016-08-09 11:46:00 +00001167 private ServerDirectories serverDirectories;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001168 private Clock clock;
Lukacs Berki9f603952017-01-13 14:04:08 +00001169 private Runnable abruptShutdownHandler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001170 private OptionsProvider startupOptionsProvider;
Ulf Adams47cb9162015-09-18 08:12:30 +00001171 private final List<BlazeModule> blazeModules = new ArrayList<>();
Ulf Adams8b56c8c2016-04-12 13:45:56 +00001172 private SubscriberExceptionHandler eventBusExceptionHandler = new RemoteExceptionHandler();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001173 private UUID instanceId;
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +00001174 private String productName;
tomlu3d1a1942017-11-29 14:01:21 -08001175 private ActionKeyContext actionKeyContext;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001176
1177 public BlazeRuntime build() throws AbruptExitException {
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +00001178 Preconditions.checkNotNull(productName);
Ulf Adams15a23b92016-08-09 11:46:00 +00001179 Preconditions.checkNotNull(serverDirectories);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001180 Preconditions.checkNotNull(startupOptionsProvider);
tomlu3d1a1942017-11-29 14:01:21 -08001181 ActionKeyContext actionKeyContext =
1182 this.actionKeyContext != null ? this.actionKeyContext : new ActionKeyContext();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001183 Clock clock = (this.clock == null) ? BlazeClock.instance() : this.clock;
1184 UUID instanceId = (this.instanceId == null) ? UUID.randomUUID() : this.instanceId;
1185
1186 Preconditions.checkNotNull(clock);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001187
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001188 for (BlazeModule module : blazeModules) {
tomluf903eb52017-10-27 12:12:11 -04001189 module.blazeStartup(
1190 startupOptionsProvider,
1191 BlazeVersionInfo.instance(),
1192 instanceId,
1193 fileSystem,
1194 serverDirectories,
1195 clock);
Ulf Adams8b56c8c2016-04-12 13:45:56 +00001196 }
Ulf Adams345e15e2016-07-07 13:27:28 +00001197 ServerBuilder serverBuilder = new ServerBuilder();
Ulf Adams54eeac52016-08-29 09:07:35 +00001198 serverBuilder.addQueryOutputFormatters(OutputFormatter.getDefaultFormatters());
Ulf Adams8b56c8c2016-04-12 13:45:56 +00001199 for (BlazeModule module : blazeModules) {
Ulf Adams345e15e2016-07-07 13:27:28 +00001200 module.serverInit(startupOptionsProvider, serverBuilder);
Nathan Harmataf0cc5b82016-03-18 14:56:28 +00001201 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001202
1203 ConfiguredRuleClassProvider.Builder ruleClassBuilder =
1204 new ConfiguredRuleClassProvider.Builder();
1205 for (BlazeModule module : blazeModules) {
1206 module.initializeRuleClasses(ruleClassBuilder);
1207 }
1208
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001209 ConfiguredRuleClassProvider ruleClassProvider = ruleClassBuilder.build();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001210
Nathan Harmata42fb5602016-05-25 20:32:08 +00001211 Package.Builder.Helper packageBuilderHelper = null;
1212 for (BlazeModule module : blazeModules) {
Nathan Harmatacaa000a2016-06-07 17:46:19 +00001213 Package.Builder.Helper candidateHelper =
tomluf903eb52017-10-27 12:12:11 -04001214 module.getPackageBuilderHelper(ruleClassProvider, fileSystem);
Nathan Harmata42fb5602016-05-25 20:32:08 +00001215 if (candidateHelper != null) {
1216 Preconditions.checkState(packageBuilderHelper == null,
1217 "more than one module defines a package builder helper");
1218 packageBuilderHelper = candidateHelper;
1219 }
1220 }
1221 if (packageBuilderHelper == null) {
1222 packageBuilderHelper = Package.Builder.DefaultHelper.INSTANCE;
1223 }
1224
Janak Ramakrishnan02498412016-05-11 22:28:43 +00001225 PackageFactory packageFactory =
1226 new PackageFactory(
1227 ruleClassProvider,
Ulf Adams345e15e2016-07-07 13:27:28 +00001228 ruleClassBuilder.getPlatformRegexps(),
1229 serverBuilder.getAttributeContainerFactory(),
Ulf Adamsed5be452016-11-02 16:33:57 +00001230 serverBuilder.getEnvironmentExtensions(),
Nathan Harmata42fb5602016-05-25 20:32:08 +00001231 BlazeVersionInfo.instance().getVersion(),
1232 packageBuilderHelper);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001233
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001234 ProjectFile.Provider projectFileProvider = null;
1235 for (BlazeModule module : blazeModules) {
1236 ProjectFile.Provider candidate = module.createProjectFileProvider();
1237 if (candidate != null) {
1238 Preconditions.checkState(projectFileProvider == null,
1239 "more than one module defines a project file provider");
1240 projectFileProvider = candidate;
1241 }
1242 }
1243
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001244 return new BlazeRuntime(
tomluf903eb52017-10-27 12:12:11 -04001245 fileSystem,
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001246 serverBuilder.getQueryEnvironmentFactory(),
Ulf Adams6bfd2c52016-08-29 08:00:39 +00001247 serverBuilder.getQueryFunctions(),
Ulf Adams54eeac52016-08-29 09:07:35 +00001248 serverBuilder.getQueryOutputFormatters(),
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001249 packageFactory,
1250 ruleClassProvider,
gregced79b9372017-08-10 21:51:15 +02001251 ruleClassProvider.getConfigurationFragments(),
Ulf Adams6bfd2c52016-08-29 08:00:39 +00001252 serverBuilder.getInfoItems(),
tomlu3d1a1942017-11-29 14:01:21 -08001253 actionKeyContext,
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001254 clock,
Lukacs Berki9f603952017-01-13 14:04:08 +00001255 abruptShutdownHandler,
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001256 startupOptionsProvider,
1257 ImmutableList.copyOf(blazeModules),
1258 eventBusExceptionHandler,
1259 projectFileProvider,
1260 serverBuilder.getInvocationPolicy(),
1261 serverBuilder.getCommands(),
Klaus Aehligdadde6f2017-01-18 17:32:07 +00001262 productName,
1263 serverBuilder.getPathToUriConverter());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001264 }
1265
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +00001266 public Builder setProductName(String productName) {
1267 this.productName = productName;
1268 return this;
1269 }
1270
tomluf903eb52017-10-27 12:12:11 -04001271 public Builder setFileSystem(FileSystem fileSystem) {
1272 this.fileSystem = fileSystem;
1273 return this;
1274 }
1275
Ulf Adams15a23b92016-08-09 11:46:00 +00001276 public Builder setServerDirectories(ServerDirectories serverDirectories) {
1277 this.serverDirectories = serverDirectories;
1278 return this;
1279 }
1280
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001281 public Builder setClock(Clock clock) {
1282 this.clock = clock;
1283 return this;
1284 }
1285
Lukacs Berki9f603952017-01-13 14:04:08 +00001286 public Builder setAbruptShutdownHandler(Runnable handler) {
1287 this.abruptShutdownHandler = handler;
1288 return this;
1289 }
1290
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001291 public Builder setStartupOptionsProvider(OptionsProvider startupOptionsProvider) {
1292 this.startupOptionsProvider = startupOptionsProvider;
1293 return this;
1294 }
1295
1296 public Builder addBlazeModule(BlazeModule blazeModule) {
1297 blazeModules.add(blazeModule);
1298 return this;
1299 }
1300
1301 public Builder setInstanceId(UUID id) {
1302 instanceId = id;
1303 return this;
1304 }
1305
1306 @VisibleForTesting
1307 public Builder setEventBusExceptionHandler(
1308 SubscriberExceptionHandler eventBusExceptionHandler) {
1309 this.eventBusExceptionHandler = eventBusExceptionHandler;
1310 return this;
1311 }
tomlu3d1a1942017-11-29 14:01:21 -08001312
1313 public Builder setActionKeyContext(ActionKeyContext actionKeyContext) {
1314 this.actionKeyContext = actionKeyContext;
1315 return this;
1316 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001317 }
1318}