blob: 8eb5ffbb47cf1be79d5443bfd626199e2a485786 [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;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010018import com.google.common.collect.ImmutableList;
Ulf Adams6bfd2c52016-08-29 08:00:39 +000019import com.google.common.collect.ImmutableMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010020import com.google.common.collect.Lists;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010021import com.google.common.eventbus.SubscriberExceptionContext;
22import com.google.common.eventbus.SubscriberExceptionHandler;
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +000023import com.google.common.util.concurrent.Futures;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010024import com.google.common.util.concurrent.Uninterruptibles;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010025import com.google.devtools.build.lib.analysis.BlazeDirectories;
26import com.google.devtools.build.lib.analysis.BlazeVersionInfo;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010027import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
Ulf Adams15a23b92016-08-09 11:46:00 +000028import com.google.devtools.build.lib.analysis.ServerDirectories;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010029import com.google.devtools.build.lib.analysis.config.BinTools;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010030import com.google.devtools.build.lib.analysis.config.BuildOptions;
gregced79b9372017-08-10 21:51:15 +020031import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory;
ulfjackab21d182017-08-10 15:36:14 +020032import com.google.devtools.build.lib.analysis.test.CoverageReportActionFactory;
Klaus Aehligdadde6f2017-01-18 17:32:07 +000033import com.google.devtools.build.lib.buildeventstream.PathConverter;
philwo3bcb9f62017-09-06 12:52:21 +020034import com.google.devtools.build.lib.clock.BlazeClock;
35import com.google.devtools.build.lib.clock.Clock;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010036import com.google.devtools.build.lib.events.Event;
37import com.google.devtools.build.lib.events.OutputFilter;
Nathan Harmata42fb5602016-05-25 20:32:08 +000038import com.google.devtools.build.lib.packages.Package;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010039import com.google.devtools.build.lib.packages.PackageFactory;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010040import com.google.devtools.build.lib.packages.RuleClassProvider;
Nathan Harmatad4803012015-09-08 20:03:22 +000041import com.google.devtools.build.lib.profiler.AutoProfiler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010042import com.google.devtools.build.lib.profiler.MemoryProfiler;
43import com.google.devtools.build.lib.profiler.ProfilePhase;
44import com.google.devtools.build.lib.profiler.Profiler;
45import com.google.devtools.build.lib.profiler.Profiler.ProfiledTaskKinds;
46import com.google.devtools.build.lib.profiler.ProfilerTask;
Nathan Harmataf0cc5b82016-03-18 14:56:28 +000047import com.google.devtools.build.lib.query2.AbstractBlazeQueryEnvironment;
48import com.google.devtools.build.lib.query2.QueryEnvironmentFactory;
Ulf Adams6bfd2c52016-08-29 08:00:39 +000049import com.google.devtools.build.lib.query2.engine.QueryEnvironment.QueryFunction;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010050import com.google.devtools.build.lib.query2.output.OutputFormatter;
Lukacs Berkice1445f2016-04-19 15:52:55 +000051import com.google.devtools.build.lib.runtime.BlazeCommandDispatcher.LockingMode;
Ulf Adams6bfd2c52016-08-29 08:00:39 +000052import com.google.devtools.build.lib.runtime.commands.InfoItem;
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +000053import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010054import com.google.devtools.build.lib.server.RPCServer;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010055import com.google.devtools.build.lib.server.signal.InterruptSignalHandler;
Lukacs Berki74dcfee2016-07-04 12:57:25 +000056import com.google.devtools.build.lib.shell.JavaSubprocessFactory;
Lukacs Berki74dcfee2016-07-04 12:57:25 +000057import com.google.devtools.build.lib.shell.SubprocessBuilder;
ulfjack27758e42017-09-07 13:41:33 +020058import com.google.devtools.build.lib.shell.SubprocessFactory;
Ulf Adams8afbd3c2017-02-28 10:42:48 +000059import com.google.devtools.build.lib.unix.UnixFileSystem;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010060import com.google.devtools.build.lib.util.AbruptExitException;
Chloe Calvarineaa3be72016-12-13 19:48:34 +000061import com.google.devtools.build.lib.util.CustomExitCodePublisher;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010062import com.google.devtools.build.lib.util.ExitCode;
63import com.google.devtools.build.lib.util.LoggingUtil;
64import com.google.devtools.build.lib.util.OS;
ccalvarin1cbe62a2017-08-14 21:09:07 +020065import com.google.devtools.build.lib.util.Pair;
Mark Schaller6df81792015-12-10 18:47:47 +000066import com.google.devtools.build.lib.util.Preconditions;
Ulf Adams8afbd3c2017-02-28 10:42:48 +000067import com.google.devtools.build.lib.util.ProcessUtils;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010068import com.google.devtools.build.lib.util.ThreadUtils;
69import com.google.devtools.build.lib.util.io.OutErr;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010070import com.google.devtools.build.lib.vfs.FileSystem;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010071import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
72import com.google.devtools.build.lib.vfs.Path;
73import com.google.devtools.build.lib.vfs.PathFragment;
Ulf Adams8afbd3c2017-02-28 10:42:48 +000074import com.google.devtools.build.lib.windows.WindowsFileSystem;
Lukacs Berki74dcfee2016-07-04 12:57:25 +000075import com.google.devtools.build.lib.windows.WindowsSubprocessFactory;
ccalvarinf39dc6f2017-06-09 11:51:45 -040076import com.google.devtools.common.options.CommandNameCache;
77import com.google.devtools.common.options.InvocationPolicyParser;
ccalvarine8aae032017-08-22 07:17:44 +020078import com.google.devtools.common.options.OptionDefinition;
ccalvarin7cd9e882017-10-16 22:18:32 +020079import com.google.devtools.common.options.OptionPriority.PriorityCategory;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010080import com.google.devtools.common.options.OptionsBase;
81import com.google.devtools.common.options.OptionsClassProvider;
82import com.google.devtools.common.options.OptionsParser;
83import com.google.devtools.common.options.OptionsParsingException;
84import com.google.devtools.common.options.OptionsProvider;
85import com.google.devtools.common.options.TriState;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010086import java.io.BufferedOutputStream;
87import java.io.IOException;
88import java.io.OutputStream;
ccalvarin987f09f2017-08-31 19:50:39 +020089import java.lang.reflect.Type;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010090import java.util.ArrayList;
91import java.util.Arrays;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010092import java.util.Date;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010093import java.util.Iterator;
Ulf Adams47cb9162015-09-18 08:12:30 +000094import java.util.LinkedHashMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010095import java.util.List;
Ulf Adamsb5a06f332016-06-21 14:55:49 +000096import java.util.Locale;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010097import java.util.Map;
ccalvarin1cbe62a2017-08-14 21:09:07 +020098import java.util.Optional;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010099import java.util.UUID;
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +0000100import java.util.concurrent.Future;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100101import java.util.concurrent.TimeUnit;
102import java.util.concurrent.atomic.AtomicInteger;
ccalvarine8aae032017-08-22 07:17:44 +0200103import java.util.function.Function;
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +0000104import java.util.logging.Handler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100105import java.util.logging.Level;
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +0000106import java.util.logging.LogRecord;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100107import java.util.logging.Logger;
Ulf Adamsfd370042017-06-16 15:52:06 +0200108import java.util.regex.Matcher;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100109import java.util.regex.Pattern;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100110import javax.annotation.Nullable;
111
112/**
Ulf Adams633f5392015-09-15 11:13:08 +0000113 * The BlazeRuntime class encapsulates the immutable configuration of the current instance. These
114 * runtime settings and services are available to most parts of any Blaze application for the
115 * duration of the batch run or server lifetime.
116 *
117 * <p>The parts specific to the current command are stored in {@link CommandEnvironment}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100118 */
119public final class BlazeRuntime {
Michajlo Matijkiwa132f0e2016-11-21 17:30:43 +0000120 private static final Pattern suppressFromLog =
Ulf Adamsfd370042017-06-16 15:52:06 +0200121 Pattern.compile("--client_env=([^=]*(?:auth|pass|cookie)[^=]*)=", Pattern.CASE_INSENSITIVE);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100122
lberki97abb522017-09-04 18:51:57 +0200123 private static final Logger logger = Logger.getLogger(BlazeRuntime.class.getName());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100124
tomluf903eb52017-10-27 12:12:11 -0400125 private final FileSystem fileSystem;
Ulf Adams50e7db62015-10-20 09:14:16 +0000126 private final Iterable<BlazeModule> blazeModules;
127 private final Map<String, BlazeCommand> commandMap = new LinkedHashMap<>();
128 private final Clock clock;
Lukacs Berki9f603952017-01-13 14:04:08 +0000129 private final Runnable abruptShutdownHandler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100130
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100131 private final PackageFactory packageFactory;
gregced79b9372017-08-10 21:51:15 +0200132 private final ImmutableList<ConfigurationFragmentFactory> configurationFragmentFactories;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100133 private final ConfiguredRuleClassProvider ruleClassProvider;
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000134 // For bazel info.
135 private final ImmutableMap<String, InfoItem> infoItems;
136 // For bazel query.
137 private final QueryEnvironmentFactory queryEnvironmentFactory;
138 private final ImmutableList<QueryFunction> queryFunctions;
Ulf Adams54eeac52016-08-29 09:07:35 +0000139 private final ImmutableList<OutputFormatter> queryOutputFormatters;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100140
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100141 private final AtomicInteger storedExitCode = new AtomicInteger();
142
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100143 // We pass this through here to make it available to the MasterLogWriter.
144 private final OptionsProvider startupOptionsProvider;
145
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100146 private final ProjectFile.Provider projectFileProvider;
Chloe Calvarin6b607182017-03-27 15:37:50 +0000147 @Nullable private final InvocationPolicy moduleInvocationPolicy;
Janak Ramakrishnan1d899602016-04-28 03:48:55 +0000148 private final String defaultsPackageContent;
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000149 private final SubscriberExceptionHandler eventBusExceptionHandler;
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +0000150 private final String productName;
Klaus Aehligdadde6f2017-01-18 17:32:07 +0000151 private final PathConverter pathToUriConverter;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100152
Ulf Adams50e7db62015-10-20 09:14:16 +0000153 // Workspace state (currently exactly one workspace per server)
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000154 private BlazeWorkspace workspace;
Ulf Adams50e7db62015-10-20 09:14:16 +0000155
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000156 private BlazeRuntime(
tomluf903eb52017-10-27 12:12:11 -0400157 FileSystem fileSystem,
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000158 QueryEnvironmentFactory queryEnvironmentFactory,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000159 ImmutableList<QueryFunction> queryFunctions,
Ulf Adams54eeac52016-08-29 09:07:35 +0000160 ImmutableList<OutputFormatter> queryOutputFormatters,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000161 PackageFactory pkgFactory,
162 ConfiguredRuleClassProvider ruleClassProvider,
gregced79b9372017-08-10 21:51:15 +0200163 ImmutableList<ConfigurationFragmentFactory> configurationFragmentFactories,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000164 ImmutableMap<String, InfoItem> infoItems,
165 Clock clock,
Lukacs Berki9f603952017-01-13 14:04:08 +0000166 Runnable abruptShutdownHandler,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000167 OptionsProvider startupOptionsProvider,
168 Iterable<BlazeModule> blazeModules,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100169 SubscriberExceptionHandler eventBusExceptionHandler,
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000170 ProjectFile.Provider projectFileProvider,
Chloe Calvarin6b607182017-03-27 15:37:50 +0000171 InvocationPolicy moduleInvocationPolicy,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000172 Iterable<BlazeCommand> commands,
Klaus Aehligdadde6f2017-01-18 17:32:07 +0000173 String productName,
174 PathConverter pathToUriConverter) {
Ulf Adams50e7db62015-10-20 09:14:16 +0000175 // Server state
tomluf903eb52017-10-27 12:12:11 -0400176 this.fileSystem = fileSystem;
Ulf Adams50e7db62015-10-20 09:14:16 +0000177 this.blazeModules = blazeModules;
178 overrideCommands(commands);
179
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100180 this.packageFactory = pkgFactory;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100181 this.projectFileProvider = projectFileProvider;
Chloe Calvarin6b607182017-03-27 15:37:50 +0000182 this.moduleInvocationPolicy = moduleInvocationPolicy;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100183
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100184 this.ruleClassProvider = ruleClassProvider;
gregced79b9372017-08-10 21:51:15 +0200185 this.configurationFragmentFactories = configurationFragmentFactories;
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000186 this.infoItems = infoItems;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100187 this.clock = clock;
Lukacs Berki9f603952017-01-13 14:04:08 +0000188 this.abruptShutdownHandler = abruptShutdownHandler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100189 this.startupOptionsProvider = startupOptionsProvider;
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000190 this.queryEnvironmentFactory = queryEnvironmentFactory;
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000191 this.queryFunctions = queryFunctions;
Ulf Adams54eeac52016-08-29 09:07:35 +0000192 this.queryOutputFormatters = queryOutputFormatters;
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000193 this.eventBusExceptionHandler = eventBusExceptionHandler;
Janak Ramakrishnan1d899602016-04-28 03:48:55 +0000194
195 this.defaultsPackageContent =
Chloe Calvarin6b607182017-03-27 15:37:50 +0000196 ruleClassProvider.getDefaultsPackageContent(getModuleInvocationPolicy());
Janak Ramakrishnane9cd0f32016-04-29 22:46:16 +0000197 CommandNameCache.CommandNameCacheInstance.INSTANCE.setCommandNameCache(
198 new CommandNameCacheImpl(getCommandMap()));
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +0000199 this.productName = productName;
Klaus Aehligdadde6f2017-01-18 17:32:07 +0000200 this.pathToUriConverter = pathToUriConverter;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100201 }
202
ulfjack5a3b24c2017-06-08 17:10:06 +0200203 public BlazeWorkspace initWorkspace(BlazeDirectories directories, BinTools binTools)
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000204 throws AbruptExitException {
Ulf Adamsea46dfc2016-08-10 08:44:19 +0000205 Preconditions.checkState(this.workspace == null);
Ulf Adamsde14ade2016-10-14 14:20:31 +0000206 WorkspaceBuilder builder = new WorkspaceBuilder(directories, binTools);
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000207 for (BlazeModule module : blazeModules) {
ulfjack94bee752017-06-13 19:13:35 +0200208 module.workspaceInit(this, directories, builder);
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000209 }
janakreea486c2017-09-22 15:38:50 -0400210 this.workspace =
211 builder.build(this, packageFactory, ruleClassProvider, eventBusExceptionHandler);
ulfjack5a3b24c2017-06-08 17:10:06 +0200212 return workspace;
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000213 }
214
Ulf Adams33644a42016-08-10 11:32:41 +0000215 @Nullable public CoverageReportActionFactory getCoverageReportActionFactory(
216 OptionsClassProvider commandOptions) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100217 CoverageReportActionFactory firstFactory = null;
218 for (BlazeModule module : blazeModules) {
Ulf Adams33644a42016-08-10 11:32:41 +0000219 CoverageReportActionFactory factory = module.getCoverageReportFactory(commandOptions);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100220 if (factory != null) {
221 Preconditions.checkState(firstFactory == null,
222 "only one Blaze Module can have a Coverage Report Factory");
223 firstFactory = factory;
224 }
225 }
226 return firstFactory;
227 }
228
229 /**
Ulf Adams47cb9162015-09-18 08:12:30 +0000230 * Adds the given command under the given name to the map of commands.
231 *
232 * @throws AssertionError if the name is already used by another command.
233 */
234 private void addCommand(BlazeCommand command) {
235 String name = command.getClass().getAnnotation(Command.class).name();
236 if (commandMap.containsKey(name)) {
237 throw new IllegalStateException("Command name or alias " + name + " is already used.");
238 }
239 commandMap.put(name, command);
240 }
241
242 final void overrideCommands(Iterable<BlazeCommand> commands) {
243 commandMap.clear();
244 for (BlazeCommand command : commands) {
245 addCommand(command);
246 }
Ulf Adams47cb9162015-09-18 08:12:30 +0000247 }
248
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +0000249 @Nullable
Chloe Calvarin6b607182017-03-27 15:37:50 +0000250 public InvocationPolicy getModuleInvocationPolicy() {
251 return moduleInvocationPolicy;
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +0000252 }
253
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100254 /**
255 * Conditionally enable profiling.
256 */
Ulf Adams633f5392015-09-15 11:13:08 +0000257 private final boolean initProfiler(CommandEnvironment env, CommonCommandOptions options,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100258 UUID buildID, long execStartTimeNanos) {
259 OutputStream out = null;
260 boolean recordFullProfilerData = false;
261 ProfiledTaskKinds profiledTasks = ProfiledTaskKinds.NONE;
262
263 try {
264 if (options.profilePath != null) {
Ulf Adamsab43b972016-03-30 12:23:50 +0000265 Path profilePath = env.getWorkspace().getRelative(options.profilePath);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100266
267 recordFullProfilerData = options.recordFullProfilerData;
268 out = new BufferedOutputStream(profilePath.getOutputStream(), 1024 * 1024);
Ulf Adams633f5392015-09-15 11:13:08 +0000269 env.getReporter().handle(Event.info("Writing profile data to '" + profilePath + "'"));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100270 profiledTasks = ProfiledTaskKinds.ALL;
271 } else if (options.alwaysProfileSlowOperations) {
272 recordFullProfilerData = false;
273 out = null;
274 profiledTasks = ProfiledTaskKinds.SLOWEST;
275 }
276 if (profiledTasks != ProfiledTaskKinds.NONE) {
277 Profiler.instance().start(profiledTasks, out,
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +0000278 getProductName() + " profile for " + env.getOutputBase() + " at " + new Date()
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100279 + ", build ID: " + buildID,
280 recordFullProfilerData, clock, execStartTimeNanos);
281 return true;
282 }
283 } catch (IOException e) {
Ulf Adams633f5392015-09-15 11:13:08 +0000284 env.getReporter().handle(Event.error("Error while creating profile file: " + e.getMessage()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100285 }
286 return false;
287 }
288
tomluf903eb52017-10-27 12:12:11 -0400289 public FileSystem getFileSystem() {
290 return fileSystem;
291 }
292
Ulf Adamsab43b972016-03-30 12:23:50 +0000293 public BlazeWorkspace getWorkspace() {
294 return workspace;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100295 }
296
297 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100298 * The directory in which blaze stores the server state - that is, the socket
299 * file and a log.
300 */
Ulf Adams94b72db2016-03-30 11:58:37 +0000301 private Path getServerDirectory() {
Ulf Adamsab43b972016-03-30 12:23:50 +0000302 return getWorkspace().getDirectories().getOutputBase().getChild("server");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100303 }
304
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100305 /**
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000306 * Returns the {@link QueryEnvironmentFactory} that should be used to create a
307 * {@link AbstractBlazeQueryEnvironment}, whenever one is needed.
308 */
309 public QueryEnvironmentFactory getQueryEnvironmentFactory() {
310 return queryEnvironmentFactory;
311 }
312
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000313 public ImmutableList<QueryFunction> getQueryFunctions() {
314 return queryFunctions;
315 }
316
Ulf Adams54eeac52016-08-29 09:07:35 +0000317 public ImmutableList<OutputFormatter> getQueryOutputFormatters() {
318 return queryOutputFormatters;
319 }
320
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000321 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100322 * Returns the package factory.
323 */
324 public PackageFactory getPackageFactory() {
325 return packageFactory;
326 }
327
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100328 /**
329 * Returns the rule class provider.
330 */
331 public ConfiguredRuleClassProvider getRuleClassProvider() {
332 return ruleClassProvider;
333 }
334
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000335 public ImmutableMap<String, InfoItem> getInfoItems() {
336 return infoItems;
337 }
338
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100339 public Iterable<BlazeModule> getBlazeModules() {
340 return blazeModules;
341 }
342
343 @SuppressWarnings("unchecked")
344 public <T extends BlazeModule> T getBlazeModule(Class<T> moduleClass) {
345 for (BlazeModule module : blazeModules) {
346 if (module.getClass() == moduleClass) {
347 return (T) module;
348 }
349 }
350
351 return null;
352 }
353
gregced79b9372017-08-10 21:51:15 +0200354 public ImmutableList<ConfigurationFragmentFactory> getConfigurationFragmentFactories() {
355 return configurationFragmentFactories;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100356 }
357
358 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100359 * Returns a provider for project file objects. Can be null if no such provider was set by any of
360 * the modules.
361 */
362 @Nullable
363 public ProjectFile.Provider getProjectFileProvider() {
364 return projectFileProvider;
365 }
366
367 /**
368 * Hook method called by the BlazeCommandDispatcher prior to the dispatch of
369 * each command.
370 *
371 * @param options The CommonCommandOptions used by every command.
372 * @throws AbruptExitException if this command is unsuitable to be run as specified
373 */
Ulf Adams50e7db62015-10-20 09:14:16 +0000374 void beforeCommand(CommandEnvironment env, CommonCommandOptions options, long execStartTimeNanos)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100375 throws AbruptExitException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100376 // Conditionally enable profiling
377 // We need to compensate for launchTimeNanos (measurements taken outside of the jvm).
378 long startupTimeNanos = options.startupTime * 1000000L;
Ulf Adams3815b4c2015-09-18 07:34:13 +0000379 if (initProfiler(env, options, env.getCommandId(), execStartTimeNanos - startupTimeNanos)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100380 Profiler profiler = Profiler.instance();
381
382 // Instead of logEvent() we're calling the low level function to pass the timings we took in
383 // the launcher. We're setting the INIT phase marker so that it follows immediately the LAUNCH
384 // phase.
385 profiler.logSimpleTaskDuration(execStartTimeNanos - startupTimeNanos, 0, ProfilerTask.PHASE,
386 ProfilePhase.LAUNCH.description);
387 profiler.logSimpleTaskDuration(execStartTimeNanos, 0, ProfilerTask.PHASE,
388 ProfilePhase.INIT.description);
389 }
390
391 if (options.memoryProfilePath != null) {
Ulf Adamsc5855302015-10-20 08:46:38 +0000392 Path memoryProfilePath = env.getWorkingDirectory().getRelative(options.memoryProfilePath);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100393 try {
394 MemoryProfiler.instance().start(memoryProfilePath.getOutputStream());
395 } catch (IOException e) {
Ulf Adams633f5392015-09-15 11:13:08 +0000396 env.getReporter().handle(
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100397 Event.error("Error while creating memory profile file: " + e.getMessage()));
398 }
399 }
400
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100401 // Initialize exit code to dummy value for afterCommand.
402 storedExitCode.set(ExitCode.RESERVED.getNumericExitCode());
403 }
404
405 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100406 * Posts the {@link CommandCompleteEvent}, so that listeners can tidy up. Called by {@link
407 * #afterCommand}, and by BugReport when crashing from an exception in an async thread.
408 */
Janak Ramakrishnan39a55132016-05-23 21:55:20 +0000409 void notifyCommandComplete(int exitCode) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100410 if (!storedExitCode.compareAndSet(ExitCode.RESERVED.getNumericExitCode(), exitCode)) {
411 // This command has already been called, presumably because there is a race between the main
412 // thread and a worker thread that crashed. Don't try to arbitrate the dispute. If the main
413 // thread won the race (unlikely, but possible), this may be incorrectly logged as a success.
414 return;
415 }
Janak Ramakrishnana50b7b72016-06-02 21:46:40 +0000416 workspace.getSkyframeExecutor().getEventBus().post(new CommandCompleteEvent(exitCode));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100417 }
418
419 /**
420 * Hook method called by the BlazeCommandDispatcher after the dispatch of each
421 * command.
422 */
423 @VisibleForTesting
Ulf Adams633f5392015-09-15 11:13:08 +0000424 public void afterCommand(CommandEnvironment env, int exitCode) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100425 // Remove any filters that the command might have added to the reporter.
Ulf Adams633f5392015-09-15 11:13:08 +0000426 env.getReporter().setOutputFilter(OutputFilter.OUTPUT_EVERYTHING);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100427
428 notifyCommandComplete(exitCode);
429
430 for (BlazeModule module : blazeModules) {
431 module.afterCommand();
432 }
433
Ulf Adamsab43b972016-03-30 12:23:50 +0000434 env.getBlazeWorkspace().clearEventBus();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100435
436 try {
437 Profiler.instance().stop();
438 MemoryProfiler.instance().stop();
439 } catch (IOException e) {
Ulf Adams633f5392015-09-15 11:13:08 +0000440 env.getReporter().handle(Event.error("Error while writing profile file: " + e.getMessage()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100441 }
Klaus Aehlig777b30d2017-02-24 16:30:15 +0000442 env.getReporter().clearEventBus();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100443 }
444
445 // Make sure we keep a strong reference to this logger, so that the
446 // configuration isn't lost when the gc kicks in.
447 private static Logger templateLogger = Logger.getLogger("com.google.devtools.build");
448
449 /**
450 * Configures "com.google.devtools.build.*" loggers to the given
451 * {@code level}. Note: This code relies on static state.
452 */
453 public static void setupLogging(Level level) {
454 templateLogger.setLevel(level);
455 templateLogger.info("Log level: " + templateLogger.getLevel());
456 }
457
458 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100459 * Returns the Clock-instance used for the entire build. Before,
460 * individual classes (such as Profiler) used to specify the type
461 * of clock (e.g. EpochClock) they wanted to use. This made it
462 * difficult to get Blaze working on Windows as some of the clocks
463 * available for Linux aren't (directly) available on Windows.
464 * Setting the Blaze-wide clock upon construction of BlazeRuntime
465 * allows injecting whatever Clock instance should be used from
466 * BlazeMain.
467 *
468 * @return The Blaze-wide clock
469 */
470 public Clock getClock() {
471 return clock;
472 }
473
474 public OptionsProvider getStartupOptionsProvider() {
475 return startupOptionsProvider;
476 }
477
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100478 public Map<String, BlazeCommand> getCommandMap() {
479 return commandMap;
480 }
481
Julio Merinod1619b752016-10-17 09:50:11 +0000482 /** Invokes {@link BlazeModule#blazeShutdown()} on all registered modules. */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100483 public void shutdown() {
484 for (BlazeModule module : blazeModules) {
485 module.blazeShutdown();
486 }
487 }
488
Lukacs Berki9f603952017-01-13 14:04:08 +0000489 public void prepareForAbruptShutdown() {
490 if (abruptShutdownHandler != null) {
491 abruptShutdownHandler.run();
492 }
493 }
494
Julio Merinod1619b752016-10-17 09:50:11 +0000495 /** Invokes {@link BlazeModule#blazeShutdownOnCrash()} on all registered modules. */
496 public void shutdownOnCrash() {
497 for (BlazeModule module : blazeModules) {
498 module.blazeShutdownOnCrash();
499 }
500 }
501
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100502 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100503 * Returns the defaults package for the default settings. Should only be called by commands that
504 * do <i>not</i> process {@link BuildOptions}, since build options can alter the contents of the
505 * defaults package, which will not be reflected here.
506 */
507 public String getDefaultsPackageContent() {
Janak Ramakrishnan1d899602016-04-28 03:48:55 +0000508 return defaultsPackageContent;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100509 }
510
511 /**
512 * Returns the defaults package for the given options taken from an optionsProvider.
513 */
514 public String getDefaultsPackageContent(OptionsClassProvider optionsProvider) {
515 return ruleClassProvider.getDefaultsPackageContent(optionsProvider);
516 }
517
518 /**
519 * Creates a BuildOptions class for the given options taken from an optionsProvider.
520 */
521 public BuildOptions createBuildOptions(OptionsClassProvider optionsProvider) {
522 return ruleClassProvider.createBuildOptions(optionsProvider);
523 }
524
525 /**
526 * An EventBus exception handler that will report the exception to a remote server, if a
527 * handler is registered.
528 */
529 public static final class RemoteExceptionHandler implements SubscriberExceptionHandler {
530 @Override
531 public void handleException(Throwable exception, SubscriberExceptionContext context) {
lberki97abb522017-09-04 18:51:57 +0200532 logger.log(Level.SEVERE, "Failure in EventBus subscriber", exception);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100533 LoggingUtil.logToRemote(Level.SEVERE, "Failure in EventBus subscriber.", exception);
534 }
535 }
536
537 /**
538 * An EventBus exception handler that will call BugReport.handleCrash exiting
539 * the current thread.
540 */
541 public static final class BugReportingExceptionHandler implements SubscriberExceptionHandler {
542 @Override
543 public void handleException(Throwable exception, SubscriberExceptionContext context) {
544 BugReport.handleCrash(exception);
545 }
546 }
547
548 /**
549 * Main method for the Blaze server startup. Note: This method logs
550 * exceptions to remote servers. Do not add this to a unittest.
551 */
552 public static void main(Iterable<Class<? extends BlazeModule>> moduleClasses, String[] args) {
553 setupUncaughtHandler(args);
554 List<BlazeModule> modules = createModules(moduleClasses);
Googlerc8c64e72015-03-23 23:22:18 +0000555 // blaze.cc will put --batch first if the user set it.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100556 if (args.length >= 1 && args[0].equals("--batch")) {
557 // Run Blaze in batch mode.
558 System.exit(batchMain(modules, args));
559 }
lberki97abb522017-09-04 18:51:57 +0200560 logger.info(
janakr1cb9a012017-03-28 20:18:21 +0000561 "Starting Blaze server with pid "
562 + maybeGetPidString()
563 + " and args "
564 + Arrays.toString(args));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100565 try {
566 // Run Blaze in server mode.
567 System.exit(serverMain(modules, OutErr.SYSTEM_OUT_ERR, args));
568 } catch (RuntimeException | Error e) { // A definite bug...
569 BugReport.printBug(OutErr.SYSTEM_OUT_ERR, e);
570 BugReport.sendBugReport(e, Arrays.asList(args));
571 System.exit(ExitCode.BLAZE_INTERNAL_ERROR.getNumericExitCode());
572 throw e; // Shouldn't get here.
573 }
574 }
575
576 @VisibleForTesting
577 public static List<BlazeModule> createModules(
578 Iterable<Class<? extends BlazeModule>> moduleClasses) {
579 ImmutableList.Builder<BlazeModule> result = ImmutableList.builder();
580 for (Class<? extends BlazeModule> moduleClass : moduleClasses) {
581 try {
Ulf Adams653a16a2016-08-11 12:39:46 +0000582 BlazeModule module = moduleClass.getConstructor().newInstance();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100583 result.add(module);
584 } catch (Throwable e) {
585 throw new IllegalStateException("Cannot instantiate module " + moduleClass.getName(), e);
586 }
587 }
588
589 return result.build();
590 }
591
592 /**
Ulf Adamsfd370042017-06-16 15:52:06 +0200593 * Generates a string form of a request to be written to the logs, filtering the user environment
594 * to remove anything that looks private. The current filter criteria removes any variable whose
595 * name includes "auth", "pass", or "cookie".
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100596 *
597 * @param requestStrings
598 * @return the filtered request to write to the log.
599 */
600 @VisibleForTesting
Ulf Adamsfd370042017-06-16 15:52:06 +0200601 static String getRequestLogString(List<String> requestStrings) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100602 StringBuilder buf = new StringBuilder();
603 buf.append('[');
604 String sep = "";
Ulf Adamsfd370042017-06-16 15:52:06 +0200605 Matcher m = suppressFromLog.matcher("");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100606 for (String s : requestStrings) {
607 buf.append(sep);
Ulf Adamsfd370042017-06-16 15:52:06 +0200608 m.reset(s);
609 if (m.lookingAt()) {
610 buf.append(m.group());
611 buf.append("__private_value_removed__");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100612 } else {
613 buf.append(s);
614 }
615 sep = ", ";
616 }
617 buf.append(']');
618 return buf.toString();
619 }
620
621 /**
622 * Command line options split in to two parts: startup options and everything else.
623 */
624 @VisibleForTesting
625 static class CommandLineOptions {
626 private final List<String> startupArgs;
627 private final List<String> otherArgs;
628
629 CommandLineOptions(List<String> startupArgs, List<String> otherArgs) {
630 this.startupArgs = ImmutableList.copyOf(startupArgs);
631 this.otherArgs = ImmutableList.copyOf(otherArgs);
632 }
633
634 public List<String> getStartupArgs() {
635 return startupArgs;
636 }
637
638 public List<String> getOtherArgs() {
639 return otherArgs;
640 }
641 }
642
643 /**
Lukacs Berki3d486832016-10-26 12:51:38 +0000644 * Splits given options into two lists - arguments matching options defined in this class and
645 * everything else, while preserving order in each list.
646 *
647 * <p>Note that this method relies on the startup options always being in the
648 * <code>--flag=ARG</code> form (instead of <code>--flag ARG</code>). This is enforced by
649 * <code>GetArgumentArray()</code> in <code>blaze.cc</code> by reconstructing the startup
650 * options from their parsed versions instead of using <code>argv</code> verbatim.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100651 */
652 static CommandLineOptions splitStartupOptions(
653 Iterable<BlazeModule> modules, String... args) {
654 List<String> prefixes = new ArrayList<>();
ccalvarin987f09f2017-08-31 19:50:39 +0200655 List<OptionDefinition> startupOptions = Lists.newArrayList();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100656 for (Class<? extends OptionsBase> defaultOptions
657 : BlazeCommandUtils.getStartupOptions(modules)) {
ccalvarin987f09f2017-08-31 19:50:39 +0200658 startupOptions.addAll(OptionsParser.getOptionDefinitions(defaultOptions));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100659 }
660
ccalvarin987f09f2017-08-31 19:50:39 +0200661 for (OptionDefinition optionDefinition : startupOptions) {
662 Type optionType = optionDefinition.getField().getType();
663 prefixes.add("--" + optionDefinition.getOptionName());
664 if (optionType == boolean.class || optionType == TriState.class) {
665 prefixes.add("--no" + optionDefinition.getOptionName());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100666 }
667 }
668
669 List<String> startupArgs = new ArrayList<>();
670 List<String> otherArgs = Lists.newArrayList(args);
671
672 for (Iterator<String> argi = otherArgs.iterator(); argi.hasNext(); ) {
673 String arg = argi.next();
674 if (!arg.startsWith("--")) {
675 break; // stop at command - all startup options would be specified before it.
676 }
677 for (String prefix : prefixes) {
678 if (arg.startsWith(prefix)) {
679 startupArgs.add(arg);
680 argi.remove();
681 break;
682 }
683 }
684 }
685 return new CommandLineOptions(startupArgs, otherArgs);
686 }
687
688 private static void captureSigint() {
689 final Thread mainThread = Thread.currentThread();
690 final AtomicInteger numInterrupts = new AtomicInteger();
691
laurentlb3d2a68c2017-06-30 00:32:04 +0200692 final Runnable interruptWatcher =
693 () -> {
694 int count = 0;
695 // Not an actual infinite loop because it's run in a daemon thread.
696 while (true) {
697 count++;
698 Uninterruptibles.sleepUninterruptibly(10, TimeUnit.SECONDS);
lberki97abb522017-09-04 18:51:57 +0200699 logger.warning("Slow interrupt number " + count + " in batch mode");
laurentlb3d2a68c2017-06-30 00:32:04 +0200700 ThreadUtils.warnAboutSlowInterrupt();
701 }
702 };
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100703
704 new InterruptSignalHandler() {
705 @Override
Janak Ramakrishnan7be337f2017-01-05 20:21:35 +0000706 public void run() {
lberki97abb522017-09-04 18:51:57 +0200707 logger.info("User interrupt");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100708 OutErr.SYSTEM_OUT_ERR.printErrLn("Blaze received an interrupt");
709 mainThread.interrupt();
710
711 int curNumInterrupts = numInterrupts.incrementAndGet();
712 if (curNumInterrupts == 1) {
713 Thread interruptWatcherThread = new Thread(interruptWatcher, "interrupt-watcher");
714 interruptWatcherThread.setDaemon(true);
715 interruptWatcherThread.start();
716 } else if (curNumInterrupts == 2) {
lberki97abb522017-09-04 18:51:57 +0200717 logger.warning("Second --batch interrupt: Reverting to JVM SIGINT handler");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100718 uninstall();
719 }
720 }
721 };
722 }
723
724 /**
725 * A main method that runs blaze commands in batch mode. The return value indicates the desired
726 * exit status of the program.
727 */
728 private static int batchMain(Iterable<BlazeModule> modules, String[] args) {
729 captureSigint();
730 CommandLineOptions commandLineOptions = splitStartupOptions(modules, args);
lberki97abb522017-09-04 18:51:57 +0200731 logger.info(
janakr1cb9a012017-03-28 20:18:21 +0000732 "Running Blaze in batch mode with "
733 + maybeGetPidString()
734 + "startup args "
735 + commandLineOptions.getStartupArgs());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100736
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100737 BlazeRuntime runtime;
ccalvarin0ddda782017-04-06 21:12:11 +0000738 InvocationPolicy policy;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100739 try {
Lukacs Berki9f603952017-01-13 14:04:08 +0000740 runtime = newRuntime(modules, commandLineOptions.getStartupArgs(), null);
ccalvarin0ddda782017-04-06 21:12:11 +0000741 policy = InvocationPolicyParser.parsePolicy(
742 runtime.getStartupOptionsProvider().getOptions(BlazeServerStartupOptions.class)
743 .invocationPolicy);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100744 } catch (OptionsParsingException e) {
ccalvarin0ddda782017-04-06 21:12:11 +0000745 OutErr.SYSTEM_OUT_ERR.printErrLn(e.getMessage());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100746 return ExitCode.COMMAND_LINE_ERROR.getNumericExitCode();
747 } catch (AbruptExitException e) {
ccalvarin0ddda782017-04-06 21:12:11 +0000748 OutErr.SYSTEM_OUT_ERR.printErrLn(e.getMessage());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100749 return e.getExitCode().getNumericExitCode();
750 }
751
ccalvarin1cbe62a2017-08-14 21:09:07 +0200752 ImmutableList.Builder<Pair<String, String>> startupOptionsFromCommandLine =
753 ImmutableList.builder();
754 for (String option : commandLineOptions.getStartupArgs()) {
755 startupOptionsFromCommandLine.add(new Pair<>("", option));
756 }
757
Ulf Adams47cb9162015-09-18 08:12:30 +0000758 BlazeCommandDispatcher dispatcher = new BlazeCommandDispatcher(runtime);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100759
760 try {
lberki97abb522017-09-04 18:51:57 +0200761 logger.info(getRequestLogString(commandLineOptions.getOtherArgs()));
ccalvarin1cbe62a2017-08-14 21:09:07 +0200762 return dispatcher.exec(
763 policy,
764 commandLineOptions.getOtherArgs(),
765 OutErr.SYSTEM_OUT_ERR,
766 LockingMode.ERROR_OUT,
767 "batch client",
768 runtime.getClock().currentTimeMillis(),
769 Optional.of(startupOptionsFromCommandLine.build()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100770 } catch (BlazeCommandDispatcher.ShutdownBlazeServerException e) {
771 return e.getExitStatus();
Lukacs Berkice1445f2016-04-19 15:52:55 +0000772 } catch (InterruptedException e) {
773 // This is almost main(), so it's okay to just swallow it. We are exiting soon.
774 return ExitCode.INTERRUPTED.getNumericExitCode();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100775 } finally {
776 runtime.shutdown();
777 dispatcher.shutdown();
778 }
779 }
780
781 /**
782 * A main method that does not send email. The return value indicates the desired exit status of
783 * the program.
784 */
785 private static int serverMain(Iterable<BlazeModule> modules, OutErr outErr, String[] args) {
Lukacs Berkif7633792016-04-21 12:38:29 +0000786 InterruptSignalHandler sigintHandler = null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100787 try {
Lukacs Berkif7633792016-04-21 12:38:29 +0000788 final RPCServer blazeServer = createBlazeRPCServer(modules, Arrays.asList(args));
789
790 // Register the signal handler.
Janak Ramakrishnan7be337f2017-01-05 20:21:35 +0000791 sigintHandler =
792 new InterruptSignalHandler() {
793 @Override
794 public void run() {
lberki97abb522017-09-04 18:51:57 +0200795 logger.severe("User interrupt");
Janak Ramakrishnan7be337f2017-01-05 20:21:35 +0000796 blazeServer.interrupt();
797 }
798 };
Lukacs Berkif7633792016-04-21 12:38:29 +0000799
Lukacs Berkibb0dac72016-04-19 07:21:19 +0000800 blazeServer.serve();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100801 return ExitCode.SUCCESS.getNumericExitCode();
802 } catch (OptionsParsingException e) {
803 outErr.printErr(e.getMessage());
804 return ExitCode.COMMAND_LINE_ERROR.getNumericExitCode();
805 } catch (IOException e) {
806 outErr.printErr("I/O Error: " + e.getMessage());
807 return ExitCode.BUILD_FAILURE.getNumericExitCode();
808 } catch (AbruptExitException e) {
809 outErr.printErr(e.getMessage());
810 return e.getExitCode().getNumericExitCode();
Lukacs Berkif7633792016-04-21 12:38:29 +0000811 } finally {
812 if (sigintHandler != null) {
813 sigintHandler.uninstall();
814 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100815 }
816 }
817
ccalvarinfb3293c2017-09-26 11:13:33 -0400818 private static FileSystem defaultFileSystemImplementation() {
Lukacs Berki28219722016-06-30 12:34:39 +0000819 if ("0".equals(System.getProperty("io.bazel.EnableJni"))) {
Damien Martin-Guillerez8c04e9e2016-01-18 12:53:39 +0000820 // Ignore UnixFileSystem, to be used for bootstrapping.
Dmitry Lomovbe939512016-02-02 20:26:25 +0000821 return OS.getCurrent() == OS.WINDOWS ? new WindowsFileSystem() : new JavaIoFileSystem();
Damien Martin-Guillerez8c04e9e2016-01-18 12:53:39 +0000822 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100823 // The JNI-based UnixFileSystem is faster, but on Windows it is not available.
Lukacs Berki58dbb7f2016-01-29 11:56:29 +0000824 return OS.getCurrent() == OS.WINDOWS ? new WindowsFileSystem() : new UnixFileSystem();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100825 }
826
ulfjack27758e42017-09-07 13:41:33 +0200827 private static SubprocessFactory subprocessFactoryImplementation() {
Lukacs Berki74dcfee2016-07-04 12:57:25 +0000828 if (!"0".equals(System.getProperty("io.bazel.EnableJni")) && OS.getCurrent() == OS.WINDOWS) {
829 return WindowsSubprocessFactory.INSTANCE;
830 } else {
831 return JavaSubprocessFactory.INSTANCE;
832 }
833 }
834
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100835 /**
836 * Creates and returns a new Blaze RPCServer. Call {@link RPCServer#serve()} to start the server.
837 */
lberki0d8d4cf2017-09-05 16:01:44 +0200838 @SuppressWarnings("LiteralClassName") // bootstrap binary does not have gRPC
Lukacs Berkibb0dac72016-04-19 07:21:19 +0000839 private static RPCServer createBlazeRPCServer(
840 Iterable<BlazeModule> modules, List<String> args)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100841 throws IOException, OptionsParsingException, AbruptExitException {
Lukacs Berki9f603952017-01-13 14:04:08 +0000842 final RPCServer[] rpcServer = new RPCServer[1];
laurentlb3d2a68c2017-06-30 00:32:04 +0200843 Runnable prepareForAbruptShutdown = () -> rpcServer[0].prepareForAbruptShutdown();
Lukacs Berki9f603952017-01-13 14:04:08 +0000844
845 BlazeRuntime runtime = newRuntime(modules, args, prepareForAbruptShutdown);
Lukacs Berkibb0dac72016-04-19 07:21:19 +0000846 BlazeCommandDispatcher dispatcher = new BlazeCommandDispatcher(runtime);
Lukacs Berki8b3b9182016-04-14 08:29:05 +0000847 CommandExecutor commandExecutor = new CommandExecutor(runtime, dispatcher);
Lukacs Berkibb0dac72016-04-19 07:21:19 +0000848
Ulf Adams7db73d32016-08-10 12:41:02 +0000849 BlazeServerStartupOptions startupOptions =
850 runtime.getStartupOptionsProvider().getOptions(BlazeServerStartupOptions.class);
Lukacs Berki3a3c4832016-10-06 14:20:04 +0000851 try {
852 // This is necessary so that Bazel kind of works during bootstrapping, at which time the
853 // gRPC server is not compiled in so that we don't need gRPC for bootstrapping.
854 Class<?> factoryClass = Class.forName(
855 "com.google.devtools.build.lib.server.GrpcServerImpl$Factory");
Lukacs Berki9f603952017-01-13 14:04:08 +0000856 RPCServer.Factory factory = (RPCServer.Factory) factoryClass.getConstructor().newInstance();
857 rpcServer[0] = factory.create(commandExecutor, runtime.getClock(),
Lukacs Berki75392052017-02-07 09:23:36 +0000858 startupOptions.commandPort,
859 runtime.getWorkspace().getWorkspace(),
860 runtime.getServerDirectory(),
Lukacs Berki9f603952017-01-13 14:04:08 +0000861 startupOptions.maxIdleSeconds);
862 return rpcServer[0];
Lukacs Berki3a3c4832016-10-06 14:20:04 +0000863 } catch (ReflectiveOperationException | IllegalArgumentException e) {
864 throw new AbruptExitException("gRPC server not compiled in", ExitCode.BLAZE_INTERNAL_ERROR);
Lukacs Berki8b3b9182016-04-14 08:29:05 +0000865 }
Lukacs Berki9f603952017-01-13 14:04:08 +0000866
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100867 }
868
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100869 /**
870 * Parses the command line arguments into a {@link OptionsParser} object.
871 *
872 * <p>This function needs to parse the --option_sources option manually so that the real option
873 * parser can set the source for every option correctly. If that cannot be parsed or is missing,
874 * we just report an unknown source for every startup option.
875 */
876 private static OptionsProvider parseOptions(
877 Iterable<BlazeModule> modules, List<String> args) throws OptionsParsingException {
Ulf Adams7e71f892016-08-10 09:06:50 +0000878 ImmutableList<Class<? extends OptionsBase>> optionClasses =
879 BlazeCommandUtils.getStartupOptions(modules);
880
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100881 // First parse the command line so that we get the option_sources argument
882 OptionsParser parser = OptionsParser.newOptionsParser(optionClasses);
883 parser.setAllowResidue(false);
ccalvarin7cd9e882017-10-16 22:18:32 +0200884 parser.parse(PriorityCategory.COMMAND_LINE, null, args);
lberki6a8a1742017-06-29 10:09:42 +0200885 Map<String, String> optionSources =
886 parser.getOptions(BlazeServerStartupOptions.class).optionSources;
ccalvarin1dce0972017-09-11 20:03:02 +0200887 Function<OptionDefinition, String> sourceFunction =
888 option ->
889 !optionSources.containsKey(option.getOptionName())
890 ? "default"
891 : optionSources.get(option.getOptionName()).isEmpty()
892 ? "command line"
893 : optionSources.get(option.getOptionName());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100894
895 // Then parse the command line again, this time with the correct option sources
896 parser = OptionsParser.newOptionsParser(optionClasses);
897 parser.setAllowResidue(false);
ccalvarin7cd9e882017-10-16 22:18:32 +0200898 parser.parseWithSourceFunction(PriorityCategory.COMMAND_LINE, sourceFunction, args);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100899 return parser;
900 }
901
902 /**
903 * Creates a new blaze runtime, given the install and output base directories.
904 *
905 * <p>Note: This method can and should only be called once per startup, as it also creates the
906 * filesystem object that will be used for the runtime. So it should only ever be called from the
907 * main method of the Blaze program.
908 *
Ulf Adams7db73d32016-08-10 12:41:02 +0000909 * @param args Blaze startup options.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100910 *
911 * @return a new BlazeRuntime instance initialized with the given filesystem and directories, and
912 * an error string that, if not null, describes a fatal initialization failure that makes
913 * this runtime unsuitable for real commands
914 */
Lukacs Berki9f603952017-01-13 14:04:08 +0000915 private static BlazeRuntime newRuntime(Iterable<BlazeModule> blazeModules, List<String> args,
916 Runnable abruptShutdownHandler)
Ulf Adams7db73d32016-08-10 12:41:02 +0000917 throws AbruptExitException, OptionsParsingException {
918 OptionsProvider options = parseOptions(blazeModules, args);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100919 for (BlazeModule module : blazeModules) {
920 module.globalInit(options);
921 }
922
923 BlazeServerStartupOptions startupOptions = options.getOptions(BlazeServerStartupOptions.class);
Ulf Adamsb5a06f332016-06-21 14:55:49 +0000924 String productName = startupOptions.productName.toLowerCase(Locale.US);
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +0000925
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100926 PathFragment workspaceDirectory = startupOptions.workspaceDirectory;
927 PathFragment installBase = startupOptions.installBase;
928 PathFragment outputBase = startupOptions.outputBase;
929
janakr1cb9a012017-03-28 20:18:21 +0000930 maybeForceJNIByGettingPid(installBase); // Must be before first use of JNI.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100931
932 // From the point of view of the Java program --install_base and --output_base
933 // are mandatory options, despite the comment in their declarations.
934 if (installBase == null || !installBase.isAbsolute()) { // (includes "" default case)
935 throw new IllegalArgumentException(
936 "Bad --install_base option specified: '" + installBase + "'");
937 }
938 if (outputBase != null && !outputBase.isAbsolute()) { // (includes "" default case)
939 throw new IllegalArgumentException(
940 "Bad --output_base option specified: '" + outputBase + "'");
941 }
942
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100943 FileSystem fs = null;
944 for (BlazeModule module : blazeModules) {
Ulf Adams124bd0a2016-08-10 13:10:46 +0000945 FileSystem moduleFs = module.getFileSystem(options);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100946 if (moduleFs != null) {
947 Preconditions.checkState(fs == null, "more than one module returns a file system");
948 fs = moduleFs;
949 }
950 }
951
952 if (fs == null) {
ccalvarinfb3293c2017-09-26 11:13:33 -0400953 fs = defaultFileSystemImplementation();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100954 }
Lukacs Berki74dcfee2016-07-04 12:57:25 +0000955
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100956 Path.setFileSystemForSerialization(fs);
Lukacs Berki74dcfee2016-07-04 12:57:25 +0000957 SubprocessBuilder.setSubprocessFactory(subprocessFactoryImplementation());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100958
959 Path installBasePath = fs.getPath(installBase);
960 Path outputBasePath = fs.getPath(outputBase);
961 Path workspaceDirectoryPath = null;
962 if (!workspaceDirectory.equals(PathFragment.EMPTY_FRAGMENT)) {
963 workspaceDirectoryPath = fs.getPath(workspaceDirectory);
964 }
965
Ulf Adams15a23b92016-08-09 11:46:00 +0000966 ServerDirectories serverDirectories =
967 new ServerDirectories(installBasePath, outputBasePath, startupOptions.installMD5);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100968 Clock clock = BlazeClock.instance();
tomluf903eb52017-10-27 12:12:11 -0400969 BlazeRuntime.Builder runtimeBuilder =
970 new BlazeRuntime.Builder()
971 .setProductName(productName)
972 .setFileSystem(fs)
973 .setServerDirectories(serverDirectories)
974 .setStartupOptionsProvider(options)
975 .setClock(clock)
976 .setAbruptShutdownHandler(abruptShutdownHandler)
977 // TODO(bazel-team): Make BugReportingExceptionHandler the default.
978 // See bug "Make exceptions in EventBus subscribers fatal"
979 .setEventBusExceptionHandler(
980 startupOptions.fatalEventBusExceptions
981 || !BlazeVersionInfo.instance().isReleasedBlaze()
982 ? new BlazeRuntime.BugReportingExceptionHandler()
983 : new BlazeRuntime.RemoteExceptionHandler());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100984
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +0000985 if (System.getenv("TEST_TMPDIR") != null
986 && System.getenv("NO_CRASH_ON_LOGGING_IN_TEST") == null) {
987 LoggingUtil.installRemoteLogger(getTestCrashLogger());
988 }
989
Ulf Adams345e15e2016-07-07 13:27:28 +0000990 runtimeBuilder.addBlazeModule(new BuiltinCommandModule());
ulfjack9274cba2017-08-11 23:19:48 +0200991 // This module needs to be registered before any module providing a SpawnCache implementation.
992 runtimeBuilder.addBlazeModule(new NoSpawnCacheModule());
ulfjack236635a2017-06-26 09:25:52 +0200993 runtimeBuilder.addBlazeModule(new CommandLogModule());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100994 for (BlazeModule blazeModule : blazeModules) {
995 runtimeBuilder.addBlazeModule(blazeModule);
996 }
997
998 BlazeRuntime runtime = runtimeBuilder.build();
Ulf Adamsea46dfc2016-08-10 08:44:19 +0000999
1000 BlazeDirectories directories =
janakr3b63a4e2017-09-14 09:55:40 +02001001 new BlazeDirectories(serverDirectories, workspaceDirectoryPath, productName);
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001002 BinTools binTools;
1003 try {
1004 binTools = BinTools.forProduction(directories);
1005 } catch (IOException e) {
1006 throw new AbruptExitException(
1007 "Cannot enumerate embedded binaries: " + e.getMessage(),
1008 ExitCode.LOCAL_ENVIRONMENTAL_ERROR);
1009 }
1010 runtime.initWorkspace(directories, binTools);
ccalvarin1419dda2017-03-28 21:12:28 +00001011 CustomExitCodePublisher.setAbruptExitStatusFileDir(serverDirectories.getOutputBase());
1012
Nathan Harmatad4803012015-09-08 20:03:22 +00001013 AutoProfiler.setClock(runtime.getClock());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001014 BugReport.setRuntime(runtime);
1015 return runtime;
1016 }
1017
janakr1cb9a012017-03-28 20:18:21 +00001018 private static String maybeGetPidString() {
1019 Integer pid = maybeForceJNIByGettingPid(null);
1020 return pid == null ? "" : "pid " + pid + " and ";
1021 }
1022
1023 /** Loads JNI libraries, if necessary under the current platform. */
1024 @Nullable
1025 private static Integer maybeForceJNIByGettingPid(@Nullable PathFragment installBase) {
1026 return jniLibsAvailable() ? getPidUsingJNI(installBase) : null;
Ulf Adams8afbd3c2017-02-28 10:42:48 +00001027 }
1028
1029 private static boolean jniLibsAvailable() {
1030 return !"0".equals(System.getProperty("io.bazel.EnableJni"));
1031 }
1032
1033 // Force JNI linking at a moment when we have 'installBase' handy, and print
1034 // an informative error if it fails.
janakr1cb9a012017-03-28 20:18:21 +00001035 private static int getPidUsingJNI(@Nullable PathFragment installBase) {
Ulf Adams8afbd3c2017-02-28 10:42:48 +00001036 try {
janakr1cb9a012017-03-28 20:18:21 +00001037 return ProcessUtils.getpid(); // force JNI initialization
Ulf Adams8afbd3c2017-02-28 10:42:48 +00001038 } catch (UnsatisfiedLinkError t) {
janakr1cb9a012017-03-28 20:18:21 +00001039 System.err.println(
1040 "JNI initialization failed: "
1041 + t.getMessage()
1042 + ". "
1043 + "Possibly your installation has been corrupted"
1044 + (installBase == null
1045 ? ""
1046 : "; if this problem persists, try 'rm -fr " + installBase + "'")
1047 + ".");
Ulf Adams8afbd3c2017-02-28 10:42:48 +00001048 throw t;
1049 }
1050 }
1051
1052 /**
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +00001053 * Returns a logger that crashes as soon as it's written to, since tests should not cause events
1054 * that would be logged.
1055 */
1056 @VisibleForTesting
1057 public static Future<Logger> getTestCrashLogger() {
1058 Logger crashLogger = Logger.getAnonymousLogger();
1059 crashLogger.addHandler(
1060 new Handler() {
1061 @Override
1062 public void publish(LogRecord record) {
Michajlo Matijkiwf5cc0b22016-01-20 17:44:44 +00001063 System.err.println("Remote logging disabled for testing, forcing abrupt shutdown.");
1064 System.err.printf("%s#%s: %s\n",
1065 record.getSourceClassName(),
1066 record.getSourceMethodName(),
1067 record.getMessage());
1068
Janak Ramakrishnana88e65b2015-09-17 15:07:26 +00001069 Throwable e = record.getThrown();
Michajlo Matijkiwf5cc0b22016-01-20 17:44:44 +00001070 if (e != null) {
1071 e.printStackTrace();
Janak Ramakrishnana88e65b2015-09-17 15:07:26 +00001072 }
Michajlo Matijkiwf5cc0b22016-01-20 17:44:44 +00001073
1074 Runtime.getRuntime().halt(ExitCode.BLAZE_INTERNAL_ERROR.getNumericExitCode());
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +00001075 }
1076
1077 @Override
1078 public void flush() {
1079 throw new IllegalStateException();
1080 }
1081
1082 @Override
1083 public void close() {
1084 throw new IllegalStateException();
1085 }
1086 });
1087 return Futures.immediateFuture(crashLogger);
1088 }
1089
1090 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001091 * Make sure async threads cannot be orphaned. This method makes sure bugs are reported to
1092 * telemetry and the proper exit code is reported.
1093 */
1094 private static void setupUncaughtHandler(final String[] args) {
Janak Ramakrishnanb6582fa2016-03-14 16:19:40 +00001095 Thread.setDefaultUncaughtExceptionHandler(
laurentlb3d2a68c2017-06-30 00:32:04 +02001096 (thread, throwable) -> BugReport.handleCrash(throwable, args));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001097 }
1098
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +00001099 public String getProductName() {
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +00001100 return productName;
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +00001101 }
1102
Klaus Aehligdadde6f2017-01-18 17:32:07 +00001103 public PathConverter getPathToUriConverter() {
1104 return pathToUriConverter;
1105 }
1106
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001107 /**
1108 * A builder for {@link BlazeRuntime} objects. The only required fields are the {@link
1109 * BlazeDirectories}, and the {@link RuleClassProvider} (except for testing). All other fields
1110 * have safe default values.
1111 *
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001112 * <p>The default behavior of the BlazeRuntime's EventBus is to exit when a subscriber throws
1113 * an exception. Please plan appropriately.
1114 */
1115 public static class Builder {
tomluf903eb52017-10-27 12:12:11 -04001116 private FileSystem fileSystem;
Ulf Adams15a23b92016-08-09 11:46:00 +00001117 private ServerDirectories serverDirectories;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001118 private Clock clock;
Lukacs Berki9f603952017-01-13 14:04:08 +00001119 private Runnable abruptShutdownHandler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001120 private OptionsProvider startupOptionsProvider;
Ulf Adams47cb9162015-09-18 08:12:30 +00001121 private final List<BlazeModule> blazeModules = new ArrayList<>();
Ulf Adams8b56c8c2016-04-12 13:45:56 +00001122 private SubscriberExceptionHandler eventBusExceptionHandler = new RemoteExceptionHandler();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001123 private UUID instanceId;
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +00001124 private String productName;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001125
1126 public BlazeRuntime build() throws AbruptExitException {
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +00001127 Preconditions.checkNotNull(productName);
Ulf Adams15a23b92016-08-09 11:46:00 +00001128 Preconditions.checkNotNull(serverDirectories);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001129 Preconditions.checkNotNull(startupOptionsProvider);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001130 Clock clock = (this.clock == null) ? BlazeClock.instance() : this.clock;
1131 UUID instanceId = (this.instanceId == null) ? UUID.randomUUID() : this.instanceId;
1132
1133 Preconditions.checkNotNull(clock);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001134
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001135 for (BlazeModule module : blazeModules) {
tomluf903eb52017-10-27 12:12:11 -04001136 module.blazeStartup(
1137 startupOptionsProvider,
1138 BlazeVersionInfo.instance(),
1139 instanceId,
1140 fileSystem,
1141 serverDirectories,
1142 clock);
Ulf Adams8b56c8c2016-04-12 13:45:56 +00001143 }
Ulf Adams345e15e2016-07-07 13:27:28 +00001144 ServerBuilder serverBuilder = new ServerBuilder();
Ulf Adams54eeac52016-08-29 09:07:35 +00001145 serverBuilder.addQueryOutputFormatters(OutputFormatter.getDefaultFormatters());
Ulf Adams8b56c8c2016-04-12 13:45:56 +00001146 for (BlazeModule module : blazeModules) {
Ulf Adams345e15e2016-07-07 13:27:28 +00001147 module.serverInit(startupOptionsProvider, serverBuilder);
Nathan Harmataf0cc5b82016-03-18 14:56:28 +00001148 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001149
1150 ConfiguredRuleClassProvider.Builder ruleClassBuilder =
1151 new ConfiguredRuleClassProvider.Builder();
1152 for (BlazeModule module : blazeModules) {
1153 module.initializeRuleClasses(ruleClassBuilder);
1154 }
1155
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001156 ConfiguredRuleClassProvider ruleClassProvider = ruleClassBuilder.build();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001157
Nathan Harmata42fb5602016-05-25 20:32:08 +00001158 Package.Builder.Helper packageBuilderHelper = null;
1159 for (BlazeModule module : blazeModules) {
Nathan Harmatacaa000a2016-06-07 17:46:19 +00001160 Package.Builder.Helper candidateHelper =
tomluf903eb52017-10-27 12:12:11 -04001161 module.getPackageBuilderHelper(ruleClassProvider, fileSystem);
Nathan Harmata42fb5602016-05-25 20:32:08 +00001162 if (candidateHelper != null) {
1163 Preconditions.checkState(packageBuilderHelper == null,
1164 "more than one module defines a package builder helper");
1165 packageBuilderHelper = candidateHelper;
1166 }
1167 }
1168 if (packageBuilderHelper == null) {
1169 packageBuilderHelper = Package.Builder.DefaultHelper.INSTANCE;
1170 }
1171
Janak Ramakrishnan02498412016-05-11 22:28:43 +00001172 PackageFactory packageFactory =
1173 new PackageFactory(
1174 ruleClassProvider,
Ulf Adams345e15e2016-07-07 13:27:28 +00001175 ruleClassBuilder.getPlatformRegexps(),
1176 serverBuilder.getAttributeContainerFactory(),
Ulf Adamsed5be452016-11-02 16:33:57 +00001177 serverBuilder.getEnvironmentExtensions(),
Nathan Harmata42fb5602016-05-25 20:32:08 +00001178 BlazeVersionInfo.instance().getVersion(),
1179 packageBuilderHelper);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001180
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001181 ProjectFile.Provider projectFileProvider = null;
1182 for (BlazeModule module : blazeModules) {
1183 ProjectFile.Provider candidate = module.createProjectFileProvider();
1184 if (candidate != null) {
1185 Preconditions.checkState(projectFileProvider == null,
1186 "more than one module defines a project file provider");
1187 projectFileProvider = candidate;
1188 }
1189 }
1190
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001191 return new BlazeRuntime(
tomluf903eb52017-10-27 12:12:11 -04001192 fileSystem,
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001193 serverBuilder.getQueryEnvironmentFactory(),
Ulf Adams6bfd2c52016-08-29 08:00:39 +00001194 serverBuilder.getQueryFunctions(),
Ulf Adams54eeac52016-08-29 09:07:35 +00001195 serverBuilder.getQueryOutputFormatters(),
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001196 packageFactory,
1197 ruleClassProvider,
gregced79b9372017-08-10 21:51:15 +02001198 ruleClassProvider.getConfigurationFragments(),
Ulf Adams6bfd2c52016-08-29 08:00:39 +00001199 serverBuilder.getInfoItems(),
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001200 clock,
Lukacs Berki9f603952017-01-13 14:04:08 +00001201 abruptShutdownHandler,
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001202 startupOptionsProvider,
1203 ImmutableList.copyOf(blazeModules),
1204 eventBusExceptionHandler,
1205 projectFileProvider,
1206 serverBuilder.getInvocationPolicy(),
1207 serverBuilder.getCommands(),
Klaus Aehligdadde6f2017-01-18 17:32:07 +00001208 productName,
1209 serverBuilder.getPathToUriConverter());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001210 }
1211
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +00001212 public Builder setProductName(String productName) {
1213 this.productName = productName;
1214 return this;
1215 }
1216
tomluf903eb52017-10-27 12:12:11 -04001217 public Builder setFileSystem(FileSystem fileSystem) {
1218 this.fileSystem = fileSystem;
1219 return this;
1220 }
1221
Ulf Adams15a23b92016-08-09 11:46:00 +00001222 public Builder setServerDirectories(ServerDirectories serverDirectories) {
1223 this.serverDirectories = serverDirectories;
1224 return this;
1225 }
1226
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001227 public Builder setClock(Clock clock) {
1228 this.clock = clock;
1229 return this;
1230 }
1231
Lukacs Berki9f603952017-01-13 14:04:08 +00001232 public Builder setAbruptShutdownHandler(Runnable handler) {
1233 this.abruptShutdownHandler = handler;
1234 return this;
1235 }
1236
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001237 public Builder setStartupOptionsProvider(OptionsProvider startupOptionsProvider) {
1238 this.startupOptionsProvider = startupOptionsProvider;
1239 return this;
1240 }
1241
1242 public Builder addBlazeModule(BlazeModule blazeModule) {
1243 blazeModules.add(blazeModule);
1244 return this;
1245 }
1246
1247 public Builder setInstanceId(UUID id) {
1248 instanceId = id;
1249 return this;
1250 }
1251
1252 @VisibleForTesting
1253 public Builder setEventBusExceptionHandler(
1254 SubscriberExceptionHandler eventBusExceptionHandler) {
1255 this.eventBusExceptionHandler = eventBusExceptionHandler;
1256 return this;
1257 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001258 }
1259}